文章目录:
01. 博文简介:
02. 环境及工具准备:
03. 分析 TP 所做的保护:
04. 干掉 NtOpenProcess 中的 Deep InLine Hook:
05. 干掉 NtOpenThread 中的 Deep InLine Hook:
06. 干掉 NtReadVirtualMemory 中的 InLine Hook:
07. 干掉 NtWriteVirtualMemory 中的 InLine Hook:
08. 干掉 KiAttachProcess 的 InLine Hook:
09. 干掉 NtGetContextThread 中的 InLine Hook:
10. 干掉 NtSetContextThread 中的 InLine Hook:
11. 干掉 DbgkpQueueMessage 中的 InLine Hook:
12. 干掉 DbgkpSetProcessDebugObject 中的 InLine Hook:
13. 干掉 Debug 清零:
共四篇,本篇为第一篇。
01. 博文简介:
本篇博文仅仅是我对过 TP 保护所作的一个总结,里面没有啥高深的技术,
仅仅是 Hook 而已,并且只有些 InLine Hook 和 SSDT Hook 的代码,
这些对大牛而言都是小菜一碟,所以大牛们可以直接飘过咯 ^_^
然后就是关于本篇博文,估计会比较长,所以我会按照上面的目录分出来一,二,三,四篇相继发表。
我先来装回逼科普下 TP 吧,直接从百度百科抄袭点过来:
TP 系统全称 TenProtect,是由腾讯自主研发推出的安全系统,可以有效保护游戏不受外挂侵犯,同时具备反木马盗号功能,
能有效的防止用户游戏帐号和虚拟财产被窃取。腾讯 TP 系统主要作用为外挂检测、反盗号、反非法工作室、防非法消息。
具体功能如下:
反注入:TP系统能有效的阻止非法模块对游戏进行注入;
反加速:TP系统能防止游戏客户端的非法加速功能;
反模拟按键:TP系统能有效阻止模拟按键程序;
反脱机: TP系统能针对非正常登录游戏的行为进行检测;
反调试: TP系统采用内核级反调试技术,保护游戏进程不被调试和分析;
反木马: TP系统可以保护玩家帐号不被木马程序窃取;
检测外挂功能:TP系统能对外挂功能进行检测;
指令混淆: TP系统能对正常指令进行虚拟和变形,加大外挂作者逆向难度;
特征匹配: TP系统采用特征码匹配技术,能准确检测到外挂的使用;
文件校验:TP系统可以准确检测游戏目录下的文件是否被第三方程序篡改;
游戏内存数据校验: TP系统所特有技术手段可以准确感知到游戏关键数据的异常;
游戏进程保护: TP系统可以保护游戏进程不被第三方程序读写;
游戏虚拟财产保护: 在玩家因不当操作引起帐号泄漏情况下,TP系统也可以保护玩家帐号内虚拟财产不被不法份子转移;
我几个日子弄了过 TP 的驱动保护,算下来前前后后也弄了半来个月,
虽然比较累,但还是收获了蛮多东西,这篇博文就是将如何过掉 TP 做的一个总结而已,
在这篇文章中我会一一介绍过掉 TP 所 Hook 的各种 API 的思路,并附上简要的代码,
在过 TP 驱动保护的过程中以及一些思路和一些代码也很大程度上都是来自国内的几大论坛,
主要是看雪,一蓑烟雨,DebugMan 等论坛,这里对我所借鉴的那些哥们说 Many Thanks。
同时也得特别感谢刘总,很多地方若没有刘总的指导,则还指不定何时能够弄出来呢。
值得一提的是,我现在所做的过 TP 驱动保护只支持 32 位 XP,在所有的 32 位 XP 上都可以正常运行,
不过 Win7 的话还不行,因为里面用到了搜索特征码来定位未导出 API,而我只针对 XP 做了处理。
免责声明:
此文仅作为技术交流,有心之人切记不要拿来做坏事,尤其是不要拿来做伤天害理,或者伤害企鹅利益的事情,
对于那些有心要做坏事的,则所有后果或者反正是坏的方面的责任都与我无关,
如果此文伤害了某些公司的利益或者之类的,请站内消息或者留言联系我,本人看到后会第一时间关闭此文。
02. 环境及工具准备:
前面也提过了,此次过 TP 的驱动保护仅仅只适用于 XP 系统,所以,首先你得准备个虚拟机,
然后装个 XP 的系统,至于是 XP SP2 还是 XP SPxxx 就随便了,当然,如果你喜欢 BSOD 的话,
也完全可以装个其他的系统,然后的话,就得准备几个专业工具了,首先一个当然是 Xuetr 了,
不过 TP 能够检测出 Xuetr,所以 Xuetr 在 XP 机器上和 TP 同时运行时,
轻则导致 TP 弹出警告框,重则蓝屏,这个看个人运气了。
然后还有一个工具也很重量级,也是 Rootkit 检测工具,叫做 Kernel Detective,
并且更重要的是 TP 和 Kernel Detective 是可以并存的,不会像 Xuetr 那样会被 TP 检测出来,
不过 Kernel Detective 想比与 Xuetr 来说,Xuetr 能够扫描内核的 InLine Hook,这个很强大,
这两个工具都很重要,其次就是 WinDbg 和 OD 以及 CE 了。
至于 WinDbg 的话自然是用来调试 Windows 内核或者调试驱动程序了,
而 OD 和 CE 的话可以用来测试咱所写的驱动是否真正的有效果,也就是测试是否真正的过了 TP 保护。
最后就是开发环境了,我的是 Visual Studio 2010 + Visual DDK + WDK,这个可以随意搭建。
03. 分析 TP 所做的保护:
如果真要分析 TP 所做的保护的话,还是比较麻烦的,不过好在各种论坛里面,各种前辈给指出了明路,
比如堕落天才有一篇极好的文章,不过这篇文章是 2010 年 12 月份的了,中间 TP 也不是吃饭的,肯定是有更新了,
(继续为堕落天才打广告)
文章名称:《散谈游戏保护那点事~就从_TP开始入手吧》
文章地址:http://bbs.pediy.com/showthread.php?t=126802
不过这篇文章的参考价值还是很大的,比如在 NtOpenProcess,NtOpenThread 等等系统服务的 Hook 上,
TP 也还是差不多的,也就是变化不大,甚至很多的代码都可以拿过来直接用,而至于 TP 更新的一些内核函数的 Hook 的话,
也可以从其他论坛里面找到一些,所以最主要的一点就是放狗搜索,放狗搜索到一些资料以后,
可以用 WinDbg 或者 Kernel Detective 来验证这个内核 API 是否真的被 TP 所干掉了。
具体的俺也不晓得要怎么说了,总结一句就是放狗搜索。
下面放出几张截图,我是用 Xuetr 进行扫描的,
04. 干掉 NtOpenProcess 中的 Deep InLine Hook:
TP Hook NtOpenProcess 的直接效果就是咱在应用层里面调用 OpenProcess(DNF 进程) 失败,
并且在 OD 或者 CE 里面也根本找不到 DNF 游戏的进程,更别提什么打开或者附加了,
这使得咱根本对 DNF 无从下手。研究过 TP 的都知道,TP 在 NtOpenProcess 中是下了深层的 InLine 钩子,
这个也早已经不是什么秘密,各个论坛上的都知道,是 Hook 的 ObOpenObjectByPointer,
对于这个,可以使用 Xuetr 扫描内核钩子扫描出来(TP 对 Xuetr 好像敏感,在 XP 机上可能蓝屏)。
在一些简单的 InLine Hook 中,咱都是直接拿内核 API 的头 5 个或者头 7 个字节做 Hook,
这种 Hook 方式是很容易被干掉的,直接 SSDT Hook 就可以干掉,
对于用 SSDT Hook 干掉浅层的 InLine Hook 可以参考看雪上堕落天才的文章:
文章名称:《SSDT Hook 的妙用 - 对抗 Ring0 InLine Hook》
文章地址:http://bbs.pediy.com/showthread.php?t=40832
不过 TP 是做的 Deep InLine Hook,也就是 Hook 的是 NtOpenProcess 深层的地址,而不是函数头,
要想用 SSDT Hook 来干掉的话,除非自己重写 NtOpenProcess,否则很难干掉,
而且 TP 在对 NtOpenProcess 上还有检测,所以即使是重写 NtOpenProcess 也很麻烦,
因为在重写中也必须要绕过 TP 可以被 TP 检测到,从而弹出经典的 TP 警告框。
在这里咱可以在 Kernel Detective 中看到它所做的 InLine Hook,
首先是启动 Kernel Detective,然后在 SSDT 子菜单中,找到 NtOpenProcess,
然后在上面右键,在右键菜单中选择反汇编当前地址,从而就会跳转到 NtOpenProcess 的反汇编代码中了,
由于我的电脑太烂了,开个虚拟机,再跑个 DNF,再主机里面开个 Visual Studio 的话,估计半天会没反应,
所以这里截图截的都是没有启动 DNF 的图,也就是在 TP 还没有进行 Hook 时候的截图,
对于电脑配置好的朋友,可以自己去测试,测试结果除了地址外,其他基本都是一样的,
那么如何实现干掉 TP 对 NtOpenProcess 所做的 Hook 呢 ?
一般干掉的意思就是恢复 Hook,但是恢复 Hook 有一个很严重的问题,那就是很容易就被 TP 检测到了,
其实可以换个思路,为什么一定要干掉 TP 对 NtOpenProcess 所做的 Hook 呢 ?
就算被干掉了,还得干掉 TP 用来检测 NtOpenProcess 的 Hook 是否被干掉的线程之类的,
这样就比较麻烦了,为何不直接绕过 TP 的 Hook 呢 ?
要想绕过 TP 的保护的话,我们也可以下一个 InLine Hook,如果发现是 DNF 进程的话,那好啊,
咱直接跳到 TP 下的 InLine Hook 中执行(这样 TP 还是执行它原来的代码,从而检测不出来被改变了)
而如果不是 DNF 进程的话,那咱就跳过 TP 下的 InLine Hook 就好了,
上面这样说是说不清楚的,来点干脆的,写点伪代码比较容易看懂:
TP 启动之前,也就是 Hook 之前的伪代码:
1: push dword ptr[ebp-38]
2: push dword ptr[ebp-24]
3: call ObOpenObjectByPointer
4: mov edi,eax
5: lea eax,dword ptr[ebp-B8]
TP 启动之后,也就是 Hook 之后的伪代码:
1: push dword ptr[ebp-38]
2: push dword ptr[ebp-24]
3: call TPHookedObOpenObjectByPointer //这里代表 TP Hook ObOpenObjectByPointer 的函数
4: mov edi,eax
5: lea eax,dword ptr[ebp-B8]
绕过 TP 所做的 Hook 的伪代码(DNF 检测不出来自己被绕过了,要是能检测出来也就不叫绕过了):
1: push dword ptr[ebp-38]
2: push dword ptr[ebp-24]
3: if(是 DNF 进程)
4: {
5: call TPHookedObOpenObjectByPointer
6: }
7: else
8: {
9: call ObOpenObjectByPointer
10: }
11: mov edi,eax
12: lea eax,dword ptr[ebp-B8]
有了伪代码以后,我们要做的也就比较清楚了,要想实现“绕过 TP 所做的 Hook 的伪代码”的话,
咱得自己也下一个 InLine Hook,至于我们在哪里下 InLine Hook 的话,也很明白,
至少得再 call ObOpenObjectByPointer 之前吧,否则都已经进入 TP 的 Hook 了,还谈什么绕过呢 ?
下面给出一副截图来标记将要下我们自己的 InLine Hook 的位置,
现在已经知道要在哪个位置下 InLine Hook 了,那么下一个问题就是咱如何才能得到这个地址呢?
办法自然是搜索特征码了,具体的实现方式可以看下面的截图,通过搜索特征码咱就可以得到咱要下 Hook 的地址了。
既然要下 InLine Hook 的位置也找到了,同时,如何绕过 TP 的 InLine Hook 的伪代码也出来了,
那么就只需要在找到的位置处安装一个 InLine Hook,同时完成我们 Hook 时的过滤代码就 OK 了。
下面对如何绕过 TP 的 InLine Hook 的伪代码再做一个详细点的说明,具体的都已经很简单了,
我们已经得到了 0x805C0D74 这个地址,这个地址 + 6 即是 0x805C0D7A,对应的就是 call 指令了,
所以如果是 DNF 进程的话,我们就直接跳到这个指令上来,从而执行 call TPHookedObOpenObjectByPointer,
1: __asm
2: {
3: push dword ptr [ebp-38h]
4: push dword ptr [ebp-24h]
5: jmp 0x805C0D7A
6: }
同时 0x805C0D74 + 11 = 0x805C0D7F 也就是 mov 指令,也就是说如果不是 DNF 进程的话,
咱自己实现下面的代码就 OK 了,也就是调用内核原始的 ObOpenObjectByPointer,
ObOpenObjectByPointer 这个函数是内核导出函数,可以使用 MmGetSystemRoutineAddress 获取其地址,
1: __asm
2: {
3: push dword ptr [ebp-38h]
4: push dword ptr [ebp-24h]
5: call ObOpenObjectByPointer
6: jmp 0x805C0D7F
7: }
下面是先贴出安装 InLine Hook 的代码,用来干掉 TP 对 call ObOpenObjectPointer 的 Hook
1: /************************************************************************/
2: /* 安装钩子从而过掉 TP 保护所 Hook 的 NtOpenProcess - 让 TP 失效
3: /************************************************************************/
4: VOID InstallPassTPNtOpenProcess()
5: {
6: CHAR szCode[7] =
7: {
8: (char)0xff,
9: (char)0x75,
10: (char)0xc8,
11: (char)0xff,
12: (char)0x75,
13: (char)0xdc,
14: (char)0xe8
15: };
16:
17: /* 获取原生的 NtOpenProcess 和 ObOpenObjectByPointer 的地址 */
18: uOriginNtOpenProcessAddr = MmGetSystemFunAddress(L"NtOpenProcess");
19: uOriginObOpenObjectByPointerAddr = MmGetSystemFunAddress(L"ObOpenObjectByPointer");
20:
21: /* 从 NtOpenProcess 这个地址开始搜索长度为 7 的特征码字符串,得到的地址将会被安装 InLine Hook */
22: uMyHookedNtOpenProcessAddr = SearchFeature(uOriginNtOpenProcessAddr, szCode, 7) - 7;
23:
24: /* 计算出自定义 InLine Hook 的跳转地址 */
25: uMyHookedNtOpenProcessJmpAddr = uMyHookedNtOpenProcessAddr + 11;
26:
27: /* 计算出 TP InLine Hook 的跳转地址 */
28: uTPHookedNtOpenProcessJmpAddr = uMyHookedNtOpenProcessAddr + 6;
29:
30: /* 安装一个 InLine Hook */
31: InstallInLineHook(uMyHookedNtOpenProcessAddr, (ULONG)InLineHookNtOpenProcess);
32:
33: KdPrint(("Pass TP - NtOpenProcess Installed."));
34: }
下面再给出我们自己 InLine Hook 的中继实现:
1: /************************************************************************/
2: /* 自定义的 NtOpenProcess,用来实现 InLine Hook Kernel API
3: /************************************************************************/
4: NTSYSHOOKAPI void InLineHookNtOpenProcess()
5: {
6: __asm
7: {
8: push dword ptr [ebp-38h]
9: push dword ptr [ebp-24h]
10: }
11:
12: /* 开始过滤 */
13: if(ValidateCurrentProcessIsDNF() == TRUE)
14: {
15: __asm
16: {
17: /* 如果是 DNF 进程调用的话,则调用已经被 TP Hook 的 NtOpenProcess */
18: jmp uTPHookedNtOpenProcessJmpAddr
19: }
20: }
21:
22: __asm
23: {
24: /* 如果不是 DNF 进程调用的话,则调用 ntoskrnl.exe 中的 NtOpenProcess */
25: call uOriginObOpenObjectByPointerAddr
26: jmp uMyHookedNtOpenProcessJmpAddr
27: }
28: }
05. 干掉 NtOpenThread 中的 Deep InLine Hook:
上面已经干掉了 NtOpenProcess 中的 Deep InLine Hook 了,其实很多人都能猜得到,
既然 TP 搞掉了 NtOpenProcess,那么 TP 就没有理由不搞掉 NtOpenThread 这个内核服务了,
不错,TP 在内核中确实也干掉了 NtOpenThread 这个内核服务,并且同样用的 Deep InLine Hook,
并且跟 NtOpenProcess 一样,NtOpenThread 中也是对 ObOpenObjectByPointer 做的 InLine Hook,
不相信的童鞋可以分别在启动 DNF 游戏之前和之后用 Kernel Detective 经过反汇编查看 NtOpenThread 的反汇编代码,
比较两次的反汇编代码就可以看出来 TP 在 NtOpenThread 中所动的手脚了,具体的步骤可以如下:
先找到 NtOpenThread 的反汇编地址,然后再在反汇编代码中找到 call ObOpenObjectByPointer(TP 启动之前),
和干掉 NtOpenProcess 中的 Deep InLine Hook 一样,我们还是使用绕过去的方式来做,
由于前面已经以这种方式分析分析过 NtOpenThread 了,所以咱直接来分析地址就 OK 了,
首先我们一样搜索特征码定位到我们将要安装 InLine Hook 的地址,
下面对如何绕过 TP 的 InLine Hook 的再做一个详细点的说明,
我们已经得到了 0x805C0FF6 这个地址,这个地址 + 6 即是 0x805C0FFC,对应的就是 call 指令了,
所以如果是 DNF 进程的话,我们就直接跳到这个指令上来,从而执行 call TPHookedObOpenObjectByPointer,
1: __asm
2: {
3: push dword ptr [ebp-34h]
4: push dword ptr [ebp-20h]
5: jmp 0x805C0FFC
6: }
同时 0x805C0FF6 + 11 = 0x805C1001 也就是 mov 指令,也就是说如果不是 DNF 进程的话,
咱自己实现下面的代码就 OK 了,也就是调用内核原始的 ObOpenObjectByPointer,
ObOpenObjectByPointer 这个函数是内核导出函数,可以使用 MmGetSystemRoutineAddress 获取其地址,
1: __asm
2: {
3: push dword ptr [ebp-34h]
4: push dword ptr [ebp-20h]
5: call ObOpenObjectByPointer
6: jmp 0x805C1001
7: }
下面先贴出用来安装 InLine Hook 的代码,用来干掉 TP 对 Call ObOpenObjectByPointer 做的 Hook:
1: /************************************************************************/
2: /* 安装钩子从而过掉 TP 保护所 Hook 的 NtOpenThread - 让 TP 失效
3: /************************************************************************/
4: VOID InstallPassTPNtOpenThread()
5: {
6: CHAR szCode[7] =
7: {
8: (char)0xff,
9: (char)0x75,
10: (char)0xcc,
11: (char)0xff,
12: (char)0x75,
13: (char)0xe0,
14: (char)0xe8
15: };
16:
17: /* 获取原生的 NtOpenThread 和 ObOpenObjectByPointer 的地址 */
18: uOriginNtOpenThreadAddr = MmGetSystemFunAddress(L"NtOpenThread");
19: uOriginObOpenObjectByPointerAddr = MmGetSystemFunAddress(L"ObOpenObjectByPointer");
20:
21: /* 从 NtOpenThread 这个地址开始搜索长度为 7 的特征码字符串,得到的地址将会被安装 InLine Hook */
22: uMyHookedNtOpenThreadAddr = SearchFeature(uOriginNtOpenThreadAddr, szCode, 7) - 7;
23: uMyHookedNtOpenThreadJmpAddr = uMyHookedNtOpenThreadAddr + 11;
24: uTPHookedNtOpenThreadJmpAddr = uMyHookedNtOpenThreadAddr + 6;
25:
26: InstallInLineHook(uMyHookedNtOpenThreadAddr, (ULONG)InLineHookNtOpenThread);
27:
28: KdPrint(("Pass TP - NtOpenThread Installed."));
29: }
下面再给出我们自己 InLine Hook 的中继实现:
1: /************************************************************************/
2: /* 自定义的 NtOpenThread,用来实现 InLine Hook Kernel API
3: /************************************************************************/
4: NTSYSHOOKAPI void InLineHookNtOpenThread()
5: {
6: __asm
7: {
8: push dword ptr [ebp-34h]
9: push dword ptr [ebp-20h]
10: }
11:
12: /* 开始过滤 */
13: if(ValidateCurrentProcessIsDNF() == TRUE)
14: {
15: __asm
16: {
17: /* 如果是 DNF 进程调用的话,则调用已经被 TP Hook 的 NtOpenThread */
18: jmp uTPHookedNtOpenThreadJmpAddr
19: }
20: }
21:
22: __asm
23: {
24: /* 如果不是 DNF 进程调用的话,则调用 ntoskrnl.exe 中的 NtOpenThread */
25: call uOriginObOpenObjectByPointerAddr
26: jmp uMyHookedNtOpenThreadJmpAddr
27: }
28: }
总结:
《过 DNF TP 驱动保护》的第一篇到这里就结束了,经过上面的处理,
我们已经过掉了 TP 在 NtOpenProcess 和 NtOpenThread 中的 InLine Hook,
现在已经能在 OD 或 CE 里看到 DNF 游戏进程并且在 Ring3 下也可以调用 OpenProcess 打开 DNF 进程了,
不过完成了这些离过 DNF TP 驱动保护还很远,详情还得留到下回分解了。
下面给出两张截图来显示一下咱的战果:
版权所有,欢迎转载,但转载请注明: 转载自 Zachary.XiaoZhen - 梦想的天空