<好吧,由于之前没法过帖子致使斑竹受累,原本本文分成了好几篇,现追加到了一起,第二篇在25楼,第3篇在第29楼>

以下言论只当作技术探讨,请勿应用在非法领域

初学驱动,闲来无事探究了一下t.p,版本为DXF中的最新版本

网上有不少介绍t.p的相关文章,我也参考了一下,顺便补充其不足

首先来看一下t.p加载之后对系统内核所作的更改,
众所周知的有:
  NtReadVirtualMemory,NtWriteVirtualMemory的入口处inline hook
  KiAttachProcess的入口处inline hook
  PsOpenProcess<win7>或NtOpenProcess<xp> 的深层inline hook
  PsOpenThread<win7>或NtOpenThread<xp> 的深层inline hook
  KdSendPacket KdReceivePacket 的IAT hook
这几项网上已经给出了比较完整的解决方案,不再赘述

我们来看一下除此之外的一些东西:
<由于害怕ARK遭封杀,自己造了几个轮子>
t.p加载之后创建了两条系统线程:


创建了一个IO TIMER:


创建了一个DPC TIMER:


注册了一个CreateProcess回调:
<这个忘了截图了..>

可以看到它们所涉及到的知识点还是比较多的,
想摘掉IO TIMER就要知道如何遍历IO TIMER链,
想摘掉DPC TIMER就要知道如何遍历DPC TIMER链,
想摘掉CreateProcess回调就要知道如何遍历PspCreateProcessNotifyRoutine数组,
要摘掉两条工作线程就要知道如何找到它们...


当然了,我们这里的讨论的目的不是恢复它们之后可以做什么,而是单纯性的通过探究它们来更深刻地理解Windows系统底层

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

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

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

<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数组暴力清空也行,不过既然有相对优雅的方式,何乐而不为呢?

关于IO TIMER, DPC TIMER等等, 下回再和大家一起分析~~
如有疏漏之处,请各位前辈不吝赐教