IsDebuggerPresent()函数
包含在debugapi.h头文件中,函数原型:
1
| BOOL IsDebuggerPresent();
|
该函数允许程序确定是否正在由用户模式调试器(如 OllyDbg 或 x64dbg)调试它,以便可以修改其行为。通常,该函数只检查进程环境块 (PEB) 的 BeingDebugged 标志。
Assembly:
1 2 3 4 5 6 7
| call IsDebuggerPresent test al, al jne being_debugged ... being_debugged: push 1 call ExitProcess
|
C/C++:
1 2
| if (IsDebuggerPresent()) ExitProcess(-1);
|
CheckRemoteDebuggerPresent()函数
包含在kernel32中的CheckRemoteDebuggerPresent()检查调试器(在同一台计算机上的不同进程中)是否连接到当前进程。
X86 Assembly:
1 2 3 4 5 6 7 8 9 10
| lea eax, [bDebuggerPresent] push eax push -1 ; GetCurrentProcess() call CheckRemoteDebuggerPresent cmp [bDebuggerPresent], 1 jz being_debugged ... being_debugged: push -1 call ExitProcess
|
X64 Assembly:
1 2 3 4 5 6 7 8 9
| lea rdx, [bDebuggerPresent] mov rcx, -1 ; GetCurrentProcess() call CheckRemoteDebuggerPresent cmp [bDebuggerPresent], 1 jz being_debugged ... being_debugged: mov ecx, -1 call ExitProcess
|
C/C++:
1 2 3 4
| BOOL bDebuggerPresent; if (TRUE == CheckRemoteDebuggerPresent(GetCurrentProces(), &bDebuggerPresent) && TRUE == bDebuggerPresent) ExitProcess(-1);
|
PEB->BeingDebugged
FS标志段寄存器总是指向TEB(当前的线程环境块),其中包含一个指针指向当前PEB(进程环境块),该结构体包含一个成员BeingDebugged,它是一个标志位,用于标识当前进程是否正在被调试

该函数包含在ntdll.h头文件中,可以从进程中检索不同类型的信息。它接受一个ProcessInformationClass参数,该参数指定要获取的信息并定义ProcessInformation参数的输出类型。
ProcessDebugPort
可以使用ntdll检索进程的调试器的端口号!NtQueryInformationProcess()的有一个记录在案的类ProcessDebugPort,如果正在调试进程,则该类将检索等于 0xFFFFFFFF(十进制 -1) 的 DWORD 值,C/C++语法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| __kernel_entry NTSTATUS NtQueryInformationProcess( IN HANDLE ProcessHandle, IN PROCESSINFOCLASS ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT PULONG ReturnLength ); HMODULE hNtdll = LoadLibraryA("ntdll.dll"); if (hNtdll) { auto pfnNtQueryInformationProcess = (TNtQueryInformationProcess)GetProcAddress( hNtdll, "NtQueryInformationProcess"); if (pfnNtQueryInformationProcess) { DWORD dwProcessDebugPort, dwReturned; NTSTATUS status = pfnNtQueryInformationProcess( GetCurrentProcess(), ProcessDebugPort, &dwProcessDebugPort, sizeof(DWORD), &dwReturned); if (NT_SUCCESS(status) && (-1 == dwProcessDebugPort)) ExitProcess(-1); } }
|
X86 Assembly:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| lea eax, [dwReturned] push eax ; ReturnLength push 4 ; ProcessInformationLength lea ecx, [dwProcessDebugPort] push ecx ; ProcessInformation push 7 ; ProcessInformationClass push -1 ; ProcessHandle call NtQueryInformationProcess inc dword ptr [dwProcessDebugPort] jz being_debugged ... being_debugged: push -1 call ExitProcess
|
X64 Assembly:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| lea rcx, [dwReturned] push rcx ; ReturnLength mov r9d, 4 ; ProcessInformationLength lea r8, [dwProcessDebugPort] ; ProcessInformation mov edx, 7 ; ProcessInformationClass mov rcx, -1 ; ProcessHandle call NtQueryInformationProcess cmp dword ptr [dwProcessDebugPort], -1 jz being_debugged ... being_debugged: mov ecx, -1 call ExitProcess
|
ProcessDebugFlags
名为EPROCESS的内核结构(表示进程对象)包含字段 NoDebugInherit。可以使用未记录的类 ProcessDebugFlags(0x1f)检索此字段的反向值。因此,如果返回值为 0,则存在调试器。
C/C++:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| typedef NTSTATUS(NTAPI *TNtQueryInformationProcess)( IN HANDLE ProcessHandle, IN DWORD ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT PULONG ReturnLength ); HMODULE hNtdll = LoadLibraryA("ntdll.dll"); if (hNtdll) { auto pfnNtQueryInformationProcess = (TNtQueryInformationProcess)GetProcAddress( hNtdll, "NtQueryInformationProcess"); if (pfnNtQueryInformationProcess) { DWORD dwProcessDebugFlags, dwReturned; const DWORD ProcessDebugFlags = 0x1f; NTSTATUS status = pfnNtQueryInformationProcess( GetCurrentProcess(), ProcessDebugFlags, &dwProcessDebugFlags, sizeof(DWORD), &dwReturned); if (NT_SUCCESS(status) && (0 == dwProcessDebugFlags)) ExitProcess(-1); } }
|
X86 Assembly:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| lea eax, [dwReturned] push eax ; ReturnLength push 4 ; ProcessInformationLength lea ecx, [dwProcessDebugFlags] push ecx ; ProcessInformation push 1Fh ; ProcessInformationClass push -1 ; ProcessHandle call NtQueryInformationProcess cmp dword ptr [dwProcessDebugFlags], 0 jz being_debugged ... being_debugged: push -1 call ExitProcess
|
X64 Assembly:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| lea rcx, [dwReturned] push rcx ; ReturnLength mov r9d, 4 ; ProcessInformationLength lea r8, [dwProcessDebugFlags] ; ProcessInformation mov edx, 1Fh ; ProcessInformationClass mov rcx, -1 ; ProcessHandle call NtQueryInformationProcess cmp dword ptr [dwProcessDebugFlags], 0 jz being_debugged ... being_debugged: mov ecx, -1 call ExitProcess
|
ProcessDebugObjectHandle
调试开始时,将创建一个名为debug object的内核对象。可以使用未记录的ProcessDebugObjectHandle(0x1e)类来查询此句柄的值。
C/C++:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| typedef NTSTATUS(NTAPI * TNtQueryInformationProcess)( IN HANDLE ProcessHandle, IN DWORD ProcessInformationClass, OUT PVOID ProcessInformation, IN ULONG ProcessInformationLength, OUT PULONG ReturnLength ); HMODULE hNtdll = LoadLibraryA("ntdll.dll"); if (hNtdll) { auto pfnNtQueryInformationProcess = (TNtQueryInformationProcess)GetProcAddress( hNtdll, "NtQueryInformationProcess"); if (pfnNtQueryInformationProcess) { DWORD dwReturned; HANDLE hProcessDebugObject = 0; const DWORD ProcessDebugObjectHandle = 0x1e; NTSTATUS status = pfnNtQueryInformationProcess( GetCurrentProcess(), ProcessDebugObjectHandle, &hProcessDebugObject, sizeof(HANDLE), &dwReturned); if (NT_SUCCESS(status) && (0 != hProcessDebugObject)) ExitProcess(-1); } }
|
X86 Assembly:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| lea eax, [dwReturned] push eax ; ReturnLength push 4 ; ProcessInformationLength lea ecx, [dwProcessDebugFlags] push ecx ; ProcessInformation push 1Fh ; ProcessInformationClass push -1 ; ProcessHandle call NtQueryInformationProcess cmp dword ptr [dwProcessDebugFlags], 0 jz being_debugged ... being_debugged: push -1 call ExitProcess
|
X64 Assembly:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| lea rcx, [dwReturned] push rcx ; ReturnLength mov r9d, 4 ; ProcessInformationLength lea r8, [hProcessDebugObject] ; ProcessInformation mov edx, 1Eh ; ProcessInformationClass mov rcx, -1 ; ProcessHandle call NtQueryInformationProcess cmp dword ptr [hProcessDebugObject], 0 jnz being_debugged ... being_debugged: mov ecx, -1 call ExitProcess
|
其检索一个WORD_PTR值,该值是进程的调试器端口号。非零值指示进程正在环3调试器的控制下运行,如果进程没有调试器,则返回零
EPROCESS_DebugPort
获取系统内核中标记进程信息的结构体EPROCESS,通过EPROCESS结构体中的DebugPort成员判断进程是否正在被调试,一般无法使用普通方法实现,需要内核调试

5. 异常处理检测
处理异常时,正常运行过程会将信息发给Windows的SEH异常捕获流程,而进行调试时则会发给调试器

6. 断点检测
函数断点体现为0xCC,可通过对比内存数据中的指令数据与磁盘内文件的数据进行对比,如发现存在不同,则说明有断点,即程序被调试

7. 检测调试器进程
通过枚举进程,判断进程名是否为调试器进程名,如果是,则说明程序正在被调试

还可通过查看是否存在调试器的窗口,判断程序是否正在被调试。可使用VC++中附带的Spy++查找窗口名

8. 时间检测

程序运行时,如果被调试,则由于会出现单步运行等过程,程序运行速度会变慢,因此可通过检测程序运行时间,判断程序是否正在被调试