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 Answer
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:
- Using thread's context to access Debug Registers
- 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