通过硬件断点绕过hook检测
字数 1679 2025-08-07 08:22:39

通过硬件断点绕过Hook检测技术详解

0x00 前言

Hook技术主要分为两类:

  1. 基于修改函数地址的Hook(如IAT Hook、SSDT Hook)
    • 只能Hook已导出函数
    • 对未导出函数无效
  2. 基于修改函数代码的Hook(如Inline Hook)
    • 可以Hook任意函数
    • 在实际应用中更受青睐

0x01 Hook检测与常规绕过方法

检测示例

  • 示例程序:Win32 MessageBox程序
  • 检测函数:Hook_E9
    • 检测MessageBoxA是否被Hook
    • 检测到Hook则调用ExitProcess退出程序

常规Inline Hook方法

  1. 修改5字节硬编码(E9跳转)
  2. 通过DLL远程线程注入
  3. 问题:会被E9硬编码检测拦截

改进方法

  1. 使用call短跳转到未监控区域
  2. 再从该区域跳转到处理函数
  3. 问题:会被CRC校验检测拦截

结论:任何修改内存的Hook方法都无法绕过CRC检测

0x02 硬件断点基础

断点类型对比

  1. 软件断点
    • 原理:修改指令为0xCC
    • 需要修改内存
  2. 内存断点
    • 原理:修改PTE属性触发异常
    • 需要修改内存
  3. 硬件断点
    • 原理:利用CPU调试寄存器
    • 不修改程序内存

调试寄存器(Dr0-Dr7)

  • Dr0-Dr3:存储断点地址(最多4个断点)
  • Dr4-Dr5:保留
  • Dr6:调试状态寄存器
  • Dr7:调试控制寄存器(最重要)

Dr7寄存器详解

  • L0/G0 - L3/G3

    • 控制Dr0-Dr3是否有效
    • Lx=1:局部有效(异常后清零)
    • Gx=1:全局有效(异常后不清零)
  • 断点长度(LENx)

    • 00:1字节
    • 01:2字节
    • 11:4字节
  • 断点类型(R/Wx)

    • 00:执行断点
    • 01:写入断点
    • 11:访问断点

硬件断点流程

  1. CPU检测当前地址与调试寄存器地址匹配
  2. 查IDT表找到中断处理函数(nt!_KiTrap01)
  3. CommonDispatchException
  4. KiDispatchException
  5. DbgkForwardException发送调试事件

0x03 绕过Hook检测的思路

核心思想

利用硬件断点触发异常,在异常处理函数中修改目标值

实现步骤

  1. 定位目标线程(Hook检测线程)
  2. 获取线程句柄(OpenThread)
  3. 注册异常处理函数(SetUnhandledExceptionFilter)
  4. 设置硬件断点(修改Dr0-Dr3和Dr7)
  5. 通过SetThreadContext应用设置

0x04 具体实现

1. 获取关键函数地址

g_fnOpenThread = (FNOPENTHREAD)::GetProcAddress(LoadLibrary("kernel32.dll"), "OpenThread");
g_dwHookAddr = (DWORD)GetProcAddress(GetModuleHandle("user32.dll"), "MessageBoxA");

2. 遍历线程定位目标

HANDLE hTool32 = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
THREADENTRY32 thread_entry32;
if (Thread32First(hTool32, &thread_entry32)) {
    do {
        if (thread_entry32.th32OwnerProcessID == GetCurrentProcessId()) {
            // 找到目标线程
            break;
        }
    } while (Thread32Next(hTool32, &thread_entry32));
}

3. 获取线程句柄

hHookThread = g_fnOpenThread(
    THREAD_SET_CONTEXT | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, 
    FALSE, 
    thread_entry32.th32ThreadID
);

4. 注册异常处理函数

SetUnhandledExceptionFilter(MyExceptionFilter);

5. 异常处理函数实现

LONG WINAPI MyExceptionFilter(PEXCEPTION_POINTERS pExceptionInfo) {
    if (pExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP) {
        if ((DWORD)pExceptionInfo->ExceptionRecord->ExceptionAddress == g_dwHookAddr) {
            PCONTEXT pContext = pExceptionInfo->ContextRecord;
            ChangeContext(pContext); // 修改上下文
            pContext->Eip = (DWORD)&OriginalFunc; // 修改EIP
            return EXCEPTION_CONTINUE_EXECUTION;
        }
    }
    return EXCEPTION_CONTINUE_SEARCH;
}

6. 修改上下文函数

void ChangeContext(PCONTEXT pContext) {
    char szNewText[] = "SEH Hook successfully";
    LPSTR lpOldText = (LPSTR)(*(DWORD*)(pContext->Esp + 0x8)); // 获取MessageBox文本参数
    DWORD dwLength = strlen(lpOldText);
    
    DWORD dwOldProtect;
    VirtualProtect(lpOldText, dwLength, PAGE_EXECUTE_READWRITE, &dwOldProtect);
    memcpy(lpOldText, szNewText, dwLength); // 修改文本内容
    VirtualProtect(lpOldText, dwLength, dwOldProtect, 0);
}

7. 原始函数跳转

g_dwHookAddrOffset = g_dwHookAddr + 2; // 跳过mov edi,edi

void __declspec(naked) OriginalFunc() {
    __asm {
        mov edi,edi
        jmp [g_dwHookAddrOffset]
    }
}

8. 设置硬件断点

CONTEXT threadContext;
threadContext.ContextFlags = CONTEXT_DEBUG_REGISTERS;
threadContext.Dr0 = g_dwHookAddr; // 断点地址
threadContext.Dr7 = 1; // L0=1,其他位为0(1字节执行断点)

SetThreadContext(hHookThread, &threadContext);
CloseHandle(hHookThread);

0x05 技术要点总结

  1. 硬件断点优势

    • 不修改目标进程内存
    • 绕过CRC等内存校验检测
    • 基于线程上下文,不影响其他线程
  2. 关键设置

    • Dr0-Dr3:设置断点地址
    • Dr7:控制断点行为
      • LENx:断点长度
      • R/Wx:断点类型
      • Lx/Gx:断点作用域
  3. 异常处理

    • 检测EXCEPTION_SINGLE_STEP
    • 验证异常地址是否为Hook目标
    • 修改上下文后继续执行
  4. 注意事项

    • 每个线程只能设置4个硬件断点
    • 需要正确处理线程上下文
    • 注意API函数前导指令(如mov edi,edi)

0x06 实际应用效果

  1. 常规Inline Hook(E8/E9跳转)会被CRC检测拦截
  2. 使用硬件断点方法:
    • 成功注入Hook_SEH.dll
    • 未被任何检测机制拦截
    • 成功修改MessageBox文本内容

0x07 扩展思考

  1. 防御措施

    • 检测Dr0-Dr7寄存器异常修改
    • 使用更复杂的校验机制
    • 监控SetThreadContext等API调用
  2. 其他应用场景

    • 反调试绕过
    • 代码流劫持
    • 敏感API监控
  3. 进阶技术

    • 结合VEH(向量化异常处理)
    • 多硬件断点协同工作
    • 动态调整断点策略

通过硬件断点实现Hook绕过是一种高级技术,它充分利用了CPU的调试功能,避免了传统Hook方法的内存修改问题,在对抗强检测机制时具有显著优势。

通过硬件断点绕过Hook检测技术详解 0x00 前言 Hook技术主要分为两类: 基于修改函数地址的Hook(如IAT Hook、SSDT Hook) 只能Hook已导出函数 对未导出函数无效 基于修改函数代码的Hook(如Inline Hook) 可以Hook任意函数 在实际应用中更受青睐 0x01 Hook检测与常规绕过方法 检测示例 示例程序:Win32 MessageBox程序 检测函数:Hook_ E9 检测MessageBoxA是否被Hook 检测到Hook则调用ExitProcess退出程序 常规Inline Hook方法 修改5字节硬编码(E9跳转) 通过DLL远程线程注入 问题:会被E9硬编码检测拦截 改进方法 使用call短跳转到未监控区域 再从该区域跳转到处理函数 问题:会被CRC校验检测拦截 结论 :任何修改内存的Hook方法都无法绕过CRC检测 0x02 硬件断点基础 断点类型对比 软件断点 : 原理:修改指令为0xCC 需要修改内存 内存断点 : 原理:修改PTE属性触发异常 需要修改内存 硬件断点 : 原理:利用CPU调试寄存器 不修改程序内存 调试寄存器(Dr0-Dr7) Dr0-Dr3 :存储断点地址(最多4个断点) Dr4-Dr5 :保留 Dr6 :调试状态寄存器 Dr7 :调试控制寄存器(最重要) Dr7寄存器详解 L0/G0 - L3/G3 : 控制Dr0-Dr3是否有效 Lx=1:局部有效(异常后清零) Gx=1:全局有效(异常后不清零) 断点长度(LENx) : 00:1字节 01:2字节 11:4字节 断点类型(R/Wx) : 00:执行断点 01:写入断点 11:访问断点 硬件断点流程 CPU检测当前地址与调试寄存器地址匹配 查IDT表找到中断处理函数(nt!_ KiTrap01) CommonDispatchException KiDispatchException DbgkForwardException发送调试事件 0x03 绕过Hook检测的思路 核心思想 利用硬件断点触发异常,在异常处理函数中修改目标值 实现步骤 定位目标线程(Hook检测线程) 获取线程句柄(OpenThread) 注册异常处理函数(SetUnhandledExceptionFilter) 设置硬件断点(修改Dr0-Dr3和Dr7) 通过SetThreadContext应用设置 0x04 具体实现 1. 获取关键函数地址 2. 遍历线程定位目标 3. 获取线程句柄 4. 注册异常处理函数 5. 异常处理函数实现 6. 修改上下文函数 7. 原始函数跳转 8. 设置硬件断点 0x05 技术要点总结 硬件断点优势 : 不修改目标进程内存 绕过CRC等内存校验检测 基于线程上下文,不影响其他线程 关键设置 : Dr0-Dr3:设置断点地址 Dr7:控制断点行为 LENx:断点长度 R/Wx:断点类型 Lx/Gx:断点作用域 异常处理 : 检测EXCEPTION_ SINGLE_ STEP 验证异常地址是否为Hook目标 修改上下文后继续执行 注意事项 : 每个线程只能设置4个硬件断点 需要正确处理线程上下文 注意API函数前导指令(如mov edi,edi) 0x06 实际应用效果 常规Inline Hook(E8/E9跳转)会被CRC检测拦截 使用硬件断点方法: 成功注入Hook_ SEH.dll 未被任何检测机制拦截 成功修改MessageBox文本内容 0x07 扩展思考 防御措施 : 检测Dr0-Dr7寄存器异常修改 使用更复杂的校验机制 监控SetThreadContext等API调用 其他应用场景 : 反调试绕过 代码流劫持 敏感API监控 进阶技术 : 结合VEH(向量化异常处理) 多硬件断点协同工作 动态调整断点策略 通过硬件断点实现Hook绕过是一种高级技术,它充分利用了CPU的调试功能,避免了传统Hook方法的内存修改问题,在对抗强检测机制时具有显著优势。