- 注册时间
 - 2021-4-16
 
- 最后登录
 - 2023-9-20
 
- 在线时间
 - 1 小时
 
 
 
 
 
编程入门 
  
	- 龙马币
 - 18 
 
 
 
 
 | 
 
大家在调试一些游戏程序的时候,可能会碰到一些反调试技术,也就是说,被调试的程序可以检测到自己是否被调试器附加了, 
如果探知自己正在被调试,肯定是有人试图反汇编啦之类的方法破解自己。为了了解如何破解反调试技术,首先我们来看看反调试技术。 
 
1、Windows API方法 
Win32提供了两个API, IsDebuggerPresent和CheckRemoteDebuggerPresent可以用来检测当前进程是否正在被调试,以IsDebuggerPresent函数为例 
 
例子如下: 
- BOOL ret = IsDebuggerPresent();
 
 - printf("ret = %d\n", ret);
 
  复制代码 
  
破解方法很简单,就是在系统里将这两个函数hook掉,让这两个函数一直返回false就可以了,所以这里就不细谈了。 
 
2、使用NtQueryInformationProcess函数 
NtQueryInformationProcess函数是一个未公开的API,它的第二个参数可以用来查询进程的调试端口。如果进程被调试,那么返回的端口值会是-1,否则就是其他的值。由于这个函数是一个未公开的函数,因此需要使用LoadLibrary和GetProceAddress的方法获取调用地址,示例代码如下: 
  
- // 声明一个函数指针。
 
 - typedef NTSTATUS (WINAPI *NtQueryInformationProcessPtr)(
 
 -        HANDLE processHandle,
 
 -        PROCESSINFOCLASS processInformationClass,
 
 -        PVOID processInformation,
 
 -        ULONG processInformationLength,
 
 -        PULONG returnLength);
 
 -  
 
 - bool NtQueryInformationProcessApproach()
 
 - {
 
 -        int debugPort = 0;
 
 -        HMODULE hModule = LoadLibrary(TEXT("Ntdll.dll "));
 
 -        NtQueryInformationProcessPtr NtQueryInformationProcess = (NtQueryInformationProcessPtr)GetProcAddress(hModule, "NtQueryInformationProcess");
 
 -        if ( NtQueryInformationProcess(GetCurrentProcess(), (PROCESSINFOCLASS)7, &debugPort, sizeof(debugPort), NULL) )
 
 -               printf("[ERROR NtQueryInformationProcessApproach] NtQueryInformationProcess failed\n");
 
 -        else
 
 -               return debugPort == -1;
 
 -        return false;
 
 - }
 
  复制代码 
  
3、NtSetInformationThread方法 
这个也是使用Windows的一个未公开函数的方法,你可以在当前线程里调用NtSetInformationThread,调用这个函数时,如果在第二个参数里指定0x11这个值(意思是ThreadHideFromDebugger),等于告诉操作系统,将所有附加的调试器统统取消掉。示例代码:  
  
- // 声明一个函数指针。
 
 - typedef NTSTATUS (*NtSetInformationThreadPtr)(HANDLE threadHandle,
 
 -        THREADINFOCLASS threadInformationClass,
 
 -        PVOID threadInformation,
 
 -        ULONG threadInformationLength);
 
 -  
 
 - void NtSetInformationThreadApproach()
 
 - {
 
 -        HMODULE hModule = LoadLibrary(TEXT("ntdll.dll"));
 
 -        NtSetInformationThreadPtr NtSetInformationThread = (NtSetInformationThreadPtr)GetProcAddress(hModule, "NtSetInformationThread");
 
 -        NtSetInformationThread(GetCurrentThread(), (THREADINFOCLASS)0x11, 0, 0);
 
 - }
 
  复制代码 
  
4、触发异常的方法 
这个技术的原理是,首先,进程使用SetUnhandledExceptionFilter函数注册一个未处理异常处理函数A,如果进程没有被调试的话,那么触发一个未处理异常,会导致操作系统将控制权交给先前注册的函数A;而如果进程被调试的话,那么这个未处理异常会被调试器捕捉,这样我们的函数A就没有机会运行了。 
 
这里有一个技巧,就是触发未处理异常的时候,如果跳转回原来代码继续执行,而不是让操作系统关闭进程。方案是在函数A里修改eip的值,因为在函数A的参数_EXCEPTION_POINTERS里,会保存当时触发异常的指令地址,所以在函数A里根据这个指令地址修改寄存器eip的值就可以了,示例代码如下: 
- // 进程要注册的未处理异常处理程序A
 
 - LONG WINAPI MyUnhandledExceptionFilter(struct _EXCEPTION_POINTERS *pei)
 
 - {
 
 -        SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)
 
 -               pei->ContextRecord->Eax); 
 
 -        // 修改寄存器eip的值
 
 -        pei->ContextRecord->Eip += 2;
 
 -        // 告诉操作系统,继续执行进程剩余的指令(指令保存在eip里),而不是关闭进程
 
 -        return EXCEPTION_CONTINUE_EXECUTION;
 
 - }
 
 -  
 
 - bool UnhandledExceptionFilterApproach()
 
 - {
 
 -        SetUnhandledExceptionFilter(MyUnhandledExceptionFilter);
 
 -        __asm
 
 -        {
 
 -               // 将eax清零
 
 -               xor eax, eax
 
 -               // 触发一个除零异常
 
 -               div eax
 
 -        }
 
 -        return false;
 
 - }
 
  复制代码 
 
5、查询进程PEB的BeingDebugged标志位 
当进程被调试器所附加的时候,操作系统会自动设置这个标志位,因此在程序里定期查询这个标志位就可以了,例子如下: 
- bool PebIsDebuggedApproach()
 
 - {
 
 -        char result = 0;
 
 -        __asm
 
 -        {
 
 -          // 进程的PEB地址放在fs这个寄存器位置上
 
 -               mov eax, fs:[30h]
 
 -        // 查询BeingDebugged标志位
 
 -               mov al, BYTE PTR [eax + 2] 
 
 -               mov result, al
 
 -        }
 
 -  
 
 -        return result != 0;
 
 - }
 
  复制代码 
  
6、查询进程PEB的NtGlobal标志位  
跟第二个方法一样,当进程被调试的时候,操作系统除了修改BeingDebugged这个标志位以外,还会修改其他几个地方,其中NtDll中一些控制堆(Heap)操作的函数的标志位就会被修改,因此也可以查询这个标志位,例子如下: 
- bool PebNtGlobalFlagsApproach()
 
 - {
 
 -        int result = 0;
 
 -  
 
 -        __asm
 
 -        {
 
 -          // 进程的PEB
 
 -               mov eax, fs:[30h]
 
 -        // 控制堆操作函数的工作方式的标志位
 
 -               mov eax, [eax + 68h]
 
 -        // 操作系统会加上这些标志位***_HEAP_ENABLE_TAIL_CHECK, 
 
 -        // ***_HEAP_ENABLE_FREE_CHECK and ***_HEAP_VALIDATE_PARAMETERS,
 
 -        // 它们的并集就是x70
 
 -        //
 
 -        // 下面的代码相当于C/C++的
 
 -        //     eax = eax & 0x70
 
 -               and eax, 0x70
 
 -               mov result, eax
 
 -        }
 
 -        return result != 0;
 
 - }
 
  复制代码 
 |   
 
 
 
 |