关于*P双机调试的学习

时间:2024-04-08 09:11:37

一:相关基础知识:

 

根据软件调试这本书上说的,Windows启动过程如下图:

 关于*P双机调试的学习

 

  系统一共调用了两次KdInitSystem()函数。

 

第一次调用KdInitSystem会初始化一下全局变量:

 

1.KdPitchDebugger:布尔类型,用来表示是否显式抑制内核调试。当启动选项中包含/DEBUG选项时,这个变量会被设置为真。

2.KdDeBuggerEnable:布尔类型,用来表示内核调试是否被启用。当启动选项中包含/DEBUG或者/DEBUGPORT 而且不包含/NODEBUG时,这个变量会被设置为真。

3.KiDebugRoutine:函数指针类型,用来记录内核调试引擎的异常处理回调函数,当内核调试引擎活动时,只想KdpTrap函数,否则指向KdpStub函数。

4.KdpBreakpointTable:结构数组类型,用来记录代码断点,每一个元素为BREAKPOINT_ENTRY结构,用来描述一个断点,包括断点地址。

5.关于*P双机调试的学习

可见KdDebuggerNotPresent也是一个判断是否是内核调试状态的标志。

 

6.根据先前大牛写的这个帖子http://bbs.pediy.com/showthread.PHP?t=186091

讲过KdEnteredDebugger也是一个判断是否为内核调试状态的标志。

 

7.如果是内核调试状态,KiDebugRoutine指向KdpTrap函数,如果不是内核调试状态则指向KdpStub函数。

 

综合上述,上述7点中的任何一点可以判断是否处于内核调试状态。

 

二:接下来看一下*P对内核做的手脚。

 

1.解决*P加载蓝屏的方法:

 

根据上面提到的帖子上有给出解决方案,因为*P会访问KdEnteredDebugger的内容所以Hook IoAllocMdl,当访问KdEnteredDebugger的时候让他去访问其他始终为0的地址。具体可以看上面帖子的地址。

 

2.解决完1之后,在虚拟机启动*P就不会蓝屏了,*P会有两个IAT Hook 两个通讯函数:KdSendPacket KdReceivePacket,反汇编一下这两个函数:

 关于*P双机调试的学习

 关于*P双机调试的学习

 

解决方案:

解析内核文件的PE结构,获取两个发送函数的地址,得到地之后把地址赋值给自己驱动的全局变量,然后用全局变量替换这两个jmp的地址。详见源码。

3.众所周知,*P是调用KdDisableDebugger函数来禁止内核调试的。所以下断KdDisableDebugger。并将KdDisableDebugger的头部改成ret

 关于*P双机调试的学习

 关于*P双机调试的学习

 

运行*P后,Tp执行到KdDisableDebugger时就会断下来,单步走两步,发现了*P的第一个调用KdDisableDebugger的函数:

 关于*P双机调试的学习

直接jmp 958aea2e 

 关于*P双机调试的学习

这里的[edi] = 8416cd2c  ==> KdDebuggerEnabled

 

这是上面7条中之一,检测是否是内核调试的标志。

 

我尝试着将比较的0改为1,然后G~

 

4.接下来进入了第二个调用KdDisableDebugger的地方:

 关于*P双机调试的学习

 

 

根据分析查看[958E4278h]这里存放的是KiDebugRoutine函数地址,

 关于*P双机调试的学习关于*P双机调试的学习

[958E427Ch]存放的是KdpStub地址

 关于*P双机调试的学习

,根据汇编可知:

*P取出KiDebugRoutine地址,然后将KdpStub赋值给KiDebugRoutine,之后返回。

 

解决方案:

修改[958E427Ch]中的内容为KdpTrap地址

 关于*P双机调试的学习

Go起来~

 

5.来到了第三处调用KdDisableDebugger的地方:

 关于*P双机调试的学习

 

这里这个CALL貌似是IAT HOOK 两个通讯函数的过程,因为push 的两个地址内容是

 关于*P双机调试的学习

 关于*P双机调试的学习

正是IAT HOOK *P自己实现的函数。

 

三:综合一下整个过程。重新整理一下所有解决方案。就是替换上述基础知识中的7个内核变量。

 

首先打开IDA,搜索全部KdDeBuggerEnableKdPitchDebugger调用:分别有下面几处:

 

//1. KeUpdateSystemTime

//

//nt!KeUpdateSystemTime + 0x417:

//84096d65 33c9            xor     ecx, ecx

//84096d67 8d542420        lea     edx, [esp + 20h]

//

//nt!KeUpdateSystemTime + 0x41d :

//84096d6b 803d2c1d188400  cmp     byte ptr[nt!KdDebuggerEnabled(84181d2c)], 0 <--ul_KdDebuggerEnabled_1

//84096d72 7464            je      nt!KeUpdateSystemTime + 0x48a (84096dd8)

//

//

//2. KeUpdateRunTime

//

//nt!KeUpdateRunTime + 0x149:

//840970c2 803d2c1d188400  cmp     byte ptr[nt!KdDebuggerEnabled(84181d2c)], 0 <--ul_KdDebuggerEnabled_2

//840970c9 7412            je      nt!KeUpdateRunTime + 0x164 (840970dd)

//

//3. KdCheckForDebugBreak

//

//kd > uf kdcheckfordebugbreak

//nt!KdCheckForDebugBreak:

//840970e9 803d275d148400  cmp     byte ptr[nt!KdPitchDebugger(84145d27)], 0 <--ul_KdPitchDebugger_1

//840970f0 7519            jne     nt!KdCheckForDebugBreak + 0x22 (8409710b)

//

//nt!KdCheckForDebugBreak + 0x9 :

//840970f2 803d2c1d188400  cmp     byte ptr[nt!KdDebuggerEnabled(84181d2c)], 0 <--ul_KdDebuggerEnabled_3

//840970f9 7410            je      nt!KdCheckForDebugBreak + 0x22 (8409710b)

//

//

//4. KdPollBreakIn

//

//kd > uf KdPollBreakIn

//nt!KdPollBreakIn:

//8409711f 8bff            mov     edi, edi

//84097121 55              push    ebp

//84097122 8bec            mov     ebp, esp

//84097124 51              push    ecx

//84097125 53              push    ebx

//84097126 33db            xor     ebx, ebx

//84097128 381d275d1484    cmp     byte ptr[nt!KdPitchDebugger(84145d27)], bl <--ul_KdPitchDebugger_2

//8409712e 7407            je      nt!KdPollBreakIn + 0x18 (84097137)

//

//nt!KdPollBreakIn + 0x11:

//84097130 32c0            xor     al, al

//84097132 e9d2000000      jmp     nt!KdPollBreakIn + 0xea (84097209)

//

//nt!KdPollBreakIn + 0x18 :

//84097137 885dff          mov     byte ptr[ebp - 1], bl

//8409713a 381d2c1d1884    cmp     byte ptr[nt!KdDebuggerEnabled(84181d2c)], bl <--ul_KdDebuggerEnabled_4

//84097140 0f84c0000000    je      nt!KdPollBreakIn + 0xe7 (84097206)

 

将几处分别替换成我们驱动中定义的全局变量就OK。详见源码~

 

上述几处修改完毕之后,双机调试就可以正常下断了~

 

最后说说此次更新后的*PTesSafe.sys并不是真正的驱动,而是一个加载工具,它NEW出来一片新的内存,新内存才是真正功能函数。那么如何寻找真正的*P驱动呢,我用的方法是找到*P HOOK NtReadVirtualMemory 地址,然后向上搜索PE特征~

 

PS:代码只是测试写的,没有好好写,大家凑合看个思路就好了~

 

最后上个图~祝大家新年快乐~

 关于*P双机调试的学习