Toggle hardware data/read/execute breakpoints programmatically






4.97/5 (38 votes)
Simple code to introduce a hardware breakpoint mechanism.
Introduction
I decided to write this article about hardware breakpoints for the following reasons:
- Visual C++ only supports write-only data breakpoints. You might want to trigger a break when data is read as well.
- You might not be using Visual C++, so chances are that your debugger uses some slow software-based mechanism.
- You might want to set/remove a breakpoint programmatically.
- You may be interested in low level CPU stuff!
Features
- Works for x86 and x64.
- Supports upto 4 hardware breakpoints per thread.
Debug Registers
x86/x64 contains a set of debug registers, named DR0, DR1, DR2, DR3, DR6, and DR7. These registers are 32-bit when in 32-bit mode, and 64-bit when in long mode. DR0, DR1, DR2, and DR3 contain the linear addresses of the breakpoint, and DR7 contains the bits explained here:
Bits | Meaning |
0-7 | Flags for each of the 4 debug registers (2 for each). The first flag is set to specify a local breakpoint (so the CPU resets the flag when switching tasks), and the second flag is set to specify a global breakpoint. In Windows, obviously, you can only use the first flag (although I haven't tried the second). |
16-23 |
2 bits for each register, defining when the breakpoint will be triggered:
|
24-31 |
2 bits for each register, defining the size of the breakpoint:
|
We use SetThreadContext
to set the necessary flags for the thread. After that, when the breakpoint is triggered, an exception of the value EXCEPTION_SINGLE_STEP
is raised.
Setting the Breakpoint
HANDLE SetHardwareBreakpoint(HANDLE hThread,HWBRK_TYPE Type,HWBRK_SIZE Size,void* s);
hThread
- Handle to the thread for which the breakpoint is to be set.Type
- Type of the breakpoint:HWBRK_TYPE_CODE
HWBRK_TYPE_READWRITE
HWBRK_TYPE_WRITE
Size
- Size of the breakpoint:HWBRK_SIZE_1
HWBRK_SIZE_2
HWBRK_SIZE_4
HWBRK_SIZE_8
addr
- The address of the breakpoint.
The function returns a handle to the breakpoint, to be used later in RemoveHardwareBreakpoint
. It can return 0 if:
- You do not have access to the thread.
- You have set the maximum number of breakpoints for that thread (4).
Removing the Breakpoint
bool RemoveHardwareBreakpoint(HANDLE hBrk);
Removes the breakpoint, returning true on success.
Sample
int __stdcall WinMain(HINSTANCE,HINSTANCE,LPSTR,int) { char c1[100] = {0}; lstrcpyA(c1,"Hello 1"); HANDLE hX1 = 0; hX1 = SetHardwareBreakpoint(GetCurrentThread(), HWBRK_TYPE_READWRITE,HWBRK_SIZE_4,c1); __try { volatile char a1 = c1[2]; // To ensure that it won't be optimized out. } __except(GetExceptionCode() == STATUS_SINGLE_STEP) { MessageBoxA(0,"Breakpoint hit!",0,MB_OK); } RemoveHardwareBreakpoint(hX1); return 0; }
I wait for your comments and questions!
History
- July 24, 2008 - First post.