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

  1. 调用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;

     

  2. 循环PSP_MAX_CREATE_PROCESS_NOTIFY次,调用ExCompareExchangeCallback函数;如果调用成功,则增加PspCreateProcessNotifyRoutineCount次数,然后返回;如果失败,调用ExFreeCallback释放ExAllocateCallback分配的内存。
  3. ExCompareExchangeCallback函数内部流程:判断第1步的地址是否为NULL,如果不为NULL,将地址的最后3bit设置为1。

4.2、进程回调添加流程X64

5、参考资料

WRK源码、ReactOS源码

posted on 2023-06-15 15:12  我,猪八戒  阅读(156)  评论(0编辑  收藏  举报