DebugBlocker反调试技术
字数 1733 2025-08-06 20:12:33
DebugBlocker反调试技术详解
0x00 技术原理
DebugBlocker是一种硬核的反调试技术,其核心原理是:
- 父子进程调试关系:父进程创建并调试子进程,两者通常是同一可执行程序
- 代码分流执行:通过
IsDebuggerPresent等检测调试的技术使父子进程执行不同代码 - 逻辑隐藏:真正的程序逻辑往往在修改后的子进程中,而子进程无法直接附加调试
- 技术组合:通常与SMC(自修改代码)技术和异常处理机制结合使用
工作流程:
- 父进程负责恢复控制流和程序代码
- 子进程执行真正的程序代码
0x01 关键API和结构体
1. CreateProcessA - 创建新进程及其主线程
BOOL CreateProcessA(
[in, optional] LPCSTR lpApplicationName,
[in, out, optional] LPSTR lpCommandLine,
[in, optional] LPSECURITY_ATTRIBUTES lpProcessAttributes,
[in, optional] LPSECURITY_ATTRIBUTES lpThreadAttributes,
[in] BOOL bInheritHandles,
[in] DWORD dwCreationFlags,
[in, optional] LPVOID lpEnvironment,
[in, optional] LPCSTR lpCurrentDirectory,
[in] LPSTARTUPINFOA lpStartupInfo,
[out] LPPROCESS_INFORMATION lpProcessInformation
);
关键结构体:
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess; // 新进程的句柄
HANDLE hThread; // 新建进程的主线程的句柄
DWORD dwProcessId; // PID
DWORD dwThreadId; // TID
} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;
关键标志位dwCreationFlags:
DEBUG_PROCESS(0x00000001):启动并调试新进程,可使用WaitForDebugEvent接收调试事件DEBUG_ONLY_THIS_PROCESS(0x00000002):与DEBUG_PROCESS同时使用时,调用方只能调试该新进程
2. WaitForDebugEvent - 等待调试进程中的调试事件
BOOL WaitForDebugEvent(
[out] LPDEBUG_EVENT lpDebugEvent,
[in] DWORD dwMilliseconds
);
关键结构体:
typedef struct _DEBUG_EVENT {
DWORD dwDebugEventCode;
DWORD dwProcessId;
DWORD dwThreadId;
union {
EXCEPTION_DEBUG_INFO Exception;
// ... 其他调试信息
} u;
} DEBUG_EVENT, *LPDEBUG_EVENT;
异常记录结构体:
typedef struct _EXCEPTION_RECORD {
DWORD ExceptionCode;
DWORD ExceptionFlags;
struct _EXCEPTION_RECORD *ExceptionRecord;
PVOID ExceptionAddress;
DWORD NumberParameters;
ULONG_PTR ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS];
} EXCEPTION_RECORD;
常见异常类型:
0x80000003:int3断点触发0xc0000005:访问未分配(不合理)的地址触发
3. Get/SetThreadContext - 获取/设置线程上下文
BOOL GetThreadContext(
[in] HANDLE hThread, // 线程句柄
[in, out] LPCONTEXT lpContext // 上下文结构指针
);
BOOL SetThreadContext(
HANDLE hThread,
CONST CONTEXT *lpContext
);
4. Read/WriteProcessMemory - 进程内存读写
BOOL WriteProcessMemory(
HANDLE hProcess, // 进程句柄
LPVOID lpBaseAddress, // 基址指针
LPVOID lpBuffer, // 要写入数据的指针
DWORD nSize, // 写入的字节数
LPDWORD lpNumberOfBytesWritten
);
BOOL ReadProcessMemory(
[in] HANDLE hProcess,
[in] LPCVOID lpBaseAddress, // 读取内存的基址
[out] LPVOID lpBuffer, // 存放读出数据的缓冲区
[in] SIZE_T nSize, // 读取字节数
[out] SIZE_T *lpNumberOfBytesRead
);
5. ContinueDebugEvent - 继续调试事件
BOOL ContinueDebugEvent(
[in] DWORD dwProcessId, // 继续调试的PID
[in] DWORD dwThreadId, // 继续调试的TID
[in] DWORD dwContinueStatus // 继续调试事件的选项
);
dwContinueStatus通常使用DBG_CONTINUE常量(0x10002),表示异常已处理,继续执行。
0x02 实例分析:2022鹏城杯BUG之眼
程序结构
int __cdecl main(int argc, const char **argv, const char **envp)
{
if (IsDebuggerPresent())
sub_1400024B0(); // 被调试时执行的代码
else
sub_140002D50(); // 原始进程执行的代码
return 0;
}
父进程逻辑(sub_140002D50)
- 获取当前文件路径
- 调用
CreateProcessA创建新进程,dwCreationFlags设为1(DEBUG_PROCESS) - 使用
WaitForDebugEvent等待调试事件 - 处理
0x80000003(int3断点)异常:- 获取异常地址
- 使用
GetThreadContext获取主线程的RIP - 用
WriteProcessMemory将0xCC替换为0xC3(ret指令)
- 解密
enc_data处的数据:- 读取
enc_data到缓冲区 - 按长度分组进行逐字节异或解密
- 将解密数据写回新进程
- 读取
- 调用
ContinueDebugEvent继续调试
子进程逻辑(sub_1400024B0)
- 向内存写入0xCC(int3)
- 以函数方式调用触发异常,让父进程捕获并修改
enc_data - 将0xCC修改为0xC3(ret)后继续执行
- 创建并调试孙子进程
孙子进程处理
- 第一次在
0x141000000触发异常:- 子进程将EIP改为
0x140001330并继续执行
- 子进程将EIP改为
- 在
0x140001330执行中出现的异常:- 使用修改后的
enc_data进行处理 enc_data[0]:异常处距离0x140001330的偏移enc_data[1]:代码块大小enc_data[2]:用于修复0xCC
- 使用修改后的
手动修复代码
使用IDAPython脚本修复解密后的代码:
import idautils
enc_data = [
0x0000000000000000, 0x000000000000005F, 0x000000000000008C,
0x000000000000005F, 0x000000000000001E, 0x0000000000000084,
# ... 更多数据
]
start = 0x140001330
for p in range(0, len(enc_data), 3):
offset = enc_data[p] # 异常地址偏移
size = enc_data[p+1] # 代码块大小
tmp = enc_data[p+2] # 首字节恢复
adr = start + offset
k = list(str(adr + 1))
PatchByte(adr, Byte(adr) ^ tmp)
for i in range(1, size): # 修复代码块大小
t = Byte(adr + i)
PatchByte(adr + i, t ^ ord(k[(i-1) % len(k)]))
if i % len(k) == 0:
k = list(str(adr + i))
print('ok')
0x03 对抗思路
-
绕过调试检测:
- 修改
IsDebuggerPresent返回值 - 使用硬件断点替代软件断点
- 修改
-
获取解密代码:
- 在合适位置patch,使孙子进程不设置调试关系
- 尝试附加调试孙子进程
-
API拦截:
- 拦截
ContinueDebugEvent等关键API - 修改调试事件处理逻辑
- 拦截
-
静态分析:
- 通过IDA Python等工具静态修复代码
- 分析加密算法和密钥生成方式
0x04 总结
DebugBlocker是一种有效的反调试技术,通过:
- 父子进程调试关系隐藏真实逻辑
- 结合SMC技术动态修改代码
- 利用异常处理机制实现代码流控制
分析这类技术需要:
- 深入理解Windows调试API
- 掌握异常处理流程
- 具备静态分析和动态调试结合的能力
- 熟悉常见的加密和代码混淆技术