过 DNF TP 驱动保护(二)

过 DNF TP 驱动保护(二)

 

文章目录:php

                 

01. 博文简介:html

02. 环境及工具准备:数组

03. 分析 TP 所作的保护:函数

04. 干掉 NtOpenProcess 中的 Deep InLine Hook:工具

05. 干掉 NtOpenThread 中的 Deep InLine Hook:post

06. 干掉 NtReadVirtualMemory 中的 InLine Hook:编码

07. 干掉 NtWriteVirtualMemory 中的 InLine Hook:spa

08. 干掉 KiAttachProcess 的 InLine Hook:调试

09. 干掉 NtGetContextThread 中的 InLine Hook:code

10. 干掉 NtSetContextThread 中的 InLine Hook:

11. 干掉 DbgkpQueueMessage 中的 InLine Hook:

12. 干掉 DbgkpSetProcessDebugObject 中的 InLine Hook:

13. 干掉 Debug 清零:

                                            

                                            

共四篇,本篇为第二篇。

                                            

                                

06. 干掉 NtReadVirtualMemory 中的 InLine Hook:

                                

前面已经干掉了 TP 对 NtOpenProcess 以及 TP 对 NtOpenThread 所作的 Hook,

这样的话,咱们已经可使用 OD 或者 CE 看到 DNF 的游戏进程了,

弄过一些游戏方面内容的朋友应该都是知道 CE 的,这东东是开源的,不过是基于 Delphi 的,

工具作得很不错,能够经过 CE 来修改一些游戏进程的内存数据,好比能够修改一些简单的游戏的血值啊之类的,

可是,此时咱们能够用 CE 来扫描一下 DNF 游戏进程的内存,你会发现根本扫描不到任何数据,

其实缘由也很简单,TP 对 NtReadVirtualMemory 进行了浅层的 InLine Hook,

从而能够达到防止其余进程读取 DNF 游戏进程内存的目的,

而 CE 的话,在 Ring3 下是经过 ReadProcessMemory 来读取游戏内存的,

而 ReadProcessMemory 进入 Ring0 后又是调用的 NtReadVirtualMemory,

因为 NtReadVirtualMemory 被 TP 干掉了,因此天然用 CE 是扫描不出任何东西的,

要干掉 TP 对 NtReadVirtualMemory 所做的浅层 InLine Hook 实际上是比较简单的,

由于 TP 并无对 NtReadVirtualMemory 作检测,因此能够直接用 SSDT 来对抗掉浅层的 InLine Hook 就 OK。

至于原理的话,很简单,直接用 SSDT Hook 替换掉系统服务 NtReadVirtualMemory,

而后在 SSDTHookNtReadVirtualMemory 这个咱们本身写的系统服务中判断,

若是是 DNF 进程的话,直接调用原来的 SSDT 系统服务就好,若是不是 DNF 进程的话,

我就跳过 TP 对 NtReadVirtualMemory 所作的 InLine Hook,也就是跳过前面 7 个字节就 OK 了。

对于这种干掉 InLine Hook 的原理,堕落天才的文章讲的最清楚了,我这里就再也不班门弄斧了。

(继续为堕落天才打广告)

对于用 SSDT Hook 干掉浅层的 InLine Hook 能够参考看雪上堕落天才的文章:

文章名称:《SSDT Hook 的妙用 - 对抗 Ring0 InLine Hook》

文章地址:http://bbs.pediy.com/showthread.php?t=40832

有的童鞋可能会问,咱是如何知道 NtReadVirtualMemory 被 TP 干掉了呢 ?

很简单,仍是采用前面的作法,用 Kernel Detective 就能够看到了,具体能够看下面的截图:

image_thumb[108]

image_thumb[103]

                                

至于如何安装 SSDT Hook 或者 SSDT Hook 是啥玩意来着的话,你们有兴趣的能够参考个人下面博文系列:

《进程隐藏与进程保护(SSDT Hook 实现)》系列,共三篇。

http://www.cnblogs.com/BoyXiao/archive/2011/09/03/2164574.html

http://www.cnblogs.com/BoyXiao/archive/2011/09/04/2166596.html

http://www.cnblogs.com/BoyXiao/archive/2011/09/05/2168115.html

                                

下面先贴出安装 SSDT 钩子的代码,该代码用来干掉 TP 对 NtReadVirtualMemory 的 InLine Hook:

 1: /************************************************************************/
 2: /* 安装钩子从而过掉 TP 保护所 Hook 的 NtReadVirtualMemory - 让 TP 失效
 3: /* 保存 NtReadVirtualMemory 第 4,5,6,7 个字节(其实就是一个 ULONG 跳转地址)
 4: /* 由于这几个字节在不一样的 XP 上会改变,因此在 SSDT Hook 以前保存下来
 5: /* 从而避免在此处进行硬编码
 6: /************************************************************************/
 7: VOID InstallPassTPNtReadVirtualMemory()
 8: {
 9: if(g_SSDTHookNtReadVirtualMemory > 0)
 10: {
 11: /* 得到 NtReadVirtualMemory 的地址 */
 12: ULONG uNtReadVirtualMemoryAddr = oldSysServiceAddr[g_SSDTHookNtReadVirtualMemory];
 13:  
 14: /* 若是是 DNF 进程,则跳到 NtReadVirtualMemory 执行,即不处理,从而让 DNF InLine Hook 生效 */
 15: uTPHookedNtReadVirtualMemoryJmpAddr = uNtReadVirtualMemoryAddr;
 16: /* 若是不是 DNF 进程,则跳过 TP 的 InLine Hook,从而使 TP 失效 */
 17: uMyHookedNtReadVirtualMemoryJmpAddr = uNtReadVirtualMemoryAddr + 7;
 18: /* 保存下未 Hook 以前的 NtReadVirtualMemory 的第 4,5,6,7 个字节 */
 19: uNtReadVirtualMemoryAddr_3 = *((ULONG *)(uNtReadVirtualMemoryAddr + 3));
 20:  
 21: InstallSysServiceHookByIndex(g_SSDTHookNtReadVirtualMemory, SSDTHookNtReadVirtualMemory);
 22:  
 23: KdPrint(("Pass TP - NtReadVirtualMemory Installed."));
 24: }
 25: }

                                

下面再给出 SSDT Hook 的中继 API 的实现代码:

 1: /************************************************************************/
 2: /* 自定义的 NtReadVirtualMemory,用来实现 SSDT Hook Kernel API
 3: /************************************************************************/
 4: NTSYSHOOKAPI VOID SSDTHookNtReadVirtualMemory()
 5: {
 6: /* 开始过滤 */
 7: if(ValidateCurrentProcessIsDNF() == TRUE)
 8: {
 9: __asm
 10: {
 11: /* 若是是 DNF 进程调用的话,则调用已经被 TP Hook 的 NtReadVirtualMemory */
 12: jmp uTPHookedNtReadVirtualMemoryJmpAddr
 13: }
 14: }
 15:  
 16: __asm
 17: {
 18: /* 已经作了针对硬编码的处理 */
 19: /* 若是不是 DNF 进程调用的话,则跳过 TP Hook 的 NtReadVirtualMemory */
 20: push 0x1C
 21: push uNtReadVirtualMemoryAddr_3
 22: jmp uMyHookedNtReadVirtualMemoryJmpAddr
 23: }
 24: }

                              

好,到这里就已经干掉了 TP 对 NtReadVirtualMemory 所作的 InLine Hook了,

对此最直白的效果就是用 CE 打开 DNF 游戏进程进行内存扫描,你会发现,On Year,能够正常扫描到 DNF 内存了。

image

image

                                                

                                                

07. 干掉 NtWriteVirtualMemory 中的 InLine Hook:

                                                

上面又干掉了 TP 对 NtReadVirtualMemory 的 InLine Hook 了,从而实现了 CE 读取 DNF 进程的内存。

可是你能够试着用 CE 修改 DNF 进程的内存,你很快就会发现,虽然能够扫描内存,可是并不能够读取内存,

然后你也确定可以想到,既然有防止读取内存,那确定也有防止写入内存,

然后你又天然会找到 NtWriteVirtualMemory 上来,等你找到 NtWriteVirtualMemory 后,

你又基本可以肯定,搞定这个 API 和搞定 NtReadVirtualMemory 应该是差很少的,

由于这两个 API 就是一对啊,一个读,一个写,因此 TP 确定也是采用相同的作法来处理这两个 API,

固然,你上面的猜测都是正确的,因此咱仍是用 SSDT 来干掉 TP 对 NtWriteVirtualMemory 所作的 InLine。

代码和上面的 NtReadVirtualMemory 差很少,这里也仍是贴出来一下吧,俺先放两幅截图,而后再贴代码出来:

image_thumb[98]

image_thumb[93]

                                                

下面先贴出安装 SSDT 钩子的代码,该代码用来干掉 TP 对 NtWriteVirtualMemory 的 InLine Hook:

 1: /************************************************************************/
 2: /* 安装钩子从而过掉 TP 保护所 Hook 的 NtWriteVirtualMemory - 让 TP 失效
 3: /* 保存 NtWriteVirtualMemory 第 4,5,6,7 个字节(其实就是一个 ULONG 跳转地址)
 4: /* 由于这几个字节在不一样的 XP 上会改变,因此在 SSDT Hook 以前保存下来
 5: /* 从而避免在此处进行硬编码
 6: /************************************************************************/
 7: VOID InstallPassTPNtWriteVirtualMemory()
 8: {
 9: if(g_SSDTHookNtWriteVirtualMemory > 0)
 10: {
 11: /* 得到 NtWriteVirtualMemory 的地址 */
 12: ULONG uNtWriteVirtualMemoryAddr = oldSysServiceAddr[g_SSDTHookNtWriteVirtualMemory];
 13:  
 14: /* 若是是 DNF 进程,则跳到 NtWriteVirtualMemory 执行,即不处理,从而让 DNF InLine Hook 生效 */
 15: uTPHookedNtWriteVirtualMemoryJmpAddr = uNtWriteVirtualMemoryAddr;
 16: /* 若是不是 DNF 进程,则跳过 TP 的 InLine Hook,从而使 TP 失效 */
 17: uMyHookedNtWriteVirtualMemoryJmpAddr = uNtWriteVirtualMemoryAddr + 7;
 18: /* 保存下未 Hook 以前的 NtReadVirtualMemory 的第4,5,6,7 个字节 */
 19: uNtWriteVirtualMemoryAddr_3 = *((ULONG *)(uNtWriteVirtualMemoryAddr + 3));
 20:  
 21: InstallSysServiceHookByIndex(g_SSDTHookNtWriteVirtualMemory, SSDTHookNtWriteVirtualMemory);
 22:  
 23: KdPrint(("Pass TP - NtWriteVirtualMemory Installed."));
 24: }
 25: }

                               

下面再给出 SSDT Hook 的中继 API 的实现代码:

 1: /************************************************************************/
 2: /* 自定义的 NtWriteVirtualMemory,用来实现 SSDT Hook Kernel API
 3: /************************************************************************/
 4: NTSYSHOOKAPI VOID SSDTHookNtWriteVirtualMemory ()
 5: {
 6: /* 开始过滤 */
 7: if(ValidateCurrentProcessIsDNF() == TRUE)
 8: {
 9: __asm
 10: {
 11: /* 若是是 DNF 进程调用的话,则调用已经被 TP Hook 的 NtWriteVirtualMemory */
 12: jmp uTPHookedNtWriteVirtualMemoryJmpAddr
 13: }
 14: }
 15:  
 16: __asm
 17: {
 18: /* 已经作了针对硬编码的处理 */
 19: /* 若是不是 DNF 进程调用的话,则跳过 TP Hook 的 NtWriteVirtualMemory */
 20: push 0x1C
 21: push uNtWriteVirtualMemoryAddr_3
 22: jmp uMyHookedNtWriteVirtualMemoryJmpAddr
 23: }
 24: }

                                                

好,到这里就已经拿下了 TP 对 NtWriteVirtualMemory 所作的 InLine Hook了,

对此最直白的效果你们也均可以想象获得了,那就是直接用 CE 打开 DNF 游戏进程进行内存修改,

你会发现,此时能够正常修改掉 DNF 游戏的内存了。

image

                                     

                                

08. 干掉 KiAttachProcess 中的 InLine Hook:

                                                

前面已经干掉 NtOpenProcess 和 NtOpenThread 了,按道理来讲,咱能够开始用 OD 来调试 DNF 游戏进程了,

不过你能够用 OD 尝试附加一下 DNF 的游戏进程,然后你会发现根本附加不上去,这又是为什么呢 ?

image

首先须要明白 Ring3 在何种操做下,会致使内核调用 KiAttachProcess,

从字面意思上看,就是附加进程,在 Ring3 下的附加进程操做通常会出如今调试进程的时候,

好比用 OD 附加进程来进行调试,或者用 Visual Studio 附加进程进行调试,

其实咱猜的没错,就是当附加进程的时候会致使内核调用 KiAttachProcess,

TP 对这个未导出的 API 进行了 InLine Hook,从而使得 DNF 游戏进程不能被附加,

这样的话,咱的 OD 或者 Visual Studio 都是不可以附加上 DNF 的游戏进程来进行调试了。

正如前面分析的 NtReadVirtualMemory 和 NtWriteVirtualMemory 这两个 API 同样,

TP 对 KiAttachProcess 也是作的浅层的 InLine Hook,也就是只是 Hook 了函数头 7 个字节,

且 TP 对 KiAttachProcess 的 InLine Hook 也没有检测,因此干掉这个 API 仍是比较简单的,

不过咱们不能够像干掉 NtRead/WriteVirtualMemory 同样用 SSDT Hook 来对抗掉,

由于 KiAttachProcess 并无在 SSDT 表中,不过咱们能够直接恢复 TP 对 KiAttachProcess 的 Hook 便可。

                                                

前面也提到了 KiAttachProcess 是一个未导出的内核 API,

因此咱们不能使用 MmGetSystemRoutineAddress 来得到它的地址,

不过好在 KeAttachProcess 这是一个导出的内核 API,

(通常 Kixxx 都是未导出的 API,而 Kexxx 则是导出的 API)

在 KeAttachProcess 的内部实质上是调用的 KiAttachProcess 来完成功能的,

因此咱能够经过 KeAttachProcess 来定位到 KiAttachProcess 的地址,

而且让人欣喜的是在 KeAttachProcess 中的第一个 call 就是 call KiAttachProcess,

因此搜索特征码也更加简单了,直接搜索第一个 e8 就 OK。

若是用 WinDbg 的话,你能够输几个命令就能够把 KeAttachProcess 的地址打印出来,可是如今咱走点弯路,

在驱动里面用 KdPrint 打印出 KeAttachProcess 的地址,而后咱们再分析。

 1: KdPrint(("KeAttachProcess: %x.", MmGetSystemFunAddress(L"KeAttachProcess")));

                                

image

                                

好,这里获得了 KeAttachProcess 的地址了,

那么咱就能够在 Kernel Detective 中来看看 KeAttachProcess 的反汇编代码了,

具体的就看图说话,俺也就很少说了,由于截图里面都明明白白摆着在哪里,

image

image

image

image

image

                                 
下面给出获取 KiAttachProcess 地址的代码:

 1: /************************************************************************/
 2: /* 获取函数 KiAttachProcess 的地址
 3: /* KiAttachProcess 在 KeAttachProcess 中的第一个 Call(e8 指令) 位置
 4: /************************************************************************/
 5: ULONG GetKiAttachProcessAddr()
 6: {
 7: ULONG uCallAddr = 0;
 8: ULONG uKeAttachProcessAddr = 0;
 9: ULONG uKiAttachProcessAddr = 0;
 10: CHAR szCode[1] = 
 11: {
 12: (char)0xe8
 13: };
 14:  
 15: /* 获取 KeAttachProcess 的地址 */
 16: uKeAttachProcessAddr = MmGetSystemFunAddress(L"KeAttachProcess");
 17:  
 18: /* 搜索特征码 e8 */
 19: uCallAddr = SearchFeature(uKeAttachProcessAddr, szCode, 1);
 20: if (uCallAddr == 0)
 21: {
 22: uKiAttachProcessAddr = 0;
 23: }
 24: else
 25: {
 26: /* 获取 KiAttachProcess 的地址 */
 27: uKiAttachProcessAddr = *((ULONG *)uCallAddr) + uCallAddr + 4;
 28: }
 29:  
 30: return uKiAttachProcessAddr;
 31: }

                              

获得了 KiAttachProcess 的地址,那么下面就要来干掉 KiAttachProcess 了,

为了简单实现,我一开始干掉 KiAttachProcess 的作法是采用的硬编码,

在获得 KiAttachProcess 地址后,能够用 Xuetr 来反汇编这个地址,

从而看到 KiAttachProcess 的反汇编指令,因此个人作法就是,

直接将 Xuetr 中 KiAttachProcess 的头 7 个字节以硬编码的形式保存在数组中,

而后等 TP 启动后,我用保存下来的这 7 个字节直接去恢复 KiAttachProcess 就 OK,

这种方式在个人虚拟机 XP 里面是 OK 的,由于我就是从这台 XP 上获取的 7 个字节的硬编码,

可是在别的 XP 系统上就不 OK 了,缘由是 KiAttachProcess 头 7 个字节并非全部的 XP 都相同的,

直接拿不一致的硬编码去恢复确定是会 BSOD 的,不事后来我用了一种简单的办法就干掉这个问题了,

办法很简单,在 TP 启动以前,我动态去读取 KiAttachProcess 的头 7 个字节,而且保存在数组中,

等 TP 启动后,我就用数组中的这 7 个字节去恢复 TP 对 KiAttachProcess 所作的 InLine Hook。

                                

实现的具体代码很简单,下面也贴出来:

先在 TP 启动以前保存 KiAttachProcess 的头 7 个字节:

 1: PUCHAR pKiAttachProcessAddr = NULL;
 2:  
 3: pKiAttachProcessAddr = (PUCHAR)GetKiAttachProcessAddr();
 4:  
 5: KdPrint(("KiAttachProcess: %x.", uKiAttachProcessAddr));
 6:  
 7: /* 保存 KiAttachProcess 被 DNF Hook 以前的头 API_HOOK_HEADER_LEN 个字节 */
 8: for(uIndex = 0; uIndex < API_HOOK_HEADER_LEN; uIndex++)
 9: {
 10: szKiAttachProcessOriginCode[uIndex] = pKiAttachProcessAddr[uIndex];
 11: }

                                

TP 启动以后恢复被 TP 修改掉的 KiAttachProcess 的头 7 个字节:

 1: /************************************************************************/
 2: /* 恢复 KiAttachProcess 头 9 个字节从而恢复 KiAttachProcess - 让 TP 失效
 3: /* 这里使用了硬编码,去掉硬编码的思路以下:
 4: /* 在 TP 未启动以前,先保存下 KiAttachProcess 的头 9 个字节
 5: /* 而后在 TP 启动以后,恢复这 9 个字节就 OK
 6: /************************************************************************/
 7: VOID RecoveryTPHookedKiAttachProcess()
 8: {
 9: ULONG uIndex = 0;
 10: ULONG uOldAttr = 0;
 11:  
 12: KIRQL kOldIRQL = PASSIVE_LEVEL;
 13: PUCHAR pKiAttachProcessAddr = NULL;
 14:  
 15: /* 获取到 KiAttachProcess 的地址 */
 16: pKiAttachProcessAddr = (PUCHAR)GetKiAttachProcessAddr();
 17:  
 18: EnableWriteProtect(&uOldAttr);
 19: kOldIRQL = KeRaiseIrqlToDpcLevel();
 20:  
 21: /* 恢复 KiAttachProcess 的头 API_HOOK_HEADER_LEN 个字节 */
 22: for(uIndex = 0; uIndex < API_HOOK_HEADER_LEN; uIndex++)
 23: {
 24: pKiAttachProcessAddr[uIndex] = szKiAttachProcessOriginCode[uIndex];
 25: }
 26:  
 27: KeLowerIrql(kOldIRQL);
 28: DisableWriteProtect(uOldAttr);
 29:  
 30: KdPrint(("Pass TP - KiAttachProcess Installed."));
 31: }

 

                                

到这里,咱们又把 KiAttachProcess 给搞定了,因此此时咱能够用 OD 来附加 DNF 游戏进程试试看了,

此时你会发现咱能够将 OD 附加上去了,不过惋惜的是,就算附加上去了,离使用 OD 来调试 DNF 还远着呢,

由于 TP 对 DNF 游戏进程还有其余的保护措施,它在内核里面 Hook 的 API 可不止这么点哦。

                                                

                                                

总结:

                                                

《过 DNF TP 驱动保护》的第二篇到这里就结束了,通过上面的处理,

咱们已通过掉了 TP 所作 InLine Hook 的 5 个 API 了,

首先是 NtOpenProcess 和 NtOpenThread 的深层 InLine Hook,

而后是 SSDT 系统服务函数 NtReadVirtualMemory 和 NtWriteVirtualMemory 的浅层次 InLine Hook,

最后咱们也干掉了 TP 对未导出内核函数 KiAttachProcess 所作的浅层次 InLine Hook。

虽然也干掉了很多 TP 在内核中 Hook 的 API 了,可是这离过 DNF TP 驱动保护还比较远的,

详情还得留到下回分解了。

 

版权全部,迎转载,但转载请注明: 转载自  Zachary.XiaoZhen - 梦想的天空

相关文章
相关标签/搜索