10 std::cout <<
"[*] Breakpoint set at " << std::hex << address << std::endl;
13 ReadProcessMemory(hProcessGlobal, address, &original, 1, &read);
14 if(original != 0xCC) {
16 breakpoints[address] = original;
17 WriteProcessMemory(hProcessGlobal, address, &int3, 1,
nullptr);
19 FlushInstructionCache(hProcessGlobal, address, 1);
29 std::cerr <<
"[-] Execute breakpoints must be 1 byte (len=0)\n";
34 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
35 if (!GetThreadContext(hThread, &ctx)) {
36 std::cerr <<
"[-] GetThreadContext failed: " << GetLastError() <<
"\n";
47 std::cerr <<
"[-] Invalid debug register index: " <<
static_cast<int>(bp.
reg) <<
"\n";
52 ctx.Dr7 |= (1 << (
static_cast<int>(bp.
reg) * 2));
57 const int shift = 16 +
static_cast<int>(bp.
reg) * 4;
58 ctx.Dr7 &= ~(0xF << shift);
59 ctx.Dr7 |= ((
static_cast<int>(bp.
type) | (
static_cast<int>(bp.
len) << 2)) << shift);
61 if (SuspendThread(hThread) == (
DWORD)-1) {
62 std::cerr <<
"[-] SuspendThread failed: " << GetLastError() <<
"\n";
66 if (!SetThreadContext(hThread, &ctx)) {
67 std::cerr <<
"[-] SetThreadContext failed: " << GetLastError() <<
"\n";
68 ResumeThread(hThread);
72 if (ResumeThread(hThread) == (
DWORD)-1) {
73 std::cerr <<
"[-] ResumeThread failed: " << GetLastError() <<
"\n";
83 bool allSucceeded =
true;
87 if (
static_cast<int>(bp.
reg) < 0 ||
static_cast<int>(bp.
reg) > 3) {
88 std::cerr <<
"[-] Invalid debug register DR" <<
static_cast<int>(bp.
reg) <<
"\n";
97 default: typeStr =
"unknown";
break;
101 switch (
static_cast<int>(bp.
len)) {
102 case 0: lenStr =
"1 byte";
break;
103 case 1: lenStr =
"2 bytes";
break;
104 case 2: lenStr =
"8 bytes";
break;
105 case 3: lenStr =
"4 bytes";
break;
106 default: lenStr =
"unknown";
break;
109 for (
const auto& thread : threads) {
111 perThreadBp.
hThread = thread.hThread;
114 std::cerr <<
"[-] Failed to set hardware breakpoint on TID=" << thread.threadId <<
"\n";
115 allSucceeded =
false;
128 if (
static_cast<int>(reg) < 0 ||
static_cast<int>(reg) > 3) {
129 std::cerr <<
"[-] Invalid debug register DR" <<
static_cast<int>(reg) <<
"\n";
134 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
136 if (!GetThreadContext(hThread, &ctx)) {
137 std::cerr <<
"[-] GetThreadContext failed: " << GetLastError() <<
"\n";
141 LPVOID oldAddr =
nullptr;
143 case DRReg::DR0: oldAddr = (LPVOID)ctx.Dr0; ctx.Dr0 = 0;
break;
144 case DRReg::DR1: oldAddr = (LPVOID)ctx.Dr1; ctx.Dr1 = 0;
break;
145 case DRReg::DR2: oldAddr = (LPVOID)ctx.Dr2; ctx.Dr2 = 0;
break;
146 case DRReg::DR3: oldAddr = (LPVOID)ctx.Dr3; ctx.Dr3 = 0;
break;
149 ctx.Dr7 &= ~(1 << (
static_cast<int>(reg) * 2));
150 ctx.Dr7 &= ~(0xF << (16 +
static_cast<int>(reg) * 4));
152 if (SuspendThread(hThread) == (
DWORD)-1) {
153 std::cerr <<
"[-] SuspendThread failed: " << GetLastError() <<
"\n";
159 if (!SetThreadContext(hThread, &ctx)) {
160 std::cerr <<
"[-] SetThreadContext failed: " << GetLastError() <<
"\n";
164 if (ResumeThread(hThread) == (
DWORD)-1) {
165 std::cerr <<
"[-] ResumeThread failed: " << GetLastError() <<
"\n";
169 if (oldAddr && hwBreakpoints.count(oldAddr)) {
170 const hwBp_t& stored = hwBreakpoints[oldAddr];
171 if (stored.
hThread == hThread && stored.
reg == reg)
172 hwBreakpoints.erase(oldAddr);
179 bool allSucceeded =
true;
182 for (
const auto& thread : threads) {
184 allSucceeded =
false;
191 std::vector<hwBp_t> result;
193 for (
const auto& thread : threads) {
195 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
197 if (!GetThreadContext(thread.hThread, &ctx))
continue;
199 for (
int i = 0; i < 4; ++i) {
200 bool enabled = ctx.Dr7 & (1 << (i * 2));
201 if (!enabled)
continue;
203 LPVOID addr =
nullptr;
205 case 0: addr = (LPVOID)ctx.Dr0;
break;
206 case 1: addr = (LPVOID)ctx.Dr1;
break;
207 case 2: addr = (LPVOID)ctx.Dr2;
break;
208 case 3: addr = (LPVOID)ctx.Dr3;
break;
211 int type = (ctx.Dr7 >> (16 + i * 4)) & 0b11;
212 int len = (ctx.Dr7 >> (18 + i * 4)) & 0b11;
217 static_cast<DRReg>(i),
221 result.push_back(bp);
229 for (
const auto& thread : threads) {
231 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
233 if (!GetThreadContext(thread.hThread, &ctx))
continue;
235 bool enabled = ctx.Dr7 & (1 << (
static_cast<int>(reg) * 2));
236 if (!enabled)
continue;
238 LPVOID addr =
nullptr;
240 case DRReg::DR0: addr = (LPVOID)ctx.Dr0;
break;
241 case DRReg::DR1: addr = (LPVOID)ctx.Dr1;
break;
242 case DRReg::DR2: addr = (LPVOID)ctx.Dr2;
break;
243 case DRReg::DR3: addr = (LPVOID)ctx.Dr3;
break;
247 int type = (ctx.Dr7 >> (16 +
static_cast<int>(reg) * 4)) & 0b11;
248 int len = (ctx.Dr7 >> (18 +
static_cast<int>(reg) * 4)) & 0b11;
249 if(len<0 || len > 3) len = 0;
265 for (
const auto& thread : threads) {
267 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
269 if (!GetThreadContext(thread.hThread, &ctx))
continue;
271 if ((ctx.Dr7 & 0x1 ) && ctx.Dr0 == (DWORD_PTR)address)
return DRReg::DR0;
272 if ((ctx.Dr7 & 0x4 ) && ctx.Dr1 == (DWORD_PTR)address)
return DRReg::DR1;
273 if ((ctx.Dr7 & 0x10) && ctx.Dr2 == (DWORD_PTR)address)
return DRReg::DR2;
274 if ((ctx.Dr7 & 0x40) && ctx.Dr3 == (DWORD_PTR)address)
return DRReg::DR3;
282 auto it = breakpoints.find(address);
283 if (it != breakpoints.end()) {
284 if(this->verbose) std::cout <<
"[~] Replacing breakpoint " << std::hex << static_cast<int>(it->second) <<
" at " << std::hex << address << std::endl;
285 WriteProcessMemory(hProcessGlobal, address, &it->second, 1,
nullptr);
286 FlushInstructionCache(hProcessGlobal, address, 1);
288 printf(
"WARNING: BREAKPOINT NOT FOUND!\n");
DRReg isHardwareBreakpointAt(LPVOID address)
Checks if a hardware breakpoint exists at an address.
bool clearHardwareBreakpoint(DRReg reg)
Clears a DRx slot across threads.
std::vector< hwBp_t > getHardwareBreakpoints()
Enumerates current hardware breakpoints.
bool setHardwareBreakpointOnThread(hwBp_t bp)
Sets a hardware breakpoint for a specific thread.
void setBreakpoint(LPVOID address)
Sets a software INT3 breakpoint at an address.
bool clearHardwareBreakpointOnThread(HANDLE hThread, DRReg reg)
Clears a DRx slot on a single thread.
hwBp_t getBreakpointByReg(DRReg reg)
Gets the breakpoint definition bound to a DRx register.
void restoreBreakpoint(LPVOID address)
Restores the original byte at a software breakpoint address.
bool setHardwareBreakpoint(hwBp_t bp)
Sets a hardware breakpoint for all existing (and future) threads where applicable.
void actualizeThreadList()
Refreshes the internal thread list by querying the target process.
AccessType
Specifies the type of memory access that triggers a hardware breakpoint.
@ READWRITE
Trigger when reading from or writing to the address.
@ EXECUTE
Trigger when executing instructions at the address.
@ WRITE
Trigger when writing to the address.
BreakpointLength
Length of the hardware breakpoint watch.
DRReg
Hardware debug registers used for breakpoints.
@ NOP
No register assigned.
Hardware breakpoint configuration.
BreakpointLength len
Length of the watch region.
LPVOID address
Address to watch.
AccessType type
Type of access that triggers the breakpoint.
DRReg reg
Debug register used.
HANDLE hThread
Target thread handle.