龙马谷

 找回密码
 立即注册

QQ登录

只需一步,快速开始

龙马谷VIP会员办理客服QQ:82926983(如果临时会话没有收到回复,请先加QQ好友再发。)
1 [已完结] GG修改器新手入门与实战教程 31课 2 [已完结] GG修改器美化修改教程 6课 3 [已完结] GG修改器Lua脚本新手入门教程 12课
4 [已完结] 触动精灵脚本新手入门必学教程 22课 5 [已完结] 手游自动化脚本入门实战教程 9课 6 [已完结] C++射击游戏方框骨骼透视与自瞄教程 27课
7 [已完结] C++零基础UE4逆向开发FPS透视自瞄教程 29课 8 [已完结] C++零基础大漠模拟器手游自动化辅助教程 22课
以下是天马阁VIP教程,本站与天马阁合作,赞助VIP可以获得天马阁对应VIP会员,名额有限! 点击进入天马阁论坛
1 [已完结] x64CE与x64dbg入门基础教程 7课 2 [已完结] x64汇编语言基础教程 16课 3 [已完结] x64辅助入门基础教程 9课
4 [已完结] C++x64内存辅助实战技术教程 149课 5 [已完结] C++x64内存检测与过检测技术教程 10课 6 [已完结] C+x64二叉树分析遍历与LUA自动登陆教程 19课
7 [已完结] C++BT功能原理与x64实战教程 29课 8 [已完结] C+FPS框透视与自瞄x64实现原理及防护思路
查看: 3177|回复: 0

游戏的反调试技术的分析报告

[复制链接]

4

主题

0

回帖

6

积分

编程入门

Rank: 1

龙马币
18
月光小丫头 | 显示全部楼层 |阅读模式
大家在调试一些游戏程序的时候,可能会碰到一些反调试技术,也就是说,被调试的程序可以检测到自己是否被调试器附加了,
如果探知自己正在被调试,肯定是有人试图反汇编啦之类的方法破解自己。为了了解如何破解反调试技术,首先我们来看看反调试技术。

1、Windows API方法
Win32提供了两个API, IsDebuggerPresent和CheckRemoteDebuggerPresent可以用来检测当前进程是否正在被调试,以IsDebuggerPresent函数为例

例子如下:
  1. BOOL ret = IsDebuggerPresent();
  2. printf("ret = %d\n", ret);
复制代码


破解方法很简单,就是在系统里将这两个函数hook掉,让这两个函数一直返回false就可以了,所以这里就不细谈了。

2、使用NtQueryInformationProcess函数
NtQueryInformationProcess函数是一个未公开的API,它的第二个参数可以用来查询进程的调试端口。如果进程被调试,那么返回的端口值会是-1,否则就是其他的值。由于这个函数是一个未公开的函数,因此需要使用LoadLibrary和GetProceAddress的方法获取调用地址,示例代码如下:

  1. // 声明一个函数指针。
  2. typedef NTSTATUS (WINAPI *NtQueryInformationProcessPtr)(
  3.        HANDLE processHandle,
  4.        PROCESSINFOCLASS processInformationClass,
  5.        PVOID processInformation,
  6.        ULONG processInformationLength,
  7.        PULONG returnLength);

  8. bool NtQueryInformationProcessApproach()
  9. {
  10.        int debugPort = 0;
  11.        HMODULE hModule = LoadLibrary(TEXT("Ntdll.dll "));
  12.        NtQueryInformationProcessPtr NtQueryInformationProcess = (NtQueryInformationProcessPtr)GetProcAddress(hModule, "NtQueryInformationProcess");
  13.        if ( NtQueryInformationProcess(GetCurrentProcess(), (PROCESSINFOCLASS)7, &debugPort, sizeof(debugPort), NULL) )
  14.               printf("[ERROR NtQueryInformationProcessApproach] NtQueryInformationProcess failed\n");
  15.        else
  16.               return debugPort == -1;
  17.        return false;
  18. }
复制代码


3、NtSetInformationThread方法
这个也是使用Windows的一个未公开函数的方法,你可以在当前线程里调用NtSetInformationThread,调用这个函数时,如果在第二个参数里指定0x11这个值(意思是ThreadHideFromDebugger),等于告诉操作系统,将所有附加的调试器统统取消掉。示例代码:

  1. // 声明一个函数指针。
  2. typedef NTSTATUS (*NtSetInformationThreadPtr)(HANDLE threadHandle,
  3.        THREADINFOCLASS threadInformationClass,
  4.        PVOID threadInformation,
  5.        ULONG threadInformationLength);

  6. void NtSetInformationThreadApproach()
  7. {
  8.        HMODULE hModule = LoadLibrary(TEXT("ntdll.dll"));
  9.        NtSetInformationThreadPtr NtSetInformationThread = (NtSetInformationThreadPtr)GetProcAddress(hModule, "NtSetInformationThread");
  10.        NtSetInformationThread(GetCurrentThread(), (THREADINFOCLASS)0x11, 0, 0);
  11. }
复制代码


4、触发异常的方法
这个技术的原理是,首先,进程使用SetUnhandledExceptionFilter函数注册一个未处理异常处理函数A,如果进程没有被调试的话,那么触发一个未处理异常,会导致操作系统将控制权交给先前注册的函数A;而如果进程被调试的话,那么这个未处理异常会被调试器捕捉,这样我们的函数A就没有机会运行了。

这里有一个技巧,就是触发未处理异常的时候,如果跳转回原来代码继续执行,而不是让操作系统关闭进程。方案是在函数A里修改eip的值,因为在函数A的参数_EXCEPTION_POINTERS里,会保存当时触发异常的指令地址,所以在函数A里根据这个指令地址修改寄存器eip的值就可以了,示例代码如下:
  1. // 进程要注册的未处理异常处理程序A
  2. LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *pei)
  3. {
  4.        SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)
  5.               pei->ContextRecord->Eax);
  6.        // 修改寄存器eip的值
  7.        pei->ContextRecord->Eip += 2;
  8.        // 告诉操作系统,继续执行进程剩余的指令(指令保存在eip里),而不是关闭进程
  9.        return EXCEPTION_CONTINUE_EXECUTION;
  10. }

  11. bool UnhandledExceptionFilterApproach()
  12. {
  13.        SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
  14.        __asm
  15.        {
  16.               // 将eax清零
  17.               xor eax, eax
  18.               // 触发一个除零异常
  19.               div eax
  20.        }
  21.        return false;
  22. }
复制代码


5、查询进程PEB的BeingDebugged标志位
当进程被调试器所附加的时候,操作系统会自动设置这个标志位,因此在程序里定期查询这个标志位就可以了,例子如下:
  1. bool PebIsDebuggedApproach()
  2. {
  3.        char result = 0;
  4.        __asm
  5.        {
  6.          // 进程的PEB地址放在fs这个寄存器位置上
  7.               mov eax, fs:[30h]
  8.        // 查询BeingDebugged标志位
  9.               mov al, BYTE PTR [eax + 2]
  10.               mov result, al
  11.        }

  12.        return result != 0;
  13. }
复制代码


6、查询进程PEB的NtGlobal标志位
跟第二个方法一样,当进程被调试的时候,操作系统除了修改BeingDebugged这个标志位以外,还会修改其他几个地方,其中NtDll中一些控制堆(Heap)操作的函数的标志位就会被修改,因此也可以查询这个标志位,例子如下:
  1. bool PebNtGlobalFlagsApproach()
  2. {
  3.        int result = 0;

  4.        __asm
  5.        {
  6.          // 进程的PEB
  7.               mov eax, fs:[30h]
  8.        // 控制堆操作函数的工作方式的标志位
  9.               mov eax, [eax + 68h]
  10.        // 操作系统会加上这些标志位***_HEAP_ENABLE_TAIL_CHECK,
  11.        // ***_HEAP_ENABLE_FREE_CHECK and ***_HEAP_VALIDATE_PARAMETERS,
  12.        // 它们的并集就是x70
  13.        //
  14.        // 下面的代码相当于C/C++的
  15.        //     eax = eax & 0x70
  16.               and eax, 0x70
  17.               mov result, eax
  18.        }
  19.        return result != 0;
  20. }
复制代码

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

龙马谷| C/C++辅助教程| 安卓逆向安全| 论坛导航| 免责申明|Archiver|
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表龙马谷立场!
任何人不得以任何方式翻录、盗版或出售本站视频,一经发现我们将追究其相关责任!
我们一直在努力成为最好的编程论坛!
Copyright© 2018-2021 All Right Reserved.
在线客服
快速回复 返回顶部 返回列表