ProcessHeap 是Windows
进程的默认堆,每个进程都有一个默认的堆,用于在进程地址空间中分配内存空间。默认情况下ProcessHeap
由内核进行初始化,该堆中存在一个未公开的属性,它被设置为加载器为进程分配的第一个堆的位置(进程堆标志),ProcessHeap
标志位于PEB
结构中偏移为0x18
处,第一个堆头部有一个属性字段,这个属性叫做ForceFlags
属性偏移为0x44
,该属性为0说明程序没有被调试,非0说明被调试,另外的Flags
属性为2说明被调试,不为2则说明没有被调试。
0 :000 > dt !_pebntdll!_PEB +0x000 InheritedAddressSpace : UChar +0x001 ReadImageFileExecOptions : UChar +0x002 BeingDebugged : UChar +0x018 ProcessHeap : Ptr32 Void 0 :000 > !heap Heap Address NT/Segment Heap 1270000 NT Heap 0 :000 > !heap -a 1270000 Index Address Name Debugging options enabled 1 : 01270000 Segment at 01270000 to 0136f 000 (00006000 bytes committed) Flags: 40000062 ForceFlags: 40000060 Granularity: 8 bytes Segment Reserve: 00100000 Segment Commit: 00002000 0 :000 > dt _HEAP 1270000 ntdll!_HEAP +0x000 Segment : _HEAP_SEGMENT +0x000 Entry : _HEAP_ENTRY +0x040 Flags : 0x40000062 +0x044 ForceFlags : 0x40000060
这里需要注意一点,堆区在不同系统中偏移值是不同的,在Windows10
系统中ForceFlags
属性位于堆头部偏移量为0x44
处,而默认情况如果被调试则ForceFlags
属性为0x40000060
,而Flags
标志为0x40000062
,有了这些参考那么通过汇编语言实现将变得很容易,如下代码则是通过汇编分别读取这两个堆头参数;
#include <stdio.h> #include <windows.h> int IsDebug (DWORD x) { DWORD Debug = 0 ; if (x == 1 ) { __asm { mov eax, fs:[0x18 ] mov eax, [eax + 0x30 ] mov eax, [eax + 0x18 ] mov eax, [eax + 0x44 ] mov Debug, eax } } if (x == 2 ) { __asm { mov eax, fs:[0x18 ] mov eax, [eax + 0x30 ] mov eax, [eax + 0x18 ] mov eax, [eax + 0x40 ] mov Debug, eax } } return Debug; } int main (int argc, char * argv[]) { if (IsDebug(1 ) && IsDebug(2 )) { printf ("[-] 进程正在被调试 \n" ); } else { printf ("[*] 进程正常 \n" ); } system("pause" ); return 0 ; }
另一种通过C语言实现的反调试版本,其反调试原理与上方相同,只不过此处我们使用了系统的API来完成检测标志位的。
#include <stdio.h> #include <windows.h> #include <winternl.h> typedef NTSTATUS (NTAPI *typedef_ZwQueryInformationProcess) ( IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT PULONG ReturnLength OPTIONAL ) ;BOOL IsDebug () { HANDLE hProcess = NULL ; DWORD ProcessId = 0 ; PROCESS_BASIC_INFORMATION Pbi; typedef_ZwQueryInformationProcess pZwQueryInformationProcess = NULL ; ProcessId = GetCurrentProcessId(); hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId); if (hProcess != NULL ) { HMODULE hModule = LoadLibrary("ntdll.dll" ); pZwQueryInformationProcess = (typedef_ZwQueryInformationProcess)GetProcAddress(hModule, "ZwQueryInformationProcess" ); NTSTATUS Status = pZwQueryInformationProcess(hProcess, ProcessBasicInformation, &Pbi, sizeof (PROCESS_BASIC_INFORMATION), NULL ); if (NT_SUCCESS(Status)) { DWORD ByteRead = 0 ; DWORD ProcessHeap = 0 ; ULONG PebBase = (ULONG)Pbi.PebBaseAddress; DWORD ForceFlagsValue = 1 ; ReadProcessMemory(hProcess, (LPCVOID)(PebBase + 0x18 ), &ProcessHeap, 2 , &ByteRead); ReadProcessMemory(hProcess, (LPCVOID)(ProcessHeap + 0x40 ), &ForceFlagsValue, 4 , &ByteRead); if (ForceFlagsValue != 0 ) { return TRUE; } } CloseHandle(hProcess); } return FALSE; } int main (int argc, char * argv[]) { if (IsDebug()) { printf ("[-] 正在被调试 \n" ); } system("pause" ); return 0 ; }