// 通過檢查 PEB.BeingDebuged 字段判斷是否被調試 // 可以在目標程序運行之前修改對應的字段為 0 進行反反調試 bool CheckBeingDebugged() { __asm { ; 獲取到 PEB 的內容 mov eax, fs:[0x30] ; 獲取 PEB 內偏移為 2 大小為 1 字節的字段 movzx eax, byte ptr[eax + 2] } } int main() { if (CheckBeingDebugged()) printf("當前處於[被]調試狀態\n"); else printf("當前處於[非]調試狀態\n"); system("pause"); return 0; }
PEB.NtGlobalFlag
#include <iostream> #include <windows.h> // 通過 PEB.NtGlobalFlag 判斷是否被調試,當處於被調試狀態時 // PEB.NtGlobalFlag 保存的是 0x70,可以通過修改標志反反調試 bool CheckNtGlobalFlag() { __asm { ; 獲取到 PEB,保存在 FS : [0x30] mov eax, fs : [0x30] ; 獲取到 NtGlobalFlag 字段的內容 mov eax, [eax + 0x68]; } } int main() { if (CheckNtGlobalFlag()) printf("當前處於[被]調試狀態\n"); else printf("當前處於[非]調試狀態\n"); system("pause"); return 0; }
通過判斷 PEB.ProcessHeap 中具體的標志位來確定是否被調試
#include <iostream> #include <windows.h> // 通過判斷 PEB.ProcessHeap 中具體的標志位來確定是否被調試 // 對應不同的系統,標志位所在的偏移可能也不同,通過 dt _HEAP // 可以查看到當前系統中具體所在的位置 // Flags 與 ForceFlags 在正常情況下應為 2 與 0 // 如果是被附加狀態,那么無法判斷當前是否被調試 // 通過修改具體的字段進行反反調試,注意不同版本 bool CheckProcessHeap() { UINT Flags = 0, ForceFlags = 0; __asm { ; 1. 獲取到 PEB mov eax, fs:[0x30] ; 2. 獲取到 ProcessHeap mov eax, [eax + 0x18] ; 3. 獲取到 Flags mov ecx, [eax + 0x40] mov Flags, ecx ; 4. 獲取到 ForceFlags mov eax, [eax + 0x44] mov ForceFlags, eax } return (Flags == 2 && ForceFlags == 0) ? false : true; } int main() { if (CheckProcessHeap()) printf("當前處於[被]調試狀態\n"); else printf("當前處於[非]調試狀態\n"); system("pause"); return 0; }
原理就是查詢 EPROCESS 結構體中相關的ProcessDebugPort字段
#include <iostream> #include <windows.h> #include <winternl.h> #pragma comment(lib,"ntdll.lib") // 原理就是查詢 EPROCESS 結構體中相關的字段, // 因為不能在 R3 修改 R0 的數據,所以只能 HOOK bool CheckProcessDebugPort() { int nDebugPort = 0; NtQueryInformationProcess( GetCurrentProcess(), // 目標進程句柄 ProcessDebugPort, // 查詢信息類型(7) &nDebugPort, // 輸出查詢信息 sizeof(nDebugPort), // 查詢類型大小 NULL); // 實際返回數據大小 // 如果返回的是 -1 ,那么就被調試了 return nDebugPort == 0xFFFFFFFF ? true : false; } int main() { if (CheckProcessDebugPort()) printf("當前處於[被]調試狀態\n"); else printf("當前處於[非]調試狀態\n"); system("pause"); return 0; }
原理就是查詢 EPROCESS 結構體中相關的0x1E字段,
#include <iostream> #include <windows.h> #include <winternl.h> #pragma comment(lib,"ntdll.lib") // 原理就是查詢 EPROCESS 結構體中相關的字段, // 因為不能在 R3 修改 R0 的數據,所以只能 HOOK bool CheckProcessDebugObjectHandle() { HANDLE hProcessDebugObjectHandle = 0; NtQueryInformationProcess( GetCurrentProcess(), // 目標進程句柄 (PROCESSINFOCLASS)0x1E, // 查詢信息類型(0x1E) &hProcessDebugObjectHandle, // 輸出查詢信息 sizeof(hProcessDebugObjectHandle), // 查詢類型大小 NULL); // 實際返回大小 // 如果它的值是非空的,就說明被調試了 return hProcessDebugObjectHandle ? true : false; } int main() { if (CheckProcessDebugObjectHandle()) printf("當前處於[被]調試狀態\n"); else printf("當前處於[非]調試狀態\n"); system("pause"); return 0; }
原理就是查詢 EPROCESS 結構體中相關的0x1F字段,
#include <iostream> #include <windows.h> #include <winternl.h> #pragma comment(lib,"ntdll.lib") // 原理就是查詢 EPROCESS 結構體中相關的字段, // 因為不能在 R3 修改 R0 的數據,所以只能 HOOK // 通過 HOOK 函數 NtQueryInformationProcess 可以 // 直接的解決所有的關於這個 API 的反調試,但是,由於 // 這個函數的功能非常的多,所以需要經過特定的篩選,篩 // 選的值就是需要查詢的類型 0x07, 0x1E, 0x1F bool CheckProcessDebugFlag() { BOOL bProcessDebugFlag = 0; NtQueryInformationProcess( GetCurrentProcess(), // 目標進程句柄 (PROCESSINFOCLASS)0x1F, // 查詢信息類型 &bProcessDebugFlag, // 輸出查詢信息 sizeof(bProcessDebugFlag), // 查詢類型大小 NULL); // 實際返回大小 // 值為 0 的話處於一個被調試的狀態 return bProcessDebugFlag ? false : true; } int main() { if (CheckProcessDebugFlag()) printf("當前處於[被]調試狀態\n"); else printf("當前處於[非]調試狀態\n"); system("pause"); return 0; }
如果一個進程被正常打開,那么它的父進程應該是 Explorer.exe, 也就是資源管理器,如果判斷不是,就可能被調試,提供參考
#include <iostream> #include <windows.h> #include <winternl.h> #pragma comment(lib,"ntdll.lib") // 如果一個進程被正常打開,那么它的父進程應該是 Explorer.exe, // 也就是資源管理器,如果判斷不是,就可能被調試,提供參考 bool CheckParentProcess() { struct PROCESS_BASIC_INFORMATION { ULONG ExitStatus; // 進程返回碼 PPEB PebBaseAddress; // PEB 地址 ULONG AffinityMask; // CPU 親和性掩碼 LONG BasePriority; // 基本優先級 ULONG UniqueProcessId; // 本進程PID ULONG InheritedFromUniqueProcessId; // 父進程PID }stcProcInfo; // 查詢到目標進程的對應信息,主要是 父進程 ID NtQueryInformationProcess( GetCurrentProcess(), ProcessBasicInformation, &stcProcInfo, sizeof(stcProcInfo), NULL); // 查詢資源管理器對應的 PID DWORD ExplorerPID = 0; DWORD CurrentPID = stcProcInfo.InheritedFromUniqueProcessId; GetWindowThreadProcessId(FindWindow(L"Progman", NULL), &ExplorerPID); // 比對兩個 PID 的值,相同就OK,不同就可能被調試 return ExplorerPID == CurrentPID ? false : true; } int main() { if (CheckParentProcess()) printf("當前處於[被]調試狀態\n"); else printf("當前處於[非]調試狀態\n"); system("pause"); return 0; }
查詢當前的系統是否處於被調試狀態,同樣可以 HOOK
#include <iostream> #include <windows.h> #include <winternl.h> #pragma comment(lib,"ntdll.lib") // 查詢當前的系統是否處於被調試狀態,同樣可以 HOOK bool CheckSystemKernelDebuggerInformation() { struct _SYSTEM_KERNEL_DEBUGGER_INFORMATION { BOOLEAN KernelDebuggerEnabled; BOOLEAN KernelDebuggerNotPresent; }DebuggerInfo = { 0 }; NtQuerySystemInformation( (SYSTEM_INFORMATION_CLASS)0x23, // 查詢信息類型 &DebuggerInfo, // 輸出查詢信息 sizeof(DebuggerInfo), // 查詢類型大小 NULL); // 實際返回大小 return DebuggerInfo.KernelDebuggerEnabled; } int main() { if (CheckSystemKernelDebuggerInformation()) printf("當前處於[被]調試狀態\n"); else printf("當前處於[非]調試狀態\n"); system("pause"); return 0; }
隱藏線程讓調試器找不到
#include <iostream> #include <windows.h> typedef enum THREAD_INFO_CLASS { ThreadHideFromDebugger = 17 }; // 這個函數被用於設置線程的信息 typedef NTSTATUS(NTAPI* ZW_SET_INFORMATION_THREAD)( IN HANDLE ThreadHandle, IN THREAD_INFO_CLASS ThreadInformaitonClass, IN PVOID ThreadInformation, IN ULONG ThreadInformationLength); // 可以通過 HOOk 的方式進行反反調試 void ZSIT_DetachDebug() { ZW_SET_INFORMATION_THREAD Func; // 獲取到函數,因為這是一個未公開函數 Func = (ZW_SET_INFORMATION_THREAD)GetProcAddress( LoadLibrary(L"ntdll.dll"), "ZwSetInformationThread"); // 需要設置的線程,需要設置的類型,不考慮,不考慮 Func(GetCurrentThread(), ThreadHideFromDebugger, NULL, NULL); } int main() { ZSIT_DetachDebug(); printf("runnning...\n"); system("pause"); return 0; }
找窗口名
#include <iostream> #include <windows.h> // 原理是通過查找窗口類或窗口名稱對應的窗口是否存在來 // 進行調試器或其它分析工具的檢查。[缺點是窗口的的名字 // 通常不是固定的,檢查起來非常的不方便] // 還可以通過遍歷進程的方式來檢查調試器或其它工具是否 // 存在。[缺點是可以通過修改 exe 的名字修改進程名] int main() { if (FindWindow(NULL, L"OllyDbg")) printf("存在調試器\n"); else printf("沒檢測到調試器\n"); return 0; }