1、进程回调
进程回调是内核下的全局变量,存放到PspCreateProcessNotifyRoutine中,该变量是个数组;该数组中已经存放函数的具体个数,则存放到全局变量PspCreateProcessNotifyRoutineCount中。
PspCreateProcessNotifyRoutine的最大值由一个宏决定:PSP_MAX_CREATE_PROCESS_NOTIFY。
1.1、XP进程回调
进程回调每个项的值,清理掉最后3bit后,以ULONG为数组,真正的函数地址是从ULONG数组的第2个成员开始;
如下是XP系统的图解:
伪代码为:
void PROCNOTIFY::Enum() { //--------------------------------------------------------------------- // 遍历进程创建/退出回调 //--------------------------------------------------------------------- PULONG Routine = (PULONG)PspCreateProcessNotifyRoutine; for ( ULONG i = 0; i < PspCreateProcessNotifyRoutineCount; ++i ) { //----------------------------------------------------------------- // 每个项清理最后3bit, 得到包含真正回调函数地址的结构体; //----------------------------------------------------------------- PULONG lpItem = Routine[i] & 0xFFFFFFF8; //----------------------------------------------------------------- // 函数回调地址, 在偏移为0x04的位置; // 偏移为0x00的位置, 具体含义未知; //----------------------------------------------------------------- PVOID Address = lpItem[1]; DbgPrint("【%02d】进程回调 =【%p】\n", i, Address); } }
一行代码:(PspCreateProcessNotifyRoutine[idx] & 0xFFFFFFF8)[1];但是第一个成员,不是函数地址;真正的函数地址从第二个成员开始;idx是PspCreateProcessNotifyRoutine数组的索引;
验证图:
1.2、Win7 X86进程回调
Win7 X86上的进程回调,同XP上面的回调是一样的。不需要做任何修改,具体图解如下:
1.3、Win7 X64进程回调
该系统下的进程回调获取操作,与XP和X86平台下的回调,稍微有区别。获取进程回调地址时,偏移为0x00,不再是0x04。
具体图解如下:
伪代码为:
void PROCNOTIFY::Enum() { //--------------------------------------------------------------------- // 遍历进程创建/退出回调 //--------------------------------------------------------------------- PULONG_PTR Routine = (PULONG_PTR)PspCreateProcessNotifyRoutine; for ( ULONG i = 0; i < PspCreateProcessNotifyRoutineCount; ++i ) { //----------------------------------------------------------------- // 每个项清理最后3bit, 得到包含真正回调函数地址的结构体; //----------------------------------------------------------------- PULONG_PTR lpItem = Routine[i] & 0xFFFFFFFFFFFFFFF8; //----------------------------------------------------------------- // 函数回调地址, 在偏移为0x00的位置; // 而不是X86和XP平台上的0x04的位置; //----------------------------------------------------------------- PVOID Address = lpItem[0]; DbgPrint("【%02d】进程回调 =【%p】\n", i, Address); } }
1.4、Win10 X86进程回调
1.5、Win10 X64进程回调
同Win7 X64平台,完全相同;
2、线程回调
进程回调是内核下的全局变量,存放到PspCreateThreadNotifyRoutine中,该变量是个数组;该数组中已经存放函数的具体个数,则存放到全局变量PspCreateThreadNotifyRoutineCount中。
PspCreateThreadNotifyRoutine的最大值由一个宏决定:PSP_MAX_CREATE_THREAD_NOTIFY。
2.1、Win7 X86线程回调
线程回调和Win7 X86的进程回调完全相同,如图:
伪代码,具体参看XP平台的进程回调。
2.2、Win7 X64线程回调
2.3、Win10 X86线程回调
2.4、Win10 X64线程回调
3、Image回调
3.1 Win7 X86映像回调
线程回调和Win7 X86的进程回调完全相同,如图:
伪代码,具体参看XP平台的进程回调。
3.2 Win7 X64映像回调
3.3 Win10 X86映像回调
3.4 Win10 X64映像回调
4、相关理论
4.1、进程回调添加流程X86
- 调用ExAllocateCallback来分配一个EX_CALLBACK_ROUTINE_BLOCK结构体;
//----------------------------------------------------------------- // XP、Win7 X86、Win10 X86上的定义 //----------------------------------------------------------------- typedef struct _EX_RUNDOWN_REF { union { ULONG Count; PVOID Ptr; }; } EX_RUNDOWN_REF, *PEX_RUNDOWN_REF; typedef struct _EX_CALLBACK_ROUTINE_BLOCK { EX_RUNDOWN_REF RundownProtect; PEX_CALLBACK_FUNCTION Function; PVOID Context; } EX_CALLBACK_ROUTINE_BLOCK, *PEX_CALLBACK_ROUTINE_BLOCK;
- 循环PSP_MAX_CREATE_PROCESS_NOTIFY次,调用ExCompareExchangeCallback函数;如果调用成功,则增加PspCreateProcessNotifyRoutineCount次数,然后返回;如果失败,调用ExFreeCallback释放ExAllocateCallback分配的内存。
- ExCompareExchangeCallback函数内部流程:判断第1步的地址是否为NULL,如果不为NULL,将地址的最后3bit设置为1。
4.2、进程回调添加流程X64
5、参考资料
WRK源码、ReactOS源码