RoboDBG
Loading...
Searching...
No Matches
debugger.breakpoints.cpp
1#include "debugger.h"
2#include <vector>
3namespace RoboDBG {
4
5void Debugger::setBreakpoint(LPVOID address) {
6 BYTE original;
7 SIZE_T read;
8
9 if(this->verbose){
10 std::cout << "[*] Breakpoint set at " << std::hex << address << std::endl;
11 }
12
13 ReadProcessMemory(hProcessGlobal, address, &original, 1, &read);
14 if(original != 0xCC) {
15 BYTE int3 = 0xCC;
16 breakpoints[address] = original; //TODO
17 WriteProcessMemory(hProcessGlobal, address, &int3, 1, nullptr);
18 }
19 FlushInstructionCache(hProcessGlobal, address, 1);
20}
21
23 HANDLE hThread = bp.hThread;
24
25 // Validate length
26
27 // Execute breakpoints must be 1 byte
29 std::cerr << "[-] Execute breakpoints must be 1 byte (len=0)\n";
30 return false;
31 }
32
33 CONTEXT ctx = {};
34 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
35 if (!GetThreadContext(hThread, &ctx)) {
36 std::cerr << "[-] GetThreadContext failed: " << GetLastError() << "\n";
37 return false;
38 }
39
40 // Assign address to correct DRx
41 switch (bp.reg) {
42 case DRReg::DR0: ctx.Dr0 = reinterpret_cast<DWORD_PTR>(bp.address); break;
43 case DRReg::DR1: ctx.Dr1 = reinterpret_cast<DWORD_PTR>(bp.address); break;
44 case DRReg::DR2: ctx.Dr2 = reinterpret_cast<DWORD_PTR>(bp.address); break;
45 case DRReg::DR3: ctx.Dr3 = reinterpret_cast<DWORD_PTR>(bp.address); break;
46 default:
47 std::cerr << "[-] Invalid debug register index: " << static_cast<int>(bp.reg) << "\n";
48 return false;
49 }
50
51 // Enable local breakpoint (L0–L3)
52 ctx.Dr7 |= (1 << ( static_cast<int>(bp.reg) * 2));
53
54 // Set type and size in DR7
55 // Bits 16-31 control type and length for DR0-DR3
56 // Each DR uses 4 bits: [LEN1][LEN0][R/W1][R/W0]
57 const int shift = 16 + static_cast<int>(bp.reg) * 4;
58 ctx.Dr7 &= ~(0xF << shift); // Clear previous settings
59 ctx.Dr7 |= ((static_cast<int>(bp.type) | ( static_cast<int>(bp.len) << 2)) << shift); // Set new type and length
60
61 if (SuspendThread(hThread) == (DWORD)-1) {
62 std::cerr << "[-] SuspendThread failed: " << GetLastError() << "\n";
63 return false;
64 }
65
66 if (!SetThreadContext(hThread, &ctx)) {
67 std::cerr << "[-] SetThreadContext failed: " << GetLastError() << "\n";
68 ResumeThread(hThread);
69 return false;
70 }
71
72 if (ResumeThread(hThread) == (DWORD)-1) {
73 std::cerr << "[-] ResumeThread failed: " << GetLastError() << "\n";
74 return false;
75 }
76
77 // Save to global tracker
78 hwBreakpoints[bp.address] = bp;
79 return true;
80}
81
83 bool allSucceeded = true;
84
86 // Validate register index
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";
89 return false;
90 }
91
92 const char* typeStr;
93 switch (bp.type) {
94 case AccessType::EXECUTE : typeStr = "execute"; break;
95 case AccessType::WRITE : typeStr = "write"; break;
96 case AccessType::READWRITE: typeStr = "read/write"; break;
97 default: typeStr = "unknown"; break;
98 }
99
100 const char* lenStr;
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;
107 }
109 for (const auto& thread : threads) {
110 hwBp_t perThreadBp = bp;
111 perThreadBp.hThread = thread.hThread;
112
113 if (!setHardwareBreakpointOnThread(perThreadBp)) {
114 std::cerr << "[-] Failed to set hardware breakpoint on TID=" << thread.threadId << "\n";
115 allSucceeded = false;
116 } else {
117 //std::cout << "[+] Hardware breakpoint set for TID=" << thread.threadId
118 //<< " at address 0x" << std::hex << (DWORD_PTR)bp.address
119 //<< " using DR" << perThreadBp.reg
120 //<< " (" << typeStr << ", " << lenStr << ")\n";
121 }
122 }
123 return allSucceeded;
124}
125
126// Helper function to clear a hardware breakpoint
128 if (static_cast<int>(reg) < 0 || static_cast<int>(reg) > 3) {
129 std::cerr << "[-] Invalid debug register DR" << static_cast<int>(reg) << "\n";
130 return false;
131 }
132
133 CONTEXT ctx = {};
134 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
135
136 if (!GetThreadContext(hThread, &ctx)) {
137 std::cerr << "[-] GetThreadContext failed: " << GetLastError() << "\n";
138 return false;
139 }
140
141 LPVOID oldAddr = nullptr;
142 switch (reg) {
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;
147 }
148
149 ctx.Dr7 &= ~(1 << (static_cast<int>(reg) * 2)); // Disable local enable
150 ctx.Dr7 &= ~(0xF << (16 + static_cast<int>(reg) * 4)); // Clear type/length
151
152 if (SuspendThread(hThread) == (DWORD)-1) {
153 std::cerr << "[-] SuspendThread failed: " << GetLastError() << "\n";
154 return false;
155 }
156
157 bool success = true;
158
159 if (!SetThreadContext(hThread, &ctx)) {
160 std::cerr << "[-] SetThreadContext failed: " << GetLastError() << "\n";
161 success = false;
162 }
163
164 if (ResumeThread(hThread) == (DWORD)-1) {
165 std::cerr << "[-] ResumeThread failed: " << GetLastError() << "\n";
166 success = false;
167 }
168
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);
173 }
174
175 return success;
176}
177
179 bool allSucceeded = true;
180
182 for (const auto& thread : threads) {
183 if (!clearHardwareBreakpointOnThread(thread.hThread, reg))
184 allSucceeded = false;
185 }
186
187 return allSucceeded;
188}
189
190std::vector<hwBp_t> Debugger::getHardwareBreakpoints() {
191 std::vector<hwBp_t> result;
192
193 for (const auto& thread : threads) {
194 CONTEXT ctx = {};
195 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
196
197 if (!GetThreadContext(thread.hThread, &ctx)) continue;
198
199 for (int i = 0; i < 4; ++i) {
200 bool enabled = ctx.Dr7 & (1 << (i * 2));
201 if (!enabled) continue;
202
203 LPVOID addr = nullptr;
204 switch (i) {
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;
209 }
210
211 int type = (ctx.Dr7 >> (16 + i * 4)) & 0b11;
212 int len = (ctx.Dr7 >> (18 + i * 4)) & 0b11;
213
214 hwBp_t bp = {
215 thread.hThread,
216 addr,
217 static_cast<DRReg>(i),
218 static_cast<AccessType>(type),
219 static_cast<BreakpointLength>(len)
220 };
221 result.push_back(bp);
222 }
223 }
224
225 return result;
226}
227
229 for (const auto& thread : threads) {
230 CONTEXT ctx = {};
231 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
232
233 if (!GetThreadContext(thread.hThread, &ctx)) continue;
234
235 bool enabled = ctx.Dr7 & (1 << (static_cast<int>(reg) * 2));
236 if (!enabled) continue;
237
238 LPVOID addr = nullptr;
239 switch (reg) {
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;
244 default: return {}; // Invalid register index
245 }
246
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;
250
251 hwBp_t bp = {
252 thread.hThread,
253 addr,
254 reg,
255 static_cast<AccessType>(type),
256 static_cast<BreakpointLength>(len)
257 };
258 return bp;
259 }
260
261 return {}; // Not found
262}
263
265 for (const auto& thread : threads) {
266 CONTEXT ctx = {};
267 ctx.ContextFlags = CONTEXT_DEBUG_REGISTERS;
268
269 if (!GetThreadContext(thread.hThread, &ctx)) continue;
270
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;
275 }
276
277 return DRReg::NOP;
278}
279
280
281void Debugger::restoreBreakpoint(LPVOID address) {
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);
287 } else {
288 printf("WARNING: BREAKPOINT NOT FOUND!\n");
289 }
290}
291
292}
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.
Definition debugger.cpp:193
Main Debugger file.
AccessType
Specifies the type of memory access that triggers a hardware breakpoint.
Definition debugger.h:47
@ READWRITE
Trigger when reading from or writing to the address.
Definition debugger.h:50
@ EXECUTE
Trigger when executing instructions at the address.
Definition debugger.h:48
@ WRITE
Trigger when writing to the address.
Definition debugger.h:49
BreakpointLength
Length of the hardware breakpoint watch.
Definition debugger.h:69
DRReg
Hardware debug registers used for breakpoints.
Definition debugger.h:57
@ NOP
No register assigned.
Definition debugger.h:58
@ DR3
Debug register 3.
Definition debugger.h:62
@ DR2
Debug register 2.
Definition debugger.h:61
@ DR1
Debug register 1.
Definition debugger.h:60
@ DR0
Debug register 0.
Definition debugger.h:59
Hardware breakpoint configuration.
Definition debugger.h:91
BreakpointLength len
Length of the watch region.
Definition debugger.h:96
LPVOID address
Address to watch.
Definition debugger.h:93
AccessType type
Type of access that triggers the breakpoint.
Definition debugger.h:95
DRReg reg
Debug register used.
Definition debugger.h:94
HANDLE hThread
Target thread handle.
Definition debugger.h:92