Since software breakpoints, unlike hardware breakpoints , do change the code, it's relatively easy to write a program that performs a checksum on itself as an anti-debugger technique. Is it possible to do something similar with hardware breakpoints?

  • 1
    yes you can get the context and check /modify/ nullify the debug registers dr7 dr6 etc – blabb Oct 14 '17 at 8:28
up vote 3 down vote accepted

This is a really good question since this topic isn't as popular as anti-debugging techniques to detect software breakpoints. Since you didn't mention the architecture we have to keep in mind that Hardware Breakpoints, as its name hints, are depends on the hardware you're running on and thus the implementation of such breakpoints is differ between each architecture. Since we can't cover in this answer all the architectures, I'll write here in an assumption that we're talking about Intel's x86 architecture on Windows.

In short, the answer is yes. There are basically two common ways to detect hardware breakpoints:

  1. Using thread's context to access Debug Registers
  2. Crafting a SEH (Structured Exception Handling), then to cause an exception and access the debug registers

In order to understand each method we should understand first what Hardware Breakpoint is and (in short) how it works.

Hardware Breakpoint

In x86 architecture the debugger uses a set of Debug Registers in order to apply hardware breakpoints. There are 8 debug registers exists to control the debugging procedure, ranging from DR0 to DR7. These registers are not accessible from ring3 privileges but only accessible from CPL0 (Current Privilege Levels, ring0). Thus, an attempt to read or write the debug registers when executing at any other privilege level causes a general protection fault. The debug registers allow the debugger to interrupt program execution and transfer the control to it when accessing memory to read or write.

x86 Debug Registers

  • DR0 - Linear breakpoint address 0
  • DR1 - Linear breakpoint address 1
  • DR2 - Linear breakpoint address 2
  • DR3 - Linear breakpoint address 3

  • DR4 - Reserved. Not defined by Intel

  • DR5 - Reserved. Not defined by Intel

  • DR6 - Breakpoint Status

  • DR7 - Breakpoint control

DR0-DR3 store a linear address of a breakpoint. The stored address can be the same as the physical address or it needs to be translated to the physical address. DR6 indicates which breakpoint is activated. DR7 defines the breakpoint activation mode by the access modes: read, write, or execute.

Detecting Hardware Breakpoints

Method one - ThreadContext Win API

The following example is based on an example from this article from CodeProject. The example is commented to describe each piece of code:

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;
    }
} 

Method 2 - SEH
The SEH method of manipulating the debug registers is much more common and is easier to implement it in Assembly, as shown in the following example, again from 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

References:

  • did you mean "are not accessible from ring3"? – Igor Skochinsky Oct 14 '17 at 18:55
  • Indeed, thanks for pointing out the typo. Fixed :) – Megabeets Oct 14 '17 at 18:56

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.