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:
- 00b - Triggers when code is executed
- 01b - Triggers when data is written
- 10b - Reserved
- 11b - Triggers when data is read or written
|
24-31 | 2 bits for each register, defining the size of the breakpoint:
- 00b - 1 byte
- 01b - 2 bytes
- 10b - 8 bytes
- 11b - 4 bytes
|
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];
}
__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.