反调试功能<CheckRemoteDebuggerPresent>
CheckRemoteDebuggerPresent这个API可以检测是否有调试器的存在,而且这个和PEB里面的BeingDebugged无关了。看一下CheckRemoteDebuggerPresent的声明:
__in HANDLE hProcess, __in_out PBOOL pbDebuggerPresent );
第一个参数是进程句柄,第二参数用于存放结果。返回值表示函数是否执行成功,
; 堆栈示意图 ;push pBool ; ebp + c ;push hProcess ; ebp + 8 ;push retaddr ; ebp + 4 ;push ebp <------------------------------------------- | 7C85AA22 > 8BFF MOV EDI,EDI ; | 7C85AA24 55 PUSH EBP ; | 7C85AA25 8BEC MOV EBP,ESP ;<-| 7C85AA27 837D 08 00 CMP DWORD PTR SS:[EBP+8],0 ; hProcess == 0 ? 7C85AA2B 56 PUSH ESI ; 7C85AA2C 74 35 JE SHORT 7C85AA63 ; jmp exit 7C85AA2E 8B75 0C MOV ESI,DWORD PTR SS:[EBP+C] ; esi = pBool 7C85AA31 85F6 TEST ESI,ESI ; esi == 0? 7C85AA33 74 2E JE SHORT 7C85AA63 ; jmp exit 7C85AA35 6A 00 PUSH 0 ; push 0 7C85AA37 6A 04 PUSH 4 ; push 4 7C85AA39 8D45 08 LEA EAX,DWORD PTR SS:[EBP+8] ; 7C85AA3C 50 PUSH EAX ; push offset hProcess 7C85AA3D 6A 07 PUSH 7 ; push ProcessDebugPort 7C85AA3F FF75 08 PUSH DWORD PTR SS:[EBP+8] ; push hProcess 7C85AA42 FF15 AC10807C CALL DWORD PTR DS:[<&ntdll.NtQueryInform> ; ntdll.ZwQueryInformationProcess 7C85AA48 85C0 TEST EAX,EAX ; eax == 0 ? 7C85AA4A 7D 08 JGE SHORT 7C85AA54 ; eax >= 0 ----------- 7C85AA4C 50 PUSH EAX ; push eax | 7C85AA4D E8 ABE9FAFF CALL 7C8093FD ; call 7C8093FD | 7C85AA52 EB 16 JMP SHORT 7C85AA6A ; jmp | 7C85AA54 33C0 XOR EAX,EAX ; eax = 0 <----------- 7C85AA56 3945 08 CMP DWORD PTR SS:[EBP+8],EAX ; hProcess == 0 ? 7C85AA59 0F95C0 SETNE AL ; hProcess != 0 --> al = 1 (else al = 0) 7C85AA5C 8906 MOV DWORD PTR DS:[ESI],EAX ; *pBool = eax 7C85AA5E 33C0 XOR EAX,EAX ; eax = 0 7C85AA60 40 INC EAX ; eax = 1 7C85AA61 EB 09 JMP SHORT 7C85AA6C ; jmp ---------------- 7C85AA63 6A 57 PUSH 57 ; push 57 | 7C85AA65 E8 D8E8FAFF CALL 7C809342 ; call 7C809342 | 7C85AA6A 33C0 XOR EAX,EAX ; eax = 0 | 7C85AA6C 5E POP ESI ; <------------------- 7C85AA6D 5D POP EBP ; 7C85AA6E C2 0800 RETN 8 ; return --------------------------------------------------------------------------------
CheckRemoteDebuggerPresent实际上调用了ntdll里面的ZwQueryInformationProcess来检测。这是一个Native API,声明如下:
NTSTATUS NtQueryInformationProcess ( __in HANDLE ProcessHandle, __in PROCESSINFOCLASS ProcessInformationClass, __out_bcount(ProcessInformationLength) PVOID ProcessInformation, __in ULONG ProcessInformationLength, __out_opt PULONG ReturnLength );
这里第二个参数是7,实际上被定义为ProcessDebugPort
测试代码如下:
HMODULE hKernel32Dll = ::LoadLibrary(TEXT("kernel32.dll")); if (NULL != hKernel32Dll) { fnCheckRemoteDebuggerPresent fn = (fnCheckRemoteDebuggerPresent)::GetProcAddress(hKernel32Dll, "CheckRemoteDebuggerPresent"); if (!fn) { ::FreeLibrary(hKernel32Dll); return DGBTOOL_NO; } BOOL bDebuggerPresent = FALSE; if(fn(GetCurrentProcess(), &bDebuggerPresent) &&bDebuggerPresent) { ::FreeLibrary(hKernel32Dll); return DBGTOOL_CUSTOM; } else { ::FreeLibrary(hKernel32Dll); return DGBTOOL_NO; } } return DGBTOOL_NO;
过掉方法:Hook住CheckRemoteDebuggerPresent,调用了ZwQueryInformationProcess之前的je改成jmp来跳过Zw这个函数~~OD还会在ZwQueryInformationProcess这个函数的调用地址强行改了,去调用作者的一个函数,该函数中根据是否查询的是7来决定时候调用ZwQueryInformationProcess: