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, 然后在 SSDT Hook NtReadVirtualMemory 这个我们自己写的系统服务中判断,如果是 DNF 进程的话,直接调用原来的 SSDT 系统服务就好,如果不是 DNF 进程的话,我就跳过 TP 对 NtReadVirtualMemory 所做的 InLine Hook,也就是跳过前面 7 个字节就 OK 了。 对于这种干掉 InLine Hook 的原理,堕落天才的文章讲的最清楚了,我这里就不再班门弄斧了。对于用 SSDT Hook 干掉浅层的 InLine Hook 可以参考文章名称:《SSDT Hook 的妙用 - 对抗 Ring0 InLine Hook》。有的初学者可能问,咱是如何知道 NtReadVirtualMemory 被 TP 干掉了呢 ?很简单,还是采用前面的做法,用 Kernel Detective 就可以看到了,具体可以看下面的截图:
至于如何安装 SSDT Hook 或者 SSDT Hook 是啥玩意来着的话,大家有兴趣的可以参考我的下面博文系列:
《进程隐藏与进程保护(SSDT Hook 实现)》系列,共三篇。 http://www.cnblogs.com/BoyXiao/archive/2011/09/03/21574.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 内存了。
07、 干掉 NtWriteVirtualMemory 中的 InLine Hook:
上面又干掉了 TP 对 NtReadVirtualMemory 的 InLine Hook 了,从而实现了 CE 读取 DNF 进程的内存。但是你可以试着用 CE 修改 DNF 进程的内存,你很快就会发现,虽然可以扫描内存,但是并不可以读取内存,而后你也肯定能够想到,既然有防止读取内存,那肯定也有防止写入内存,而后你又自然会找到 NtWriteVirtualMemory 上来,等你找到 NtWriteVirtualMemory 后,你又基本能够确定,搞定这个 API 和搞定 NtReadVirtualMemory 应该是差不多的,因为这两个 API 就是一对啊,一个读,一个写,所以 TP 肯定也是采用相同的做法来处理这两个 API,当然,你上面的猜想都是正确的,所以咱还是用 SSDT 来干掉 TP 对 NtWriteVirtualMemory 所做的 InLine。代码和上面的 NtReadVirtualMemory 差不多,这里也还是贴出来一下吧,俺先放两幅截图,然后再贴代码出来:
下面先贴出安装 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 oldSysServiceAddr[g_SSDTHookNtWriteVirtualMemory];
uNtWriteVirtualMemoryAddr
=
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 游戏的内存了。
08、干掉 KiAttachProcess 中的 InLine Hook:
前面已经干掉 NtOpenProcess 和 NtOpenThread 了,按道理来说,咱可以开始用 OD 来调试 DNF 游戏进程了,不过你可以用 OD 尝试附加一下 DNF 的游戏进程,而后你会发现根本附加不上去,这又是为何呢 ?
首先需要明白 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.\
好,这里得到了 KeAttachProcess 的地址了,
那么咱就可以在 Kernel Detective 中来看看 KeAttachProcess 的反汇编代码了, 具体的就看图说话,俺也就不多说了,因为截图里面都明明白白摆着在哪里,
下面给出获取 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.\ 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 驱动保护还比较远的,详情还得留到下回分解了。
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- yrrf.cn 版权所有 赣ICP备2024042794号-2
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务