// 通过检查 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; }