检测硬件断点

逆向工程 反调试 断点
2021-06-15 10:19:14

由于软件断点硬件断点不同,它确实会更改代码,因此编写一个对自身执行校验和的程序作为反调试器技术相对容易。是否可以对硬件断点做类似的事情?

1个回答

这是一个非常好的问题,因为该主题不像检测软件断点的反调试技术那样受欢迎。由于您没有提到架构,我们必须牢记硬件断点,正如其名称所暗示的那样,取决于您正在运行的硬件,因此此类断点的实现在每个架构之间是不同的。由于我们无法在此答案中涵盖所有架构,因此我将假设我们正在讨论Windows 上的英特尔 x86 架构

简而言之,答案是肯定的检测硬件断点基本上有两种常用的方法:

  1. 使用线程的上下文访问调试寄存器
  2. 制作一个SEH(结构化异常处理),然后引发异常并访问调试寄存器

为了理解每种方法,我们应该首先了解硬件断点是什么以及(简而言之)它是如何工作的。

硬件断点

x86架构中,调试器使用一组调试寄存器来应用硬件断点。存在 8 个调试寄存器来控制调试过程,范围从DR0DR7这些寄存器不能从ring3 权限访问,而只能从 CPL0(当前权限级别,ring0)访问。因此,在任何其他特权级别执行时尝试读取或写入调试寄存器会导致一般保护错误。调试寄存器允许调试器在访问存储器进行读或写时中断程序执行并将控制权转移给它。

x86 调试寄存器

  • DR0 - 线性断点地址 0
  • DR1 - 线性断点地址 1
  • DR2 - 线性断点地址 2
  • DR3 - 线性断点地址 3

  • DR4 - 保留。英特尔未定义

  • DR5 - 保留。英特尔未定义

  • DR6 - 断点状态

  • DR7 - 断点控制

DR0-DR3 存储断点的线性地址。存储的地址可以与物理地址相同,也可以转换为物理地址。DR6指示哪个断点被激活。DR7通过访问模式定义断点激活模式:readwriteexecute

检测硬件断点

方法一——ThreadContext Win API

下面的例子是基于一个例子这种文章从CodeProject。该示例被注释以描述每段代码:

bool IsHWBreakpointExists()
{
    // This structure is key to the function and is the 
    CONTEXT ctx;
    ZeroMemory(&ctx, sizeof(CONTEXT));

    // The CONTEXT structure is an in/out parameter therefore we have
    // to set the flags so Get/SetThreadContext knows what to set or get.   
    ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;

    // Get a handle to our thread
    HANDLE hThread = GetCurrentThread();
    // Get the registers
    if(GetThreadContext(hThread, &ctx) == 0)
        return false;   

    if ((ctx.Dr0) || (ctx.Dr1) || (ctx.Dr2) || (ctx.Dr3)) {
        return true;
    }
    else {
        return false;
    }
} 

方法 2 - SEH
操作调试寄存器的 SEH 方法更常见,并且更容易在汇编中实现,如下面的示例所示,同样来自 CodeProject:

ClrHwBpHandler proto
 .safeseh ClrHwBpHandler

ClearHardwareBreakpoints proc
     assume fs:nothing
     push offset ClrHwBpHandler
    push fs:[0]
    mov dword ptr fs:[0], esp ; Setup SEH
     xor eax, eax
     div eax ; Cause an exception
     pop dword ptr fs:[0] ; Execution continues here
     add esp, 4
     ret
ClearHardwareBreakpoints endp

ClrHwBpHandler proc 
     xor eax, eax
    mov ecx, [esp + 0ch] ; This is a CONTEXT structure on the stack
     mov dword ptr [ecx + 04h], eax ; Dr0
     mov dword ptr [ecx + 08h], eax ; Dr1
     mov dword ptr [ecx + 0ch], eax ; Dr2
     mov dword ptr [ecx + 10h], eax ; Dr3
     mov dword ptr [ecx + 14h], eax ; Dr6
     mov dword ptr [ecx + 18h], eax ; Dr7
     add dword ptr [ecx + 0b8h], 2 ; We add 2 to EIP to skip the div eax
     ret
ClrHwBpHandler endp

参考: