过TP保护的基本方案.doc

上传人:叶*** 文档编号:35246175 上传时间:2022-08-20 格式:DOC 页数:21 大小:372.50KB
返回 下载 相关 举报
过TP保护的基本方案.doc_第1页
第1页 / 共21页
过TP保护的基本方案.doc_第2页
第2页 / 共21页
点击查看更多>>
资源描述

《过TP保护的基本方案.doc》由会员分享,可在线阅读,更多相关《过TP保护的基本方案.doc(21页珍藏版)》请在得力文库 - 分享文档赚钱的网站上搜索。

1、过TP保护分析过程【转帖】本文只为研究技术,请所有童鞋切勿使用本文之方法做下那天理难容罪恶不舍之坏事。既是研究游戏保护,那么总要有一个研究对象。本文就以TMD_TP这款游戏保护为例进行分析讲解。请勿对号入座,如有雷同之处。纯属反汇编引擎之错误,不关我的事!转载请注明出处 关键字:DNF 驱动保护鉴于最近很多同学找上门来求解这那问题,反正这东西又不是绝密档案,放在我手里大半个月了,还不如放出来让大家一起进步算了。另外都是取之看雪还之看雪罢了。索性我也就公布一个全套的方案。绝无其他意思,所以还请同道中人嘴下留情。切勿背地使坏!在正式开篇之前我要感谢看雪ID:十年寒窗 在我最困惑的时候,他给予了最大

2、的帮助!另外还有一位和我同岁的神秘人物也给予了不小的帮助,感谢你们。废话了半天,正式开始吧。tmd_TP也就是国内比较流行的游戏D_N*F的游戏保护。它在ring0层一共HOOK了几个地方和一些其他的工作。来达到保护的目的下面是简报:url=javascript:复制代码/url1. NtOpenThread /防止调试器在它体内创建线程 NtOpenProcess /防止OD等在进程列表看到它 KiAttachProcess /防止其他软件附加它 NtReadVirtualMemory /防止别人读取它的内存 NtWriteVirtualMemory /防止别人在它的内存里面乱写乱画 KDC

3、OM.dll:KdReceivePacket /这两个是COM串口的接受和发送数据 KDCOM.dll:KdSendPacket /主要用来方式别人双机调试使用了KdDisableDebugger来禁用双机调试代码: url=javascript:复制代码/url1. .text:010025F0 jz short loc_1002622 .text:010025F2 call sub_10022A4 .text:010025F7 call ds:KdDisableDebugger .text:010025FD push offset byte_10022EC .text:01002602 p

4、ush esi .text:01002603 push offset byte_10022DC .text:01002608 push edi .text:01002609 push dword_100CF24并对debugport进行了疯狂的清零操作甚至还包括EPROCESS+70+74+78等几处位置图片:1.jpg处理的手段通常都是向64端口写入FE导致计算机被重启代码: url=javascript:复制代码/url1. .text:01001665 mov al, 0FEh .text:01001667 out 64h, al ; AT Keyboard controller 804

5、2. .text:01001667 ; Resend the last transmission .text:01001669 popa .text:0100166A retn下面简单看下他关键的几个HOOK:KiAttachProcess 图片:2.jpgNtReadVirtualMemory 图片:3.jpgNtWriteVirtualMemory 图片:4.jpgNtOpenThread图片:5.jpgNtOpenProcess 图片:6.jpg引用:其中,前3个直接恢复即可。第4个有监视,直接恢复即刻重启第5个和ring3有通信,直接恢复1分钟内SX非法模块 根据上面的分析,下面给出相

6、应的解决方案1.直接恢复 第1、2、3处HOOK2.绕过4、5处HOOK3.将debugport清零的内核线程干掉4.恢复硬件断点但是要有一个先后的逻辑顺序因为内核有一个线程负责监视几个地方,必须要先干掉它。但是这个内容我写在了处理debugport清零的一起,也就是第3步。所以大家在照搬源码的时候注意代码执行次序先从简单的工作讲起,恢复1、2、3处的HOOKKiAttachProcess的处理代码: url=javascript:复制代码/url1. / / 名称: Nakd_KiAttachProcess / 功能: My_RecoveryHook_KiAttachProcess的中继函数

7、 / 参数: / 返回: / static NAKED VOID Nakd_KiAttachProcess() _asm mov edi,edi push ebp mov ebp,esp push ebx push esi mov eax,KiAttachProcessAddress /注意这个是全局变量 BYTE* add eax,7 jmp eax / / 名称: RecoveryHook_KiAttachProcess / 功能: 解除游戏保护对_KiAttachProcess函数的HOOK(DNF) / 参数: / 返回: 状态 / NTSTATUS My_RecoveryHook_K

8、iAttachProcess() BYTE *KeAttachProcessAddress = NULL; /KeAttachProcess函数地址 BYTE *p; BYTE MovEaxAddress5 = 0xB8,0,0,0,0; / BYTE JmpEax2 = 0xff,0xe0; KIRQL Irql; /特征码 BYTE Signature1 = 0x56, /p-1 Signature2 = 0x57, /p-2 Signature3 = 0x5F, /p-3 Signature4 = 0x5E, /p+5 Signature5 = 0xE8; /p第一个字节 /获得KeAt

9、tachProcess地址,然后通过特征码找到 /KiAttachProcess的地址 KeAttachProcessAddress = (BYTE*)MyGetFunAddress(LKeAttachProcess); if (KeAttachProcessAddress = NULL) KdPrint(KeAttachProcess地址获取失败n); return FAILED_TO_OBTAIN_FUNCTION_ADDRESSES; /将p指向KeAttachProcess函数开始处 p = KeAttachProcessAddress; while (1) if (*(p-1) =

10、Signature1) & (*(p-2) = Signature2) & (*(p+5) = Signature3) & (*(p+6) = Signature4) & (*p = Signature5) /定位成功后取地址 KiAttachProcessAddress = *(PULONG)(p+1)+(ULONG)(p+5); break; /推动指针 p+; /计算中继函数地址 *(ULONG *)(MovEaxAddress+1)=(ULONG)Nakd_KiAttachProcess; WPOFF(); /清除CR0 /提升IRQL中断级 Irql=KeRaiseIrqlToDpc

11、Level(); /写入 RtlCopyMemory(KiAttachProcessAddress,MovEaxAddress,5); RtlCopyMemory(KiAttachProcessAddress+5,JmpEax,2); /恢复Irql KeLowerIrql(Irql); WPON(); /恢复CR0 return STATUS_SUCCESS; NtReadVirtualMemory和NtWriteVirtualMemory的处理注意这里,我对他们俩开头的第2句PUSH的处理我直接写入了push 0x78563412大家可以根据自己的地址来硬编码一次。或者干脆这样使用代码:

12、url=javascript:复制代码/url1. / / 名称: My_RecoveryHook_NtReadAndWriteMemory / 功能: 解除游戏保护对NtReadVirtualMemory和 / NtWriteVirtualMemory的HOOK / 参数: / 返回: / NTSTATUS My_RecoveryHook_NtReadAndWriteMemory() BYTE Push1Ch2 = 0x6a,0x1c; /02字节 BYTE PushAdd5 = 0x68,0x12,0x34,0x56,0x78; /NtReadVirtualMemory物理机 /BYTE

13、PushAdd25 = 0x68,0xf0,0x6f,0x4f,0x80; /NtWriteVirtualMemory物理机 KIRQL Irql; BYTE *NtReadVirtualMemoryAddress = NULL; /NtReadVirtualMemory的地址 BYTE *NtWriteVirtualMemoryAddress = NULL; /NtWriteVirtualMemory的地址 /从SSDT表中获取NtReadVirtualMemory函数地址 NtReadVirtualMemoryAddress = (BYTE*)myGetCurrentAddress(0xB

14、A); if (NtReadVirtualMemoryAddress = NULL) KdPrint(NtReadVirtualMemory函数地址获取失败! n); return FAILED_TO_OBTAIN_FUNCTION_ADDRESSES; /从SSDT表中获取NtWriteVirtualMemory函数地址 NtWriteVirtualMemoryAddress = (BYTE*)myGetCurrentAddress(0x115); if (NtWriteVirtualMemoryAddress = NULL) KdPrint(NtWriteVirtualMemory函数地址

15、获取失败! n); return FAILED_TO_OBTAIN_FUNCTION_ADDRESSES; WPOFF(); /清除CR0 /提升IRQL中断级 Irql=KeRaiseIrqlToDpcLevel(); /写入 RtlCopyMemory(NtReadVirtualMemoryAddress,Push1Ch,2); RtlCopyMemory(NtReadVirtualMemoryAddress+2,PushAdd,5); RtlCopyMemory(NtWriteVirtualMemoryAddress,Push1Ch,2); RtlCopyMemory(NtWriteVi

16、rtualMemoryAddress+2,PushAdd,5); /恢复Irql KeLowerIrql(Irql); WPON(); /恢复CR0 return STATUS_SUCCESS; 好了,下面来处理NtOpenProcess和NtOpenThread这两个函数的处理上不能太鲁莽了。手法要风骚一点细腻一点了介于篇幅的原因,我只贴出来前者的处理方法,后者雷同细微之处大家自行修改。我总不能真的给你方法又给你工具。眼看着自己变成教唆犯代码: url=javascript:复制代码/url1. /NtOpenProcess用到的全局变量为了方便堆栈平衡的处理使用全局变量 PEPROCESS

17、 processEPROCESS = NULL; /保存访问者的EPROCESS ANSI_STRING p_str1,p_str2; /保存进程名称 BYTE *ObOpenObjectByPointerAddress = NULL; /ObOpenObjectByPointer的地址 BYTE *p_TpHookAddress = NULL; /TP的HOOK函数地址 BYTE *p_ReturnAddress = NULL; /返回到的地址 BYTE *p_MyHookAddress = NULL; /我们的HOOK函数在哪写入 #define DNF_EXE DNF.exe /要检索的

18、进程名 / / 名称: Nakd_NtOpenProcess / 功能: My_RecoveryHook_NtOpenProcess的中继函数 / 参数: / 返回: / static NAKED VOID Nakd_NtOpenProcess() /获得调用者的EPROCESS processEPROCESS = IoGetCurrentProcess(); /将调用者的进程名保存到str1中 RtlInitAnsiString(&p_str1,(ULONG)processEPROCESS+0x174); /将我们要比对的进程名放入str2 RtlInitAnsiString(&p_str2

19、,DNF_EXE); if (RtlCompareString(&p_str1,&p_str2,TRUE) = 0) /说明是DNF进程访问了这里 _asm push dword ptr ebp-38h push dword ptr ebp-24h push p_ReturnAddress mov eax,p_TpHookAddress jmp eax else _asm push dword ptr ebp-38h push dword ptr ebp-24h push p_ReturnAddress mov eax,ObOpenObjectByPointerAddress jmp eax

20、/ / 名称: My_RecoveryHook_NtOpenProcess / 功能: 解除游戏保护对NtOpenProcess的HOOK / 参数: / 返回: 状态 / NTSTATUS My_RecoveryHook_NtOpenProcess() BYTE *NtOpenProcessAddress = NULL; /NtOpenProcess的地址 BYTE *p = NULL; /临时 TOP5CODE *top5code = NULL; /保存5字节内容 BYTE JmpAddress6 = 0xE9,0,0,0,0,0x90; KIRQL Irql; /获取NtOpenProc

21、ess的地址 NtOpenProcessAddress = (BYTE*)MyGetFunAddress(LNtOpenProcess); if (NtOpenProcessAddress = NULL) KdPrint(NtOpenProcess地址获取失败n); return FAILED_TO_OBTAIN_FUNCTION_ADDRESSES; /获取ObOpenObjectByPointer的地址 ObOpenObjectByPointerAddress = (BYTE*)MyGetFunAddress(LObOpenObjectByPointer); if (ObOpenObjec

22、tByPointerAddress = NULL) KdPrint(ObOpenObjectByPointer地址获取失败n); return FAILED_TO_OBTAIN_FUNCTION_ADDRESSES; /将p指向NtOpenProcess函数开始处 p = NtOpenProcessAddress; /用一个无限循环来判断给定的特征码来确定被HOOK位置 while (1) if (*(p-7) = 0x50) & (*(p-0xE) = 0x56) & (*(p+0xd) = 0x50) & (*(p+0x16) = 0x3b) & (*(p+0x17) = 0xce) &

23、(*p = 0xE8) & (*(p+5) = 0x8b) & (*(p+6) = 0xf8) KdPrint(%0X n,(ULONG)p); break; /推动指针向前走 p+; /将top5code指向 p 的当前处 /用以取出 call 地址 这5字节里面的地址 top5code = (TOP5CODE*)p; p_TpHookAddress = (BYTE*)(ULONG)p+5+top5code-address); /找到我们写入自定义函数的地址 p_MyHookAddress = p-6; /保存调用ObOpenObjectByPointer函数以后的返回地址 p_Return

24、Address = p+5; /将一条JMP Nakd_NtOpenProcess写入到数组中 *(ULONG *)(JmpAddress+1)=(ULONG)Nakd_NtOpenProcess - (ULONG)p_MyHookAddress+5); WPOFF(); /清除CR0 /提升IRQL中断级 Irql=KeRaiseIrqlToDpcLevel(); /写入 RtlCopyMemory(p_MyHookAddress,JmpAddress,6); /恢复Irql KeLowerIrql(Irql); WPON(); /恢复CR0 return STATUS_SUCCESS; 处

25、理之后:图片:7.jpg简而言之其原理就是,任何人调用了NtOpenProcess的时候会先进入Nakd_NtOpenProcess函数,我们判断。如果是游戏进程访问的话,就有可能是验证之类的我们转到它自己的函数里面。让它保持与ring3层的通信。否则的话,嘿嘿接下来是第3步处理debugport清零的这块了。我想绝大多数人关心的都是这里了网络上能搜多到的办法几乎都失效了有办法的人又不肯放出来,急眼了就自己想了个土办法虽然不那么时尚。但是绝对的奏效。由于代码凌乱不堪,简单说下其原理。我们定位内核模块TxxxSxxx.sys的首地址然后根据特征码遍历整个模块找到我们需要的地方,然后干掉他们。那么

26、我们又如何能够通过人工的判断出来到底是哪里在作怪呢利用syser或Start SoftICE对EPROCESS+BC处设置断点。就可以一层一层的追溯上去了到底如何用他们,我想大家自己多花点时间在看雪和GOOGLE或者BAIDU上面是不会吃亏的。由于ZwQuerySystemInformation函数的使用非常繁琐。而且篇幅有限。所以我只给出关键代码,至于这个函数如何使用。大家可以自己在搜索引擎找“枚举内核模块”代码: url=javascript:复制代码/url1. / / 名称: MyEnumKernelModule / 功能: 枚举内核模块 / 参数: str:内核模块名称 / modu

27、leadd:该模块地址传出 / modulesie:该模块大小传出 / 返回: / NTSTATUS MyEnumKernelModule(IN CHAR* str,OUT ULONG *moduleadd,OUT ULONG *modulesie) NTSTATUS status = STATUS_SUCCESS; ULONG n = 0; ULONG i = 0; PSYSTEM_MODULE_INFORMATION_ENTRY module = NULL; PVOID pbuftmp = NULL; ANSI_STRING ModuleName1,ModuleName2; BOOLEAN

28、 tlgstst= FALSE; /如果找到了指定模块则设置为TRUE /利用11号功能枚举内核模块 status = ZwQuerySystemInformation(11, &n, 0, &n); /申请内存 pbuftmp = ExAllocatePool(NonPagedPool, n); /再次执行,将枚举结果放到指定的内存区域 status = ZwQuerySystemInformation(11, pbuftmp, n, NULL); module = (PSYSTEM_MODULE_INFORMATION_ENTRY)(PULONG )pbuftmp + 1 ); /初始化字

29、符串 RtlInitAnsiString(&ModuleName1,str); / n = *(PULONG)pbuftmp ); for ( i = 0; i n; i+ ) RtlInitAnsiString(&ModuleName2,&module.ImageName); /DbgPrint(%dt0x%08X 0x%08X %sn,module.LoadOrderIndex,module.Base,module.Size,module.ImageName); if (RtlCompareString(&ModuleName1,&ModuleName2,TRUE) = 0) DbgPri

30、nt(MyEnumKernelModule:%s:%0X n,ModuleName2.Buffer,module.Base); *moduleadd = module.Base; *modulesie = module.Size; tlgstst = TRUE; break; ExFreePool(pbuftmp); if tlgstst = FALSE) return FAILED_TO_OBTAIN_FUNCTION_ADDRESSES; return status; / / 名称: My_Recovery_Debugport / 功能: 恢复游戏对debugport的清零操作 / 参数:

31、 / 返回: / NTSTATUS My_Recovery_Debugport() NTSTATUS stats; BYTE *sd1 = NULL,*sd2 = NULL,*pd = NULL; ULONG ModuleSize,ModuleAddress,i,number = 0; BYTE *p; KIRQL Irql; BYTE C3902 = 0xc3,0x90; /获取指定的内核模块地址和字节数 stats = MyEnumKernelModule(?c:windowssystem32tessafe.sys,&ModuleAddress,&ModuleSize); if (stat

32、s = FAILED_TO_OBTAIN_FUNCTION_ADDRESSES) return FAILED_TO_OBTAIN_FUNCTION_ADDRESSES; KdPrint(Address:%0X Sie:%d n,ModuleAddress,ModuleSize); /特征码 /* sd1特征 p-1:18 p-2:87 p-3:DB p-4:33 p-5:07 p-6:03 p :33 p+1:C0 p+7:3B p+8:D8 sd2特征 p-1:07 p-2:87 p-3:c0 p-4:33 p+14:89 p+15:1c p+16:38 */ /将P指向内核模块开始处 p

33、= (BYTE*)ModuleAddress + 20; for (i = 0; i = 3) KdPrint(特征 %d -退出n,number); break; /首先干掉监视函数 while (1) if (*(pd-1) = 0xcc) & (*(pd-2) = 0xcc) KdPrint(pd首地址:%0X n,(ULONG)pd); WPOFF(); /清除CR0 /提升IRQL中断级 Irql=KeRaiseIrqlToDpcLevel(); /写入 RtlCopyMemory(pd,C390,2); /恢复Irql KeLowerIrql(Irql); WPON(); /恢复C

34、R0 break; pd-; /干掉2个SD while (1) if (*(sd1-1) = 0xcc) & (*(sd1-2) = 0xcc) KdPrint(sd1首地址:%0X n,(ULONG)sd1); WPOFF(); /清除CR0 /提升IRQL中断级 Irql=KeRaiseIrqlToDpcLevel(); /写入 RtlCopyMemory(sd1,C390,2); /恢复Irql KeLowerIrql(Irql); WPON(); /恢复CR0 break; sd1-; while (1) if (*(sd2-1) = 0xcc) & (*(sd2-2) = 0xcc

35、) KdPrint(sd2首地址:%0X n,(ULONG)sd2); WPOFF(); /清除CR0 /提升IRQL中断级 Irql=KeRaiseIrqlToDpcLevel(); /写入 RtlCopyMemory(sd2,C390,2); /恢复Irql KeLowerIrql(Irql); WPON(); /恢复CR0 break; sd2-; return STATUS_SUCCESS; 图片:8.jpg最后,处理一下硬件断点就可以了这里我们使用到了SSDT HOOK分别HOOK了 SSDT 表中索引为 0xD5和0x55的函数。由于这里比较简单我想10个人有9个人懂得SSDT H

36、OOK的。所以直接给出源码,不做原理分析了代码: url=javascript:复制代码/url1. /处理硬件断点时 ULONG uNtSetContextThreadAddress; ULONG uNtGetContextThreadAddress; ULONG TenNtSetContextThread, TenNtGetContextThread; / / 名称: _MyNtGetThreadContext / 功能: 两个SSDT HOOK伪造函数的中继函数 / 参数: / 返回: / static NAKED NTSTATUS Nakd_NtGetThreadContext(HAN

37、DLE hThread, PCONTEXT pContext) _asm jmp dword ptrTenNtGetContextThread static NAKED NTSTATUS Nakd_NtSetThreadContext(HANDLE hThread, PCONTEXT pContext) _asm jmp dword ptrTenNtSetContextThread / / 名称: MyNtGetThreadContext & MyNtSetThreadContext / 功能: NtGetThreadContext与NtSetThreadContext函数被SSDT HOOK的伪造函数 / 参数: / 返回: /

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 教育专区 > 高中资料

本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知得利文库网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

工信部备案号:黑ICP备15003705号-8 |  经营许可证:黑B2-20190332号 |   黑公网安备:91230400333293403D

© 2020-2023 www.deliwenku.com 得利文库. All Rights Reserved 黑龙江转换宝科技有限公司 

黑龙江省互联网违法和不良信息举报
举报电话:0468-3380021 邮箱:hgswwxb@163.com