11 this->verbose = verbose;
15 PROCESS_BASIC_INFORMATION pbi = {};
18 NTSTATUS status = NtQueryInformationProcess(
20 ProcessBasicInformation,
26 std::cerr <<
"[-] NtQueryInformationProcess failed\n";
31 BYTE beingDebugged = 0;
32 SIZE_T bytesWritten = 0;
35 LPVOID pebDebugFlagAddr = (LPBYTE)pbi.PebBaseAddress + 2;
38 if (!WriteProcessMemory(hProcessGlobal, pebDebugFlagAddr, &beingDebugged,
sizeof(beingDebugged), &bytesWritten)) {
39 std::cerr <<
"[-] WriteProcessMemory failed: " << GetLastError() <<
"\n";
58 std::cerr <<
"Process not found: " << exeName << std::endl;
61 std::cout <<
"Attach to " << pid << std::endl;
62 if (!DebugActiveProcess(pid)) {
63 std::cerr <<
"Failed to attach to process: " << GetLastError() << std::endl;
67 hProcessGlobal = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
68 if (hProcessGlobal == NULL) {
69 std::cerr <<
"Failed to open process: " << GetLastError() << std::endl;
79 if (!DebugActiveProcessStop(debuggedPid)) {
80 std::cerr <<
"Failed to detach debugger. Error: " << GetLastError() << std::endl;
88 STARTUPINFO si = {
sizeof(si) };
89 PROCESS_INFORMATION pi;
93 const_cast<LPSTR
>(exeName.c_str()),
97 DEBUG_ONLY_THIS_PROCESS,
102 std::cerr <<
"CreateProcess failed: " << GetLastError() << std::endl;
106 hProcessGlobal = pi.hProcess;
107 hThreadGlobal = pi.hThread;
112 STARTUPINFO si = {
sizeof(si) };
113 PROCESS_INFORMATION pi{};
116 std::string cmdLine =
"\"" + exeName +
"\"";
117 for (
const auto& arg : args) {
118 cmdLine +=
" " + arg;
122 std::vector<char> cmdLineMutable(cmdLine.begin(), cmdLine.end());
123 cmdLineMutable.push_back(
'\0');
127 cmdLineMutable.data(),
131 DEBUG_ONLY_THIS_PROCESS,
137 std::cerr <<
"CreateProcess failed: " << GetLastError() << std::endl;
141 hProcessGlobal = pi.hProcess;
142 hThreadGlobal = pi.hThread;
150 ctx.ContextFlags = CONTEXT_CONTROL;
152 if (GetThreadContext(hThread, &ctx)) {
154 SetThreadContext(hThread, &ctx);
159 if (SuspendThread(hThread) == (
DWORD)-1) {
160 std::cerr <<
"[-] SuspendThread failed: " << GetLastError() <<
"\n";
163 ctx.ContextFlags = CONTEXT_CONTROL;
164 if (GetThreadContext(hThread, &ctx)) {
170 SetThreadContext(hThread, &ctx);
173 if (ResumeThread(hThread) == (
DWORD)-1) {
174 std::cerr <<
"[-] ResumeThread failed: " << GetLastError() <<
"\n";
182 ctx.ContextFlags = CONTEXT_CONTROL;
183 if (GetThreadContext(hThread, &ctx)) {
185 std::cout <<
"[!] RIP = 0x" << std::hex << ctx.Rip << std::endl;
187 std::cout <<
"[!] EIP = 0x" << std::hex << ctx.Eip << std::endl;
196 DWORD processId = GetProcessId(hProcessGlobal);
197 if (processId == 0) {
198 std::cerr <<
"Failed to get process ID from handle. Error: " << GetLastError() << std::endl;
202 HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
203 if (snapshot == INVALID_HANDLE_VALUE) {
204 std::cerr <<
"Failed to create thread snapshot. Error: " << GetLastError() << std::endl;
208 THREADENTRY32 te32 = {};
209 te32.dwSize =
sizeof(te32);
211 if (Thread32First(snapshot, &te32)) {
213 if (te32.th32OwnerProcessID == processId) {
214 HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID);
216 std::cerr <<
"Failed to open thread " << te32.th32ThreadID <<
". Error: " << GetLastError() << std::endl;
228 threads.push_back(t);
230 }
while (Thread32Next(snapshot, &te32));
233 CloseHandle(snapshot);
237 bool stepping =
false;
239 DEBUG_EVENT dbgEvent;
241 if (!WaitForDebugEvent(&dbgEvent, INFINITE))
break;
243 DWORD cont = DBG_CONTINUE;
245 switch (dbgEvent.dwDebugEventCode) {
246 case CREATE_PROCESS_DEBUG_EVENT: {
247 baseImageBase =
reinterpret_cast<uintptr_t
>(dbgEvent.u.CreateProcessInfo.lpBaseOfImage);
253 case EXIT_PROCESS_DEBUG_EVENT: {
254 DWORD exitCode = dbgEvent.u.ExitProcess.dwExitCode;
255 DWORD pid = dbgEvent.dwProcessId;
256 onEnd(exitCode, pid);
259 case CREATE_THREAD_DEBUG_EVENT: {
264 LPVOID threadBase = dbgEvent.u.CreateThread.lpThreadLocalBase;
265 LPVOID threadStartAddr = dbgEvent.u.CreateThread.lpStartAddress;
267 HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, dbgEvent.dwThreadId);
269 std::cerr <<
"[-] Failed to open thread: " << GetLastError() <<
"\n";
272 onThreadCreate( hThread, dbgEvent.dwThreadId,
reinterpret_cast<uintptr_t
>(threadBase),
reinterpret_cast<uintptr_t
>(threadStartAddr));
277 .threadId = dbgEvent.dwThreadId,
278 .threadBase = threadBase,
279 .startAddress = threadStartAddr
281 threads.push_back(newThread);
286 case EXIT_THREAD_DEBUG_EVENT: {
292 case LOAD_DLL_DEBUG_EVENT: {
293 LPVOID base = dbgEvent.u.LoadDll.lpBaseOfDll;
294 auto name =
Util::getDllName(hProcessGlobal, dbgEvent.u.LoadDll.lpImageName, dbgEvent.u.LoadDll.fUnicode);
296 onDLLLoad(
reinterpret_cast<uintptr_t
>(base), name,
static_cast<uintptr_t
>(entryPoint));
303 case UNLOAD_DLL_DEBUG_EVENT: {
304 LPVOID base = dbgEvent.u.UnloadDll.lpBaseOfDll;
305 auto name =
Util::getDllName(hProcessGlobal, dbgEvent.u.LoadDll.lpImageName, dbgEvent.u.LoadDll.fUnicode);
306 onDLLUnload(
reinterpret_cast<uintptr_t
>(base), name);
311 case EXCEPTION_DEBUG_EVENT: {
312 DWORD code = dbgEvent.u.Exception.ExceptionRecord.ExceptionCode;
313 LPVOID addr = dbgEvent.u.Exception.ExceptionRecord.ExceptionAddress;
314 HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, dbgEvent.dwThreadId);
317 if (code == EXCEPTION_BREAKPOINT) {
318 if(this->verbose) std::cout <<
"[~] Breakpoint Hit" << std::endl;
319 auto it = breakpoints.find(addr);
320 if (it != breakpoints.end()) {
325 if (bpType !=
BREAK ) {
330 bpAddrToRestore = addr;
332 needToRestoreBreakpoint =
true;
338 }
else if (code == EXCEPTION_SINGLE_STEP && stepping) {
339 if(needToRestoreBreakpoint)
343 if(this->verbose) std::cout <<
"[~] Restoring hardware breakpoint" << std::endl;
349 needToRestoreBreakpoint =
false;
355 if (bpType !=
BREAK) {
356 bpAddrToRestore = addr;
357 needToRestoreBreakpoint =
true;
366 }
else if (code == EXCEPTION_SINGLE_STEP ) {
367 if(this->verbose) std::cout <<
"[~] Single step: 0x" << std::hex << (DWORD_PTR)addr <<
"\n";
371 if(bpType ==
BREAK) {
381 needToRestoreBreakpoint =
true;
384 bpAddrToRestore = addr;
385 hwBPThreadToRestore = hThread;
397 }
else if ( code == EXCEPTION_ACCESS_VIOLATION ) {
398 ULONG_PTR rawAddr = dbgEvent.u.Exception.ExceptionRecord.ExceptionInformation[1];
399 LPVOID faultingAddress =
reinterpret_cast<LPVOID
>(rawAddr);
400 ULONG_PTR accessType = dbgEvent.u.Exception.ExceptionRecord.ExceptionInformation[0];
403 onAccessViolation(
static_cast<uintptr_t
>(rawAddr),
reinterpret_cast<uintptr_t
>(faultingAddress),
static_cast<long>(accessType));
411 CloseHandle(hThread);
415 case OUTPUT_DEBUG_STRING_EVENT: {
416 OUTPUT_DEBUG_STRING_INFO& info = dbgEvent.u.DebugString;
419 if (!info.fUnicode) {
421 char buffer[1024] = {0};
424 info.lpDebugStringData,
426 std::min<DWORD>(info.nDebugStringLength,
sizeof(buffer) - 1),
430 std::string msg(buffer);
434 wchar_t wbuffer[1024] = {0};
437 info.lpDebugStringData,
439 std::min<DWORD>(info.nDebugStringLength *
sizeof(
wchar_t),
sizeof(wbuffer) -
sizeof(
wchar_t)),
444 int size_needed = WideCharToMultiByte(CP_UTF8, 0, wbuffer, -1,
nullptr, 0,
nullptr,
nullptr);
445 std::string msg(size_needed, 0);
446 WideCharToMultiByte(CP_UTF8, 0, wbuffer, -1, &msg[0], size_needed,
nullptr,
nullptr);
455 const RIP_INFO& rip = dbgEvent.u.RipInfo;
465 ContinueDebugEvent(dbgEvent.dwProcessId, dbgEvent.dwThreadId, cont);
468 CloseHandle(hProcessGlobal);
469 CloseHandle(hThreadGlobal);
int start(std::string exeName)
Starts a process under debugging.
virtual void onAttach()
Called after successfully attaching to an already running process.
DRReg isHardwareBreakpointAt(LPVOID address)
Checks if a hardware breakpoint exists at an address.
void enableSingleStep(HANDLE hThread)
Enables trap flag (single-step) for a thread.
int loop()
Main debugger message loop.
int attach(std::string exeName)
Attaches to a running process by name.
bool hideDebugger()
Attempts to hide the debugger from basic anti-debug checks.
uintptr_t ASLR(LPVOID address)
Applies the module ASLR slide to an LPVOID.
bool clearHardwareBreakpoint(DRReg reg)
Clears a DRx slot across threads.
virtual void onAccessViolation(uintptr_t address, uintptr_t faultingAddress, long accessType)
Called on access violation (AV).
virtual void onThreadExit(DWORD threadID)
Called when a thread exits.
virtual void onEnd(DWORD exitCode, DWORD pid)
Called when the debuggee exits.
virtual void onDLLUnload(uintptr_t address, std::string dllName)
Called when a DLL is unloaded.
virtual void onUnknownException(uintptr_t addr, DWORD code)
Called on unknown exception.
virtual void onStart(uintptr_t imageBase, uintptr_t entryPoint)
Called when a new debuggee process is started.
virtual void onThreadCreate(HANDLE hThread, DWORD threadId, uintptr_t threadBase, uintptr_t startAddress)
Called when a thread is created in the debuggee.
int detach()
Detaches from the current debuggee.
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 decrementIP(HANDLE hThread)
Moves the instruction pointer one instruction backward (post-breakpoint fixup).
virtual void onRIPError(const RIP_INFO &rip)
Called on RIP error (native debug port issues).
virtual bool onDLLLoad(uintptr_t address, std::string dllName, uintptr_t entryPoint)
Called when a DLL is loaded.
void restoreBreakpoint(LPVOID address)
Restores the original byte at a software breakpoint address.
virtual BreakpointAction onBreakpoint(uintptr_t address, HANDLE hThread)
Called on software breakpoint (INT3).
virtual void onDebugString(std::string dbgString)
Called when OutputDebugString is emitted by the debuggee.
bool setHardwareBreakpoint(hwBp_t bp)
Sets a hardware breakpoint for all existing (and future) threads where applicable.
void printIP(HANDLE hThread)
Prints the current instruction pointer (IP/EIP/RIP) of a thread.
virtual BreakpointAction onHardwareBreakpoint(uintptr_t address, HANDLE hThread, DRReg reg)
Called on hardware breakpoint hit.
virtual void onUnknownDebugEvent(DWORD code)
Called on unhandled/unknown debug events.
Debugger()
Constructs a Debugger with default settings.
uintptr_t baseImageBase
Typical image base (with ASLR).
void actualizeThreadList()
Refreshes the internal thread list by querying the target process.
BreakpointAction
Specifies the action to take when a breakpoint is hit.
@ SINGLE_STEP
Perform a single-step execution after hitting the breakpoint.
@ RESTORE
Restore the original instruction at the breakpoint.
@ BREAK
Stop execution at the breakpoint.
DRReg
Hardware debug registers used for breakpoints.
@ NOP
No register assigned.
DWORD_PTR getEntryPoint(HANDLE hProcess, LPVOID baseAddress)
Gets the entry point address of a loaded module in a remote process.
DWORD findProcessId(const std::string &processName)
Finds the process ID of a process by name.
std::string getDllName(HANDLE hProcess, LPVOID lpImageName, BOOL isUnicode)
Retrieves the name of a DLL from a remote process.
Hardware breakpoint configuration.
Represents a thread in a debugged process.
LPVOID threadBase
Base address of the thread.
HANDLE hThread
Thread handle.
LPVOID startAddress
Start address of the thread.