目的:
     遍历 系统中的 回调
类型:
     与Xuetr遍历到的类型相同

     如有雷同,还望见谅。。。有错误或者不恰当的地方请指正。
     附件中代码大量冗余,可以将相同的部分写成一个函数,一开始没注意,懒得改了。。。

详细实现见代码

环境:
     WIN XP SP3   大量使用硬编码,本次仅在于实现我的虚拟机环境下的遍历,没有考虑寻找通用方法。

参考资料:1、WRK 1.2   
          2、React OS  
          3、标 题:替换进程创建监视函数
          作 者:鹿剑
          链 接:http://bbs.pediy.com/showthread.php?t=93759
将包括:
  1.创建进程、创建线程、加载模块回调
  2.注册表回调
  3.错误检测回调
  4.Lego回调
  5.关机回调
  6.注销回调
  7.文件系统改变回调
  8.即插即用回调还未完成,忘大家给些提示,我好补充完整

   8.已经完成,见下面:(从2000里面抄抄捡捡,大牛莫笑。。。
  
其实实现起来都差不多,一个链表头串起来,好几种只是结构不同而已

1、创建进程、创建线程、加载模块回调
(1)创建进程 回调
   注册创建进程 回调使用函数 PsSetCreateProcessNotifyRoutine,调用这个函数会将注册的信息保存到一个数组里面。
反汇编这个函数,会发现以下片段:
代码:
805d0c27 56              push    esi 805d0c28 57              push    edi 805d0c29 7464            je      nt!PsSetCreateProcessNotifyRoutine+0x73 (805d0c8f) 805d0c2b bf404a5680      mov     edi,offset nt!PspCreateProcessNotifyRoutine (80564a40)    805d0c30 57              push    edi  PspCreateProcessNotifyRoutine (80564a40)即这个数组的地址,这个数组是以下结构: ypedef struct _EX_FAST_REF {     union     {         PVOID Object;         ULONG_PTR RefCnt:3;         ULONG_PTR Value;     }; } EX_FAST_REF, *PEX_FAST_REF;  低三位RefCnt是引用指针,Value指向一个结构,如下: typedef struct _EX_CALLBACK_ROUTINE_BLOCK {     EX_RUNDOWN_REF RundownProtect;     PEX_CALLBACK_FUNCTION Function;     PVOID Context; } EX_CALLBACK_ROUTINE_BLOCK, *PEX_CALLBACK_ROUTINE_BLOCK; 
可以看到保存了 回调函数的地址

因而采用搜索数组的方式在进行遍历即可,因为在WIN XP SP3中数组元素为8个,所以遍历的时候就按八个来的,WIN 7就不是了

(2)创建线程和加载模块分别是PsSetCreateThreadNotifyRoutine,PsSetLoadImageNotifyRoutine函数 其原理一样,略过。。。

(3)卸载方法参考 http://blog.csdn.net/whatday/article/details/13354031


2、注册表回调
一开始真不知道哪个函数是注册注册表 回调,尝试着在MSDN以cm(Xuetr 回调类型里就是cmpCallBack)开头看到了CmRegisterCallback,大致看了一下,跟上面的原理一样。。不过是不是八个元素没注意,按八个处理的

3、错误检测回调
函数是:
KeRegisterBugCheckCallback                
KeRegisterBugCheckReasonCallback 
反汇编KeRegisterBugCheckCallback :
代码:
804fa138 c7400458d75580  mov     dword ptr [eax+4],offset nt!KeBugCheckCallbackListHead (8055d758) 
又一个链表头,查看WRK ,这里没有考虑WRK与WIN XP 在该函数上可能的差异。。。得到以下信息:
/
代码:
* NTKERNELAPI BOOLEAN KeRegisterBugCheckCallback (     __out PKBUGCHECK_CALLBACK_RECORD CallbackRecord,     __in PKBUGCHECK_CALLBACK_ROUTINE CallbackRoutine,     __in PVOID Buffer,     __in ULONG Length,     __in PUCHAR Component     ) typedef struct _KBUGCHECK_CALLBACK_RECORD {     LIST_ENTRY Entry;     PKBUGCHECK_CALLBACK_ROUTINE CallbackRoutine;     PVOID Buffer;     ULONG Length;     PUCHAR Component;     ULONG_PTR Checksum;     UCHAR State; } KBUGCHECK_CALLBACK_RECORD, *PKBUGCHECK_CALLBACK_RECORD;  LIST_ENTRY KeBugCheckCallbackListHead; LIST_ENTRY KeBugCheckReasonCallbackListHead; 
结构类型就很清楚了,得到链表头,按照双向链表便利即可

4、Lego回调
刚从XUETR里面看到这个还真不知道是什么,搜索一番后,得到了MJ的一些说明:

ULONG PsSetLegoNotifyRoutine(PVOID notifyroutine)
notifyroutine为需要设置的 回调函数地址
返回值是_ETHREAD->Tcb->LegoData的偏移量
调用此函数, 系统会将一个未导出的全局变量_PspLegoNotifyRoutine设置为你设定的 回调函数地址
当一个线程的_ETHREAD->Tcb->LegoData不为空,且_PspLegoNotifyRoutine不为空,那么当这个线程调用PspExitThread退出时,会调用PspLegoNotifyRoutine中的 回调函数
系统中只允许设置一个这样的 回调函数
有人跟着给出具体结构和定义:
代码:
typedef VOID (*PLEGO_NOTIFY_ROUTINE)( PKTHREAD Thread ); ULONG PsSetLegoNotifyRoutine( PLEGO_NOTIFY_ROUTINE LegoNotifyRoutine )  {      PAGED_CODE();      PspLegoNotifyRoutine = LegoNotifyRoutine;      return FIELD_OFFSET(KTHREAD,LegoData);  }  
硬编码搜索到这个全局变量,查看地址是否为0即可(不知是否有误,或者加上地址有效检测?)
代码:
nt!PsSetLegoNotifyRoutine: 805d299a 8bff            mov     edi,edi 805d299c 55              push    ebp 805d299d 8bec            mov     ebp,esp 805d299f 8b4508          mov     eax,dword ptr [ebp+8] 805d29a2 a3c0d26780      mov     dword ptr [nt!PspLegoNotifyRoutine (8067d2c0)],eax//这里 805d29a7 b8d0000000      mov     eax,0D0h 805d29ac 5d              pop     ebp 805d29ad c20400          ret     4 
5、关机回调
有两个函数:
IoRegisterShutdownNotification
IoRegisterLastChanceShutdownNotification
来看第一个:
相关结构和定义也是参考了WRK。。。。
代码:
LIST_ENTRY IopNotifyShutdownQueueHead; LIST_ENTRY IopNotifyLastChanceShutdownQueueHead;  typedef struct _SHUTDOWN_PACKET {     LIST_ENTRY ListEntry;     PDEVICE_OBJECT DeviceObject; } SHUTDOWN_PACKET, *PSHUTDOWN_PACKET; 
要得到 回调函数地址,需要得到设备对象, 回调函数地址即IRP_MJ_SHUTDOWN的例程地址
//#define IRP_MJ_SHUTDOWN                 0x10

代码如下:
代码:
ULONG GetNotifyAddr(PDEVICE_OBJECT Device_Object) {   ULONG Addr;   PDRIVER_OBJECT Driver_Object;   Driver_Object=Device_Object->DriverObject;   Addr=*(ULONG*)((ULONG)Driver_Object+0x38+0x40);   return Addr; } 
6、注销回调
搜了N久也不知道是哪个函数,于是看了看Xuetr,只有mrxsmb.sys注册了注销 回调,查看其导入表看看有没有跟注册相关的字眼,于是找到了SeRegisterLogonSessionTerminatedRoutine
,查了一下,果然是。
也是一个链表头的形式,相关结构和定义:
代码:
typedef NTSTATUS (*PSE_LOGON_SESSION_TERMINATED_ROUTINE) (   IN PLUID LogonId   ); typedef struct _SEP_LOGON_SESSION_TERMINATED_NOTIFICATION {     struct _SEP_LOGON_SESSION_TERMINATED_NOTIFICATION *Next;     PSE_LOGON_SESSION_TERMINATED_ROUTINE CallbackRoutine; } SEP_LOGON_SESSION_TERMINATED_NOTIFICATION, *PSEP_LOGON_SESSION_TERMINATED_NOTIFICATION;  NTSTATUS SeRegisterLogonSessionTerminatedRoutine(     IN PSE_LOGON_SESSION_TERMINATED_ROUTINE CallbackRoutine     ); 
7、文件系统改变回调
相关的注册函数是:IoRegisterFsRegistrationChange
结构:
代码:
typedef struct _NOTIFICATION_PACKET  {     LIST_ENTRY ListEntry;     PDRIVER_OBJECT DriverObject;     ULONG NotificationRoutine; } NOTIFICATION_PACKET, *PNOTIFICATION_PACKET; 
8、即插即用回调
(1)注册的函数应该是IoRegisterPlugPlayNotification(由于搞不定,我都开始怀疑了。。。)
一开始想用上面的方法找到链表头,遍历链表即可,但是发现结果不对。。。
我找的是IopDeferredRegistrationList,是不是找错了?
WRK里面没有这个函数的实现,于是翻了翻React OS,找到参考的代码有这么几句:

代码:
InsertHeadList(&PnpNotifyListHead, &Entry->PnpNotifyList); KeReleaseGuardedMutex(&PnpNotifyListLock); DPRINT("IoRegisterPlugPlayNotification() returns NotificationEntry %p\n",         Entry); *NotificationEntry = Entry; 
其中相关结构:
代码:
typedef struct _PNP_NOTIFY_ENTRY {     LIST_ENTRY PnpNotifyList;     IO_NOTIFICATION_EVENT_CATEGORY EventCategory;     PVOID Context;     UNICODE_STRING Guid;     PFILE_OBJECT FileObject;     PDRIVER_NOTIFICATION_CALLBACK_ROUTINE PnpNotificationProc; } PNP_NOTIFY_ENTRY, *PPNP_NOTIFY_ENTRY; 
结果完全对不上。。。。。从ntoskrnl.exe中找到这个函数,无奈逆向太烂看不出来将注册信息保存到哪了。。。貌似是一个常量指向的地址。。。
(2)从函数实现来看,注册成功后,将相关结构的地址返回了,如果按照React OS的做法,这个结构是在整个链表里面的,结果自己注册了一个 回调打算进行遍历,还是不行。。。。
还望大家指点。。。

其余结果都能和Xuetr对得上

8.即插即用 回调
  今天下了一份2000源代码回来,找到了IoRegisterPlugPlayNotification函数的实现部分,发现了一个链表IopDeviceClassNotifyList,用Windbg查看这个链表,得到如下信息:

 |------------------------|
1.8067d080 -> e1dd7698 ->--^
  8067d080 <- e1dd7698 <----
  |________________________|
     

  |------------------------------------------------|
2.e1336d98 -> e1036468 -> e1307548 -> 8067d090 ->--^
  e1307548 <- 8067d090 <- e1336d98 <- e1036468 <----
  |________________________________________________|
  
  
  |------------------------------------------------|
4.e1000888 -> e1f58478 -> e1bc04a0 -> 8067d0a0 ->--^
  e1bc04a0 <- 8067d0a0 <- e1000888 <- e1f58478 <----
  |________________________________________________|


  |------------------------|
5.e1537858 -> 8067d0a8 ->--^
  e1537858 <- 8067d0a8 <----
  |________________________|


  |------------------------|
6.e156e7c8 -> 8067d0b0 ->--^
  e156e7c8 <- 8067d0b0 <----
  |________________________|


   |------------------------|
10.e1315d60 -> 8067d0d0 ->--^
   e1315d60 <- 8067d0d0 <----
   |________________________|


   |------------------------------------------------|
11.e1002ad8 -> e156fc48 -> e1301258 -> 8067d0d8 ->--^
   e1301258 <- 8067d0d8 <- e1002ad8 <- e156fc48 <----
   |________________________________________________|

   
   |------------------------------------------------------------|
12.e1023c40 -> e131ae08 -> e101ea20 -> e1f752c8 -> 8067d0e0 ->--^
   e1f752c8 <- 8067d0e0 <- e1023c40 <- e131ae08 <- e101ea20 <----
   |____________________________________________________________|
一共17个, 正好跟Xuetr对应上

而参照2000代码,IopDeviceClassNotifyList是一个具有13个(XP也是)元素的数组,每个都是一个链表,数据格式:
代码:
typedef struct _SETUP_NOTIFY_DATA  {     LIST_ENTRY ListEntry;     IO_NOTIFICATION_EVENT_CATEGORY EventCategory;     PDRIVER_NOTIFICATION_CALLBACK_ROUTINE Callback;     PVOID Context;     PDRIVER_OBJECT DriverObject;     USHORT RefCount;     BOOLEAN Unregistered;     PFAST_MUTEX Lock; } SETUP_NOTIFY_DATA, *PSETUP_NOTIFY_DATA; 
但是偏移跟实际的对不上。。。应该有所更改了
通过计算注册时提供的ClassGuid得到不同的hash值,hash值相同的串在一个链表里

但是技术太烂,WinDbg在u IoRegisterPlugPlayNotification里面没看到IopDeviceClassNotifyList数组,但是发现还有一个函数:IopInitializePlugPlayNotification   这个函数没有导出,我用了最烂的方法,遍历内核模块空间,根据特征码得到函数地址

代码:
/* nt!IopInitializePlugPlayNotification: 8058a5b4 6a0d            push    0Dh 8058a5b6 b880d06780      mov     eax,offset nt!IopDeviceClassNotifyList (8067d080)   8058a5bb 59              pop     ecx 8058a5bc 894004          mov     dword ptr [eax+4],eax */ 
再得到IopDeviceClassNotifyList 数组地址,遍历即可。。。

另外有一个问题:
其中的一个PCALLBACK_RECORD:
代码:
0: kd> dd e1dd7698 e1dd7698  8067d080 8067d080 00000002 00000000 e1dd76a8  00000000 b2884fcc 00000000 81c26b10    kd> dt _driver_object 81c26b10 nt!_DRIVER_OBJECT    +0x000 Type             : 4    +0x002 Size             : 168    +0x004 DeviceObject     : 0x81b332d0 _DEVICE_OBJECT    +0x008 Flags            : 0x10    +0x00c DriverStart      : 0xf8ba4000     +0x010 DriverSize       : 0x1100    +0x014 DriverSection    : 0x81f317c0     +0x018 DriverExtension  : 0x81c26bb8 _DRIVER_EXTENSION    +0x01c DriverName       : _UNICODE_STRING "\Driver\swenum"    +0x024 HardwareDatabase : 0x8067e260 _UNICODE_STRING "\REGISTRY\MACHINE\HARDWARE\DESCRIPTION\SYSTEM"    +0x028 FastIoDispatch   : (null)     +0x02c DriverInit       : 0xf8ba48dd     long  swenum!GsDriverEntry+0    +0x030 DriverStartIo    : (null)     +0x034 DriverUnload     : 0xf8ba432a     void  swenum!DriverUnload+0    +0x038 MajorFunction    : [28] 0xf8ba4768     long  swenum!DispatchCreate+0 

  而用XUETR得到的结果是属于sysaudio.sys模块。。。。。:
点击图片以查看大图图片名称: XUETR_BUG.JPG查看次数: 14文件大小: 19.9 KB文件 ID : 67225

最后附代码,硬编码:
代码:
NTSTATUS GetPlugPlayCallBack(ULONG KernelAddress) {    //未导出   UCHAR* Base=KernelAddress;   ULONG i=0;   ULONG NotifyAddr;//回调函数地址   PSETUP_NOTIFY_DATA pPnpNotifyPack;   PLIST_ENTRY pListEntry;   PLIST_ENTRY pListNext;   PULONG Address;   int count;   Address=0;      if(KernelAddress!=0)   {      for(i=0;i<0x20D000;i++)//内核大小硬编码了      {               if((*(UCHAR*)Base==0x6a)&&(*(UCHAR*)(Base+1)==0x0d)&&(*(UCHAR*)(Base+2)==0xb8)&&(*(UCHAR*)(Base+7)==0x59)&&(*(UCHAR*)(Base+8)==0x89))       {        DbgPrint("找到IopInitializePlugPlayNotification地址%x\n",Base);        Address=*(ULONG*)(Base+3);        break;       }       Base++;      }   }      if(Address==0)     {      DbgPrint("获取PlugPlay回调数组失败\n");        return STATUS_UNSUCCESSFUL;     }   DbgPrint("PlugPlay回调数组地址%x\n",(ULONG)Address);   //13个元素   for(count=0;count回调数组元素%x\n",Address[count]);         {       pListEntry=Address[count];       pListNext=pListEntry;       if(pListNext->Flink!=pListEntry)       {          DbgPrint("回调入口:%X  类型:PlugPlay  包地址:%X\n",*(ULONG*)((PULONG)pListNext+5),pListNext);          PlugPlayNum++;       }       pListNext=pListNext->Flink;       while(pListNext->Flink!=pListEntry)       {         if(pListNextFlink;              DbgPrint("回调入口:%X  类型:PlugPlay  包地址:%X\n",*(ULONG*)((PULONG)pListNext+5),pListNext);           pListNext=pListNext->Blink;         }         else         {           DbgPrint("回调入口:%X  类型:PlugPlay  包地址:%X\n",*(ULONG*)((PULONG)pListNext+5),pListNext);         }         PlugPlayNum++;         pListNext=pListNext->Flink;       }     }   }     }