Windows 平台反调试相关的技术方法总结—part 2
字数 1041 2025-08-05 08:19:19
Windows平台反调试技术详解(Part 2)
1. 陷阱标志(TF)检查技术
技术原理
陷阱标志(TF)位于EFLAGS寄存器内,当TF设置为1时,CPU将在每个指令执行后产生INT 01h(单步)异常。
实现代码
BOOL isDebugged = TRUE;
__try {
__asm {
pushfd
or dword ptr[esp], 0x100 // 设置Trap Flag
popfd // 将值加载到EFLAGS寄存器
nop
}
}
__except (EXCEPTION_EXECUTE_HANDLER) {
// 如果引发异常-调试器不存在
isDebugged = FALSE;
}
if (isDebugged) {
std::cout << "Stop debugging program!" << std::endl;
exit(-1);
}
绕过方法
- 不要单步执行pushfd指令
- 跳过该指令并在其后设置断点
- 断点后继续跟踪执行
2. CheckRemoteDebuggerPresent和NtQueryInformationProcess
CheckRemoteDebuggerPresent实现
int main(int argc, char *argv[]) {
BOOL isDebuggerPresent = FALSE;
if (CheckRemoteDebuggerPresent(GetCurrentProcess(), &isDebuggerPresent)) {
if (isDebuggerPresent) {
std::cout << "Stop debugging program!" << std::endl;
exit(-1);
}
}
return 0;
}
NtQueryInformationProcess实现
typedef NTSTATUS(NTAPI *pfnNtQueryInformationProcess)(
_In_ HANDLE ProcessHandle,
_In_ UINT ProcessInformationClass,
_Out_ PVOID ProcessInformation,
_In_ ULONG ProcessInformationLength,
_Out_opt_ PULONG ReturnLength
);
const UINT ProcessDebugPort = 7;
int main(int argc, char *argv[]) {
pfnNtQueryInformationProcess NtQueryInformationProcess = NULL;
NTSTATUS status;
DWORD isDebuggerPresent = 0;
HMODULE hNtDll = LoadLibrary(TEXT("ntdll.dll"));
if (NULL != hNtDll) {
NtQueryInformationProcess = (pfnNtQueryInformationProcess)GetProcAddress(hNtDll, "NtQueryInformationProcess");
if (NULL != NtQueryInformationProcess) {
status = NtQueryInformationProcess(
GetCurrentProcess(),
ProcessDebugPort,
&isDebuggerPresent,
sizeof(DWORD),
NULL);
if (status == 0x00000000 && isDebuggerPresent != 0) {
std::cout << "Stop debugging program!" << std::endl;
exit(-1);
}
}
}
return 0;
}
绕过方法
使用mhook库钩子NtQueryInformationProcess函数:
#include <Windows.h>
#include "mhook.h"
typedef NTSTATUS(NTAPI *pfnNtQueryInformationProcess)(
_In_ HANDLE ProcessHandle,
_In_ UINT ProcessInformationClass,
_Out_ PVOID ProcessInformation,
_In_ ULONG ProcessInformationLength,
_Out_opt_ PULONG ReturnLength
);
const UINT ProcessDebugPort = 7;
pfnNtQueryInformationProcess g_origNtQueryInformationProcess = NULL;
NTSTATUS NTAPI HookNtQueryInformationProcess(
_In_ HANDLE ProcessHandle,
_In_ UINT ProcessInformationClass,
_Out_ PVOID ProcessInformation,
_In_ ULONG ProcessInformationLength,
_Out_opt_ PULONG ReturnLength
) {
NTSTATUS status = g_origNtQueryInformationProcess(
ProcessHandle,
ProcessInformationClass,
ProcessInformation,
ProcessInformationLength,
ReturnLength);
if (status == 0x00000000 && ProcessInformationClass == ProcessDebugPort) {
*((PDWORD_PTR)ProcessInformation) = 0;
}
return status;
}
DWORD SetupHook(PVOID pvContext) {
HMODULE hNtDll = LoadLibrary(TEXT("ntdll.dll"));
if (NULL != hNtDll) {
g_origNtQueryInformationProcess = (pfnNtQueryInformationProcess)GetProcAddress(hNtDll, "NtQueryInformationProcess");
if (NULL != g_origNtQueryInformationProcess) {
Mhook_SetHook((PVOID*)&g_origNtQueryInformationProcess, HookNtQueryInformationProcess);
}
}
return 0;
}
BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpvReserved) {
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hInstDLL);
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)SetupHook, NULL, NULL, NULL);
Sleep(20);
case DLL_PROCESS_DETACH:
if (NULL != g_origNtQueryInformationProcess) {
Mhook_Unhook((PVOID*)&g_origNtQueryInformationProcess);
}
break;
}
return TRUE;
}
3. 基于NtQueryInformationProcess的其他反调试技术
3.1 ProcessDebugObjectHandle (0x1E)
status = NtQueryInformationProcess(
GetCurrentProcess(),
ProcessDebugObjectHandle,
&hProcessDebugObject,
sizeof(HANDLE),
NULL);
if (0x00000000 == status && NULL != hProcessDebugObject) {
std::cout << "Stop debugging program!" << std::endl;
exit(-1);
}
3.2 ProcessDebugFlags (0x1F)
status = NtQueryInformationProcess(
GetCurrentProcess(),
ProcessDebugObjectHandle,
&debugFlags,
sizeof(ULONG),
NULL);
if (0x00000000 == status && NULL != debugFlags) {
std::cout << "Stop debugging program!" << std::endl;
exit(-1);
}
3.3 ProcessBasicInformation (0x00)
std::wstring GetProcessNameById(DWORD pid) {
HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hProcessSnap == INVALID_HANDLE_VALUE) {
return 0;
}
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
std::wstring processName = L"";
if (!Process32First(hProcessSnap, &pe32)) {
CloseHandle(hProcessSnap);
return processName;
}
do {
if (pe32.th32ProcessID == pid) {
processName = pe32.szExeFile;
break;
}
} while (Process32Next(hProcessSnap, &pe32));
CloseHandle(hProcessSnap);
return processName;
}
status = NtQueryInformationProcess(
GetCurrentProcess(),
ProcessBasicInformation,
&processBasicInformation,
sizeof(PROCESS_BASIC_INFORMATION),
NULL);
std::wstring parentProcessName = GetProcessNameById((DWORD)processBasicInformation.InheritedFromUniqueProcessId);
if (L"devenv.exe" == parentProcessName) {
std::cout << "Stop debugging program!" << std::endl;
exit(-1);
}
绕过方法
修改NtQueryInformationProcess返回值:
- 将ProcessDebugObjectHandle设置为0
- 将ProcessDebugFlags设置为1
- 对于ProcessBasicInformation,将InheritedFromUniqueProcessId值更改为另一个进程ID
4. 断点检测技术
4.1 软件断点检测
DWORD CalcFuncCrc(PUCHAR funcBegin, PUCHAR funcEnd) {
DWORD crc = 0;
for (; funcBegin < funcEnd; ++funcBegin) {
crc += *funcBegin;
}
return crc;
}
#pragma auto_inline(off)
VOID DebuggeeFunction() {
int calc = 0;
calc += 2;
calc <<= 8;
calc -= 3;
}
VOID DebuggeeFunctionEnd() {};
#pragma auto_inline(on)
DWORD g_origCrc = 0x2bd0;
int main() {
DWORD crc = CalcFuncCrc((PUCHAR)DebuggeeFunction, (PUCHAR)DebuggeeFunctionEnd);
if (g_origCrc != crc) {
std::cout << "Stop debugging program!" << std::endl;
exit(-1);
}
return 0;
}
4.2 硬件断点检测
CONTEXT ctx = {};
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
if (GetThreadContext(GetCurrentThread(), &ctx)) {
if (ctx.Dr0 != 0 || ctx.Dr1 != 0 || ctx.Dr2 != 0 || ctx.Dr3 != 0) {
std::cout << "Stop debugging program!" << std::endl;
exit(-1);
}
}
硬件断点重置
CONTEXT ctx = {};
ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
SetThreadContext(GetCurrentThread(), &ctx);
绕过方法
钩子NtGetContextThread和NtSetContextThread函数:
typedef NTSTATUS(NTAPI *pfnNtGetContextThread)(
_In_ HANDLE ThreadHandle,
_Out_ PCONTEXT pContext
);
typedef NTSTATUS(NTAPI *pfnNtSetContextThread)(
_In_ HANDLE ThreadHandle,
_In_ PCONTEXT pContext
);
pfnNtGetContextThread g_origNtGetContextThread = NULL;
pfnNtSetContextThread g_origNtSetContextThread = NULL;
NTSTATUS NTAPI HookNtGetContextThread(
_In_ HANDLE ThreadHandle,
_Out_ PCONTEXT pContext
) {
DWORD backupContextFlags = pContext->ContextFlags;
pContext->ContextFlags &= ~CONTEXT_DEBUG_REGISTERS;
NTSTATUS status = g_origNtGetContextThread(ThreadHandle, pContext);
pContext->ContextFlags = backupContextFlags;
return status;
}
NTSTATUS NTAPI HookNtSetContextThread(
_In_ HANDLE ThreadHandle,
_In_ PCONTEXT pContext
) {
DWORD backupContextFlags = pContext->ContextFlags;
pContext->ContextFlags &= ~CONTEXT_DEBUG_REGISTERS;
NTSTATUS status = g_origNtSetContextThread(ThreadHandle, pContext);
pContext->ContextFlags = backupContextFlags;
return status;
}
void HookThreadContext() {
HMODULE hNtDll = LoadLibrary(TEXT("ntdll.dll"));
g_origNtGetContextThread = (pfnNtGetContextThread)GetProcAddress(hNtDll, "NtGetContextThread");
g_origNtSetContextThread = (pfnNtSetContextThread)GetProcAddress(hNtDll, "NtSetContextThread");
Mhook_SetHook((PVOID*)&g_origNtGetContextThread, HookNtGetContextThread);
Mhook_SetHook((PVOID*)&g_origNtSetContextThread, HookNtSetContextThread);
}
5. SEH(结构化异常处理)反调试
实现代码
BOOL g_isDebuggerPresent = TRUE;
EXCEPTION_DISPOSITION ExceptionRoutine(
PEXCEPTION_RECORD ExceptionRecord,
PVOID EstablisherFrame,
PCONTEXT ContextRecord,
PVOID DispatcherContext
) {
g_isDebuggerPresent = FALSE;
ContextRecord->Eip += 1;
return ExceptionContinueExecution;
}
int main() {
__asm {
// 设置SEH处理程序
push ExceptionRoutine
push dword ptr fs:[0]
mov dword ptr fs:[0], esp
// 生成中断
int 3h
// 恢复原始SEH处理程序
mov eax, [esp]
mov dword ptr fs:[0], eax
add esp, 8
}
if (g_isDebuggerPresent) {
std::cout << "Stop debugging program!" << std::endl;
exit(-1);
}
return 0;
}
总结
本文详细介绍了Windows平台上的多种反调试技术,包括:
- 陷阱标志(TF)检查
- CheckRemoteDebuggerPresent和NtQueryInformationProcess检测
- 基于NtQueryInformationProcess的其他检测方法
- ProcessDebugObjectHandle
- ProcessDebugFlags
- ProcessBasicInformation
- 断点检测技术
- 软件断点检测
- 硬件断点检测和重置
- SEH(结构化异常处理)反调试
对于每种技术,都提供了详细的实现代码和相应的绕过方法。这些技术可以单独使用,也可以组合使用以提高反调试效果。在实际应用中,开发者应根据具体需求选择合适的技术组合。