今天我们首先来看一下最简单的,关于遍历PspCreateProcessNotifyRoutine数组,PspLoadImageNotifyRoutine也同理

这两个数组保存了两组函数地址,它们将在有进程被创建或销毁,有镜像被装载或映射入内存的时候被依次调用,
当然了这两个符号都是未导出的,而且它们的数量在xp和win7下也有所不同,xp<2k3>下最大数组数量为8,win7下为64

既然要找到两个未导出符号,我们就在已导出的相关函数中看一下有没有线索,
相关函数:PsSetCreateProcessNotifyRoutine,注册或注销一个进程创建,销毁的通知回调
相关函数:PsSetLoadImageNotifyRoutine,注册一个镜像装载,映射的通知回调
相关函数:PsRemoveLoadImageNotifyRoutine,注销一个镜像装载,映射的通知回调
相关函数:PsSetCreateThreadNotifyRoutine,注册一个线程创建,销毁的通知回调

相关函数:PsRemoveCreateThreadNotifyRoutine,注销一个线程创建,销毁的通知回调

<PS一下:不清楚为什么那么多文章说PsSetCreateProcessNotifyRoutine,或PsSetLoadImageNotifyRoutine注册的例程无法被删除,从而导致相关的驱动无法被卸载,云云..
WDK中已经解释的很清楚了,而且我平时的使用中没出过任何异常,两个回调都可以通过正规函数删掉,当然手动遍历数组删除也没问题>

另外值得一提的是PsSetLoadImageNotifyRoutine,
该函数注册的回调例程会在镜像被装载或映射入内存之后马上被调用,
注意它是先于DLLMAIN或者DRIVER_ENTRY被调用的,联想到有的驱动会在被加载之后马上删除源文件,我们可以通过这种回调在驱动自动删掉文件本身之前将它copy出去,这种方式比内存dump这种马后炮的方式优雅可靠得多

首先来看一下xp的PsSetCreateProcessNotifyRoutine <2k3下相仿>:

可以看到在入口地址不远处就有PspCreateProcessNotifyRoutine的出现,我们可以很方便地通过搜索特征码的方式得到.

在看一下win7下的PsSetCreateProcessNotifyRoutine:

它首先调用了PspSetCreateProcessNotifyRoutine函数,追踪一下:

PspCreateProcessNotifyRoutine在入口不远处出现

这样,我们就得到了xp<2k3>和win7下的PspCreateProcessNotifyRoutine的地址,
接下来就是遍历一下这个数组看一下每个回调例程都位于哪个模块中了,
找到之后调用PsSetCreateProcessNotifyRoutine,第二个参数设置为TRUE就能摘掉回调例程了

...什么?你遍历了数组但是没有发现目标模块中的回调?好吧,这其中貌似有些猫腻,我们来看一下:
<图中选中的项是我自己的回调例程,当然地址是我已经计算好了的,PspCreateProcessNotifyRoutine数组中并不会出现此地址>

我们来看一下这个驱动文件在内核地址空间中的范围:
<图中选中项是我自己的回调例程所在模块>

可以计算一下该驱动文件的地址范围是 0x927F7000 至 0x927F7000+7000 <927FE000>

接下来我们用livekd来看一下,直接找到PspCreateProcessNotifyRoutine:

可以看到8个回调地址中没有 0x927F7000 - 927FE000 范围内的地址,直觉上最后一个应该是与我们自己注册的例程有关系的数据,那么我们看一下地址 0x9c79061f 中又有什么玄机:

这..貌似没有什么有用的数据,那么我自己注册的例程地址保存到了哪里呢?于是我们自然想到翻看一下WRK,
在WRK中找到PsSetCreateProcessNotifyRoutine,
然后发现其中一处调用了ExAllocateCallBack,看名称应该是为回调分配内存,我们看一下:

这里进行了内存分配,并且将传参Function,也就是我们注册回调时提供的例程地址,写入了一个结构中PEX_CALLBACK_ROUTINE_BLOCK,看一下这个结构有什么成员:

我们感兴趣的只有结构中的第二个成员Function.
回想一下,既然系统为回调分配了内存,那么PspCreateProcessNotifyRoutine中保存的应该是每个结构的地址了,但是回过头我们再看一下 0x9c79061f 地址处的数据:

这里还是没有我们要寻找的地址!
而继续在PsSetCreateProcessNotifyRoutine中寻找线索也没有什么收获

于是我们开始转换了思路,既然我们找不到这个回调的地址,那么系统是如何找到的呢?
嗯,貌似这是一个新的突破口,可是我们不知道系统是在什么时候通过什么方式调用 PspCreateProcessNotifyRoutine 中的例程的啊!
额,再想一下,系统要在某个函数中调用 PspCreateProcessNotifyRoutine 中的例程,那么应该会对 PspCreateProcessNotifyRoutine 进行引用的吧,我们在WRK中搜索 PspCreateProcessNotifyRoutine ,看一看都有哪里对其进行了引用:

于是我们找到了这里:

想想也对,因为PspCreateProcessNotifyRoutine是在进程创建,销毁都进行调用的
跟踪一下 CallBack = ExReferenceCallBackBlock (&PspCreateProcessNotifyRoutine[i]); 这行代码,这里应该是取得回调结构的调用

仔细看看该函数的函数头,我们才发现原来 PspCreateProcessNotifyRoutine 中保存的是 PEX_CALLBACK 结构:

我们看这一行代码
CallBackBlock = ExFastRefGetObject (CallBack->RoutineBlock);
CallBackBlock 被定义为 PEX_CALLBACK_ROUTINE_BLOCK 也就是 PsSetCreateProcessNotifyRoutine 分配的内存数据结构

它的意思很明确,就是得到这个结构的地址,看一下ExFastRefGetObject:

只是简单地和一个常量进行了与运算

好了,我们理清思路:
  PspCreateProcessNotifyRoutine数组中保存的是 PEX_CALLBACK 类型的结构地址数组;
  EX_CALLBACK结构中只有一个成员,就是 RoutineBlock ,它是一个 EX_FAST_REF 结构;
  EX_FAST_REF是一个联合,我们这里简单地将之视为一个指针;
  省略掉中间步骤, PspCreateProcessNotifyRoutine 保存的就是指针数组;
  系统将数组中的指针传递给 ExFastRefGetObject,就得到了 PEX_CALLBACK_ROUTINE_BLOCK 结构地址,这个结构中保存这我们想要的回调例程地址;
  ExFastRefGetObject 将传进来的指针进行了与运算得到了 PEX_CALLBACK_ROUTINE_BLOCK 结构地址.

好了,回想一下我们自己寻找回调地址的过程,我们没有进行最后的与运算而是直接将 PspCreateProcessNotifyRoutine 中的数据当成了结构的地址

最后看一下那个神秘的常量:

win32下被定义为7,先取反再与运算,也就是低3位清零

我们回过头在用windbg看一下:

对 0x9c79061f 进行运算得到了 0x9C790618, 看一下这里的数据:

成功得到了注册的例程地址,我们在PT中验证一下:

如过想通过 PsSetCreateProcessNotifyRoutine 摘掉其他进程的回调,传参必须经过以上运算

当然了,如果你直接将PspCreateProcessNotifyRoutine数组暴力清空也行,不过既然有相对优雅的方式,何乐而不为呢?

---------------------------------------------------------------------------------------------------------------------------

练习实例

看了上边的教程 以下是练习的过程 代码参考来源于WRK

windbg中创建进程的回调函数数组实例如下:
3: kd> dd nt!PspCreateProcessNotifyRoutine
83f789a0  8d808a3f 8fe950b7 8fea577f 8fe81daf
83f789b0  8fe3bff7 9c08feb7 a9345397 00000000
以 a9345397 为例

1.在PsSetCreateProcessNotifyRoutine函数中有如下代码
PEX_CALLBACK_ROUTINE_BLOCK CallBack = ExReferenceCallBackBlock (&PspCreateProcessNotifyRoutine[i]);    //这里传入的是 83f789b8
通过代码知道 ExReferenceCallBackBlock将回调数组的值转换为 我们需要的 PEX_CALLBACK_ROUTINE_BLOCK 结构 从而可以得到函数地址
其中PEX_CALLBACK_ROUTINE_BLOCK结构定义如下:
typedef struct _EX_CALLBACK_ROUTINE_BLOCK {
    EX_RUNDOWN_REF        RundownProtect;
    PEX_CALLBACK_FUNCTION Function;
    PVOID                 Context;
} EX_CALLBACK_ROUTINE_BLOCK, *PEX_CALLBACK_ROUTINE_BLOCK;
其中Function成员就是我们要的函数地址

2.
再来看看ExReferenceCallBackBlock函数定义
PEX_CALLBACK_ROUTINE_BLOCK
ExReferenceCallBackBlock (
    IN OUT PEX_CALLBACK CallBack        //CallBack是83f789b8
    )
通过参数可以知道&PspCreateProcessNotifyRoutine[i]为 PEX_CALLBACK 类型
这样就知道了回调函数数组中的元素PspCreateProcessNotifyRoutine[i]为 EX_CALLBACK 类型
EX_CALLBACK结构定义如下:
typedef struct _EX_CALLBACK {
    EX_FAST_REF RoutineBlock;
} EX_CALLBACK, *PEX_CALLBACK;

3.
ExReferenceCallBackBlock函数中有以下代码:
PEX_CALLBACK_ROUTINE_BLOCK CallBackBlock = ExFastRefGetObject (CallBack->RoutineBlock);        //CallBack->RoutineBlock的值是 a9345397
可以看到通过ExFastRefGetObject得到了 我们想要的最终结果 PEX_CALLBACK_ROUTINE_BLOCK 结构
传入的参数是RoutineBlock成员 也就是EX_FAST_REF结构
EX_FAST_REF结构定义如下:
typedef struct _EX_FAST_REF {
    union {
        PVOID Object;
#if defined (_WIN64)
        ULONG_PTR RefCnt : 4;
#else
        ULONG_PTR RefCnt : 3;
#endif
        ULONG_PTR Value;
    };
} EX_FAST_REF, *PEX_FAST_REF;

4.再来看看ExFastRefGetObject函数中具体的转换过程
NTKERNELAPI
PVOID
FORCEINLINE
ExFastRefGetObject (
    __in EX_FAST_REF FastRef
    )
{
    return (PVOID) (FastRef.Value & ~MAX_FAST_REFS);    //FastRef.Value的值是 a9345397
}
仅仅只有一句代码 简单的逻辑运算
其中MAX_FAST_REFS定义是
#if defined (_WIN64)
#define MAX_FAST_REFS 15
#else
#define MAX_FAST_REFS 7
#endif
这里是WIN7 32位 所以MAX_FAST_REFS的值为7

5.MAX_FAST_REFS为7 二进制是111 ~MAX_FAST_REFS二进制则是000 所以代码相当于 FastRef.Value & 000 把FastRef.Value二进制的最后三位置为0
a9345397 & ~7 = A9345390 这里的结果是一个PEX_CALLBACK_ROUTINE_BLOCK结构 是我们一直想要的 然后看看他的第二个成员 Function
windbg运行结果如下:
3: kd> dd A9345390
a9345390  00000010 9cd63f40 00000000 000ca0e8
可以看到是 9cd63f40 和 工具里边的值对比是一样的 由此得到了真正的函数地址

6.总结一下 过程感觉很漫长 其实就2步 要想得到PspCreateProcessNotifyRoutine 回调数组的某个函数地址
实例:
3: kd> dd nt!PspCreateProcessNotifyRoutine
83f789a0  8d808a3f 8fe950b7 8fea577f 8fe81daf
83f789b0  8fe3bff7 9c08feb7 a9345397 00000000
比如这里的   9c08feb7 或 8fe3bff7 或 8fe81daf 等等。。这里用XXX来代替

第一步:YYY=XXX & ~7
第二步: *((PULONG)YYY+1) 就是函数地址了
当然这里是伪代码 而且是32位系统

遍历创建进程、创建线程、加载模块的回调函数相关推荐

  1. Linux驱动之内核加载模块过程分析

    Linux内核支持动态的加载模块运行:比如insmod first_drv.ko,这样就可以将模块加载到内核所在空间供应用程序调用.现在简单描述下insmod first_drv.ko的过程 1.in ...

  2. Javascript 的模块化编程及加载模块【转载+整理】

    http://www.ruanyifeng.com/blog/2012/10/javascript_module.html 本文内容 引入 模块化 最初写法 对象写法 立即执行函数写法 放大模式 宽放 ...

  3. C语言编写IDL动态可加载模块(DLM)入门

    由于之前有些工作确实是可以直接从底层对数据进行读写的,如果采用动态链接库的话应该可以提高效率. 参考资料: HARRIS对于DLM的官方说明 一个第三方的DLM,用于参考学习 另一个讲DLM的CSDN ...

  4. 进程的创建与可执行程序的加载

    SA1***6*69    张*铭 实验环境:ubuntu 12.04 LTS 当在Linux下编写一个源程序,经过编译链接之后生成可执行程序,在终端shell命令行下输入./(可执行程序的名字)来执 ...

  5. Linux操作系统分析-lab2-进程的创建与可执行程序的加载

    学号:sa****340  姓名:**钰 一.进程的创建过程分析 1.创建进程 Linux提供了几个函数fork,vfork和clone系统调用创建新进程,其中,clone创建轻量级进程,必须指定要共 ...

  6. 如何在React Native中创建精美的动画加载器

    by Vikrant Negi 通过Vikrant Negi 如何在React Native中创建精美的动画加载器 (How to create a beautifully animated load ...

  7. Python创建进程、线程的两种方式

    代码创建进程和线程的两种方式 很多人学习python,不知道从何学起. 很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手. 很多已经做案例的人,却不知道如何去学习更加高深的知识. ...

  8. Java 多线程详解(二)------如何创建进程和线程

    Java 多线程详解(一)------概念的引入:https://blog.csdn.net/weixin_39816740/article/details/80089790 在上一篇博客中,我们已经 ...

  9. 进程:execve加载流程

    续上一篇<<ELF:加载过程>>中分析elf解析器.解析器填充等内容后,本章分析elf可执行程序加载过程. 目录 1. 源码流程 1.1 execve 2. 源码结构 3. 部 ...

最新文章

  1. 算法学习:常用排序方法
  2. 新手关于import/export的理解
  3. java private 变量_java 的private的用法保护成员变量的值,将值判断
  4. ML:MLOps系列讲解之《MLOps原则—迭代增量过程/自动化/持续部署/版本控制/实验跟踪/测试/监控/“ML成绩”系统/可再现性/松散耦合架构(模块化)/基于ML的软件交付指标等》解读
  5. 使用树莓派搭建WordPress个人博客
  6. Unable to compile class for JSP的解决方法
  7. 摊牌了,我靠他实现了NLP模型使用入门
  8. Gale-Shapley算法
  9. 密钥交换算法: 迪菲-赫尔曼算法
  10. 编码,charset,乱码,unicode,utf-8与net简单释义
  11. 使用IntelliJ IDEA 构建Maven的web项目
  12. 子类重写方法aop切不到_SpringBoot源码之旅——AOP
  13. 降序排序_排序简单,应用不易,使用Excel排序的几点建议
  14. 删除替换字符串中第一次出现的字符串
  15. 面试北京XX数通总结
  16. 数据库引擎 SQLite 发布的新行为准则,为何引众怒?
  17. windows自带录屏_电脑版免费的录屏软件有哪些?
  18. 专业卸载工具Your Uninstaller! Pro
  19. Spring实战——FileSystemResource
  20. 关于苹果侵权邮件反馈邮件范文

热门文章

  1. 使用golang求出A-Z的所有子集
  2. Java知识点总结(JDBC-封装JDBC)
  3. 你不懂js系列学习笔记-类型与文法- 04
  4. 通过网络方式安装linux的五种方法
  5. 北京尚学堂|程序员励志名言
  6. Ramdisk文件系统的制作与调试运行
  7. 查看IIS上面的每个网站分别用了多少内存
  8. 初探Object Pascal的类(三)
  9. 完成动态根据类别动态填充区域颜色
  10. Linux - 手册(manual)使用 详解