首页 > 其他分享 >【回调详解】内核回调的详细图解【未完成】

【回调详解】内核回调的详细图解【未完成】

时间:2023-06-15 15:25:18浏览次数:40  
标签:图解 X86 ----------------------------------------------------------------- Win7 X6

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源码

标签:图解,X86,-----------------------------------------------------------------,Win7,X6
From: https://www.cnblogs.com/yvqvan/p/17482964.html

相关文章

  • 驱动开发:内核解锁与强删文件
    在某些时候我们的系统中会出现一些无法被正常删除的文件,如果想要强制删除则需要在驱动层面对其进行解锁后才可删掉,而所谓的解锁其实就是释放掉文件描述符(句柄表)占用,文件解锁的核心原理是通过调用ObSetHandleAttributes函数将特定句柄设置为可关闭状态,然后在调用ZwClose将其文件关......
  • 图解机器学习总结——1、基本概念
    序言:近期主要帮同事讲解《图解机器学习》,刚拿到这本书觉得内容相比较平常使用的机器学习算法,很多地方讲解得比较奇怪,在认真的读完后,觉得还是有很多重要的东西,因此读了书就想把知识点整理出来,加上一些自己对各种算法的认识,因此这个系列里面有一些个人的理解,若有不对的地方,还请不吝指......
  • Linux内核学习-通知链
    前言内核中有许多子系统,他们相互独立,但又具有很强的依赖性。因此其中一个子系统侦测到的或者产生的事件其他子系统可能都有兴趣,那么为了实现这样的交互需求,Linux使用了所谓的通知链(notificationchain)。本博客包含的主要内容1.通知链如何声明以及内核代码定义了那些链(chain)。2.内核......
  • java开发系统内核:使用一个中断实现多个API调用
    在上一节,我们实现了通过中断访问内核API的功能,本节,我们进一步改进中断调用内核API的机制。当前,我们使用一个中断来对应一个API,问题是内核导出的API不可能只有一个,如果始终保持一个中断对应一个API的话,那么CPU只支持两百多个中断,也就是说,按照上一节的办法,我们内核最多只能导出两百......
  • java开发系统内核:应用程序与系统内核的内存隔离
    当前,我们可以开发运行在系统上的应用程序了,接下来的问题是如何保护系统内核免受恶意应用程序的危害。恶意程序要想侵犯系统,主要路径有两条,一是让内核执行它的代码,而是修改内核数据,通过修改数据改变内核的行为。我们看看,如何预防恶意程序侵入到系统内核的数据区域中。无论是内核还是......
  • java开发操作系统内核:由实模式进入保护模式之32位寻址
    从时模式到保护模式,是计算法技术跨时代的发展。大家想想笨拙的Dos界面,黑底白字的那种冷漠界面到win95各种色彩斑斓的窗口,两者之间的区别其实就是实模式和保护模式的天壤之别。保护模式中,最重要的一个概念莫过于”保护”二字,有了“保护”功能后,CPU为软件提供了很多的功能,当然也有了......
  • java开发操作系统内核:让内核突破512字节的限制
    我们当前的系统内核,必须包含在虚拟软盘的第1扇区,由于一个扇区只有512字节,因此,系统内核的大小不可能超过512字节。但是,一个拥有完善功能的内核不可能只有512字节,因此要想越过512字节的限制,具体的做法就是做一个内核加载器,放入到第一扇区,加载器加载如内存后,再将内核从软盘加载到系统......
  • 用java做操作系统内核:软盘读写
    在前两节,我们将一段代码通过软盘加载到了系统内存中,并指示cpu执行加入到内存的代码,事实上,操作系统内核加载也是这么做的。只不过我们加载的代码,最大只能512byte,一个操作系统内核,少说也要几百兆,由此,系统内核不可能直接从软盘读入系统内存。通常的做法是,被加载进内存的512Byte程......
  • java开发系统内核:caps 按键处理
    更详细的讲解和代码调试演示过程,请参看视频LinuxkernelHacker,从零构建自己的内核上一节,我们成功实现了对shift按键的处理,这一节,我们看看如何处理caps按键,当该键按下时,输入系统的字符在大小写间切换。由于我们系统启动后,默认输入是大写字符,完成本节后,我们把系统的默认字符改成......
  • java开发系统内核:像Linux一样使用中断实现内核API
    我们当前提供的内核API有个问题,就是每次使用时,需要计算API函数在内核中的位置,一旦内核代码改变,API接口的位置也会改变,同时调用API的应用程序也必须跟着改变,显然这种限制是不可接受的。为了突破当前缺陷,我们必须想出新的API提供办法。常用的做法是,仿照Linux将API当做一个中断调用,由......