diff --git a/GleeBug/Debugger.Loop.DebugString.cpp b/GleeBug/Debugger.Loop.DebugString.cpp new file mode 100644 index 0000000..54d83c1 --- /dev/null +++ b/GleeBug/Debugger.Loop.DebugString.cpp @@ -0,0 +1,13 @@ +#include "Debugger.h" + +namespace GleeBug +{ + void Debugger::debugStringEvent(const OUTPUT_DEBUG_STRING_INFO & debugString) + { + //prevent anti-debug trick (debug string events are actually exceptions) + _continueStatus = DBG_EXCEPTION_NOT_HANDLED; + + //call the debug event callback + cbDebugStringEvent(debugString); + } +}; \ No newline at end of file diff --git a/GleeBug/Debugger.Loop.Dll.cpp b/GleeBug/Debugger.Loop.Dll.cpp new file mode 100644 index 0000000..ee0ddcc --- /dev/null +++ b/GleeBug/Debugger.Loop.Dll.cpp @@ -0,0 +1,38 @@ +#include "Debugger.h" + +namespace GleeBug +{ + void Debugger::loadDllEvent(const LOAD_DLL_DEBUG_INFO & loadDll) + { + //DLL housekeeping + MODULEINFO modinfo; + memset(&modinfo, 0, sizeof(MODULEINFO)); + GetModuleInformation(_curProcess->hProcess, + (HMODULE)loadDll.lpBaseOfDll, + &modinfo, + sizeof(MODULEINFO)); + DllInfo dll(loadDll.lpBaseOfDll, modinfo.SizeOfImage, modinfo.EntryPoint); + _curProcess->dlls.insert({ Range(dll.lpBaseOfDll, dll.lpBaseOfDll + dll.sizeOfImage - 1), dll }); + + //call the debug event callback + cbLoadDllEvent(loadDll, dll); + + //close the file handle + CloseHandle(loadDll.hFile); + } + + void Debugger::unloadDllEvent(const UNLOAD_DLL_DEBUG_INFO & unloadDll) + { + //call the debug event callback + ULONG_PTR lpBaseOfDll = (ULONG_PTR)unloadDll.lpBaseOfDll; + auto dll = _curProcess->dlls.find(Range(lpBaseOfDll, lpBaseOfDll)); + if (dll != _curProcess->dlls.end()) + cbUnloadDllEvent(unloadDll, dll->second); + else + cbUnloadDllEvent(unloadDll, DllInfo(unloadDll.lpBaseOfDll, 0, 0)); + + //DLL housekeeping + if (dll != _curProcess->dlls.end()) + _curProcess->dlls.erase(dll); + } +}; \ No newline at end of file diff --git a/GleeBug/Debugger.Loop.Exception.cpp b/GleeBug/Debugger.Loop.Exception.cpp new file mode 100644 index 0000000..9be1187 --- /dev/null +++ b/GleeBug/Debugger.Loop.Exception.cpp @@ -0,0 +1,50 @@ +#include "Debugger.h" + +namespace GleeBug +{ + void Debugger::exceptionBreakpoint(const EXCEPTION_RECORD & exceptionRecord, const bool firstChance) + { + if (!_curProcess->systemBreakpoint) //handle system breakpoint + { + _curProcess->systemBreakpoint = true; + _continueStatus = DBG_CONTINUE; + + //call the callback + cbSystemBreakpoint(); + } + else //handle other breakpoint exceptions + { + } + } + + void Debugger::exceptionSingleStep(const EXCEPTION_RECORD & exceptionRecord, const bool firstChance) + { + } + + void Debugger::exceptionEvent(const EXCEPTION_DEBUG_INFO & exceptionInfo) + { + //let the debuggee handle exceptions per default + _continueStatus = DBG_EXCEPTION_NOT_HANDLED; + + const EXCEPTION_RECORD & exceptionRecord = exceptionInfo.ExceptionRecord; + const bool firstChance = exceptionInfo.dwFirstChance == 1; + + //dispatch the exception + switch (exceptionInfo.ExceptionRecord.ExceptionCode) + { + case STATUS_BREAKPOINT: + exceptionBreakpoint(exceptionRecord, firstChance); + break; + case STATUS_SINGLE_STEP: + exceptionSingleStep(exceptionRecord, firstChance); + break; + } + + //call the unhandled exception callback + if (_continueStatus == DBG_EXCEPTION_NOT_HANDLED) + cbUnhandledException(exceptionRecord, firstChance); + + //call the debug event callback + cbExceptionEvent(exceptionInfo); + } +}; \ No newline at end of file diff --git a/GleeBug/Debugger.Loop.Process.cpp b/GleeBug/Debugger.Loop.Process.cpp new file mode 100644 index 0000000..d4e6883 --- /dev/null +++ b/GleeBug/Debugger.Loop.Process.cpp @@ -0,0 +1,38 @@ +#include "Debugger.h" + +namespace GleeBug +{ + void Debugger::createProcessEvent(const CREATE_PROCESS_DEBUG_INFO & createProcess) + { + //process housekeeping + ProcessInfo process(_debugEvent.dwProcessId, + _debugEvent.dwThreadId); + _processes.insert({ process.dwProcessId, process }); + + //set the current process and current thread + _curProcess = &_processes[process.dwProcessId]; + _curProcess->curThread = &_curProcess->threads[process.dwMainThreadId]; + + //call the debug event callback + cbCreateProcessEvent(createProcess, *_curProcess); + + //close the file handle + CloseHandle(createProcess.hFile); + } + + void Debugger::exitProcessEvent(const EXIT_PROCESS_DEBUG_INFO & exitProcess) + { + //check if the terminated process is the main debuggee + if (_debugEvent.dwProcessId == _mainProcess.dwProcessId) + _breakDebugger = true; + + //call the debug event callback + cbExitProcessEvent(exitProcess, *_curProcess); + + //process housekeeping + _processes.erase(_debugEvent.dwProcessId); + + //set the current process + _curProcess = nullptr; + } +}; \ No newline at end of file diff --git a/GleeBug/Debugger.Loop.Rip.cpp b/GleeBug/Debugger.Loop.Rip.cpp new file mode 100644 index 0000000..f364f45 --- /dev/null +++ b/GleeBug/Debugger.Loop.Rip.cpp @@ -0,0 +1,13 @@ +#include "Debugger.h" + +namespace GleeBug +{ + void Debugger::ripEvent(const RIP_INFO & rip) + { + //prevent anti-debug trick (RIP events are actually exceptions) + _continueStatus = DBG_EXCEPTION_NOT_HANDLED; + + //call the debug event callback + cbRipEvent(rip); + } +}; \ No newline at end of file diff --git a/GleeBug/Debugger.Loop.Thread.cpp b/GleeBug/Debugger.Loop.Thread.cpp new file mode 100644 index 0000000..5fad0c0 --- /dev/null +++ b/GleeBug/Debugger.Loop.Thread.cpp @@ -0,0 +1,29 @@ +#include "Debugger.h" + +namespace GleeBug +{ + void Debugger::createThreadEvent(const CREATE_THREAD_DEBUG_INFO & createThread) + { + //thread housekeeping + ThreadInfo thread(_debugEvent.dwThreadId, createThread.lpThreadLocalBase, createThread.lpStartAddress); + _curProcess->threads.insert({ thread.dwThreadId, thread }); + + //set the current thread + _curProcess->curThread = &_curProcess->threads[thread.dwThreadId]; + + //call the debug event callback + cbCreateThreadEvent(createThread, *_curProcess->curThread); + } + + void Debugger::exitThreadEvent(const EXIT_THREAD_DEBUG_INFO & exitThread) + { + //call the debug event callback + cbExitThreadEvent(exitThread, *_curProcess->curThread); + + //thread housekeeping + _curProcess->threads.erase(_debugEvent.dwThreadId); + + //set the current thread + _curProcess->curThread = nullptr; + } +}; \ No newline at end of file diff --git a/GleeBug/Debugger.Loop.cpp b/GleeBug/Debugger.Loop.cpp index 50f932e..02ef305 100644 --- a/GleeBug/Debugger.Loop.cpp +++ b/GleeBug/Debugger.Loop.cpp @@ -2,122 +2,21 @@ namespace GleeBug { - void Debugger::createProcessEvent(const CREATE_PROCESS_DEBUG_INFO & createProcess) - { - //process housekeeping - ProcessInfo process(_debugEvent.dwProcessId, - _debugEvent.dwThreadId); - _processes.insert({ process.dwProcessId, process }); - - //set the current process and current thread - _curProcess = &_processes[process.dwProcessId]; - _curProcess->curThread = &_curProcess->threads[process.dwMainThreadId]; - - //call the callback - cbCreateProcessEvent(createProcess, *_curProcess); - - //close the file handle - CloseHandle(createProcess.hFile); - } - - void Debugger::exitProcessEvent(const EXIT_PROCESS_DEBUG_INFO & exitProcess) - { - //check if the terminated process is the main debuggee - if (_debugEvent.dwProcessId == _mainProcess.dwProcessId) - _breakDebugger = true; - - //call the callback - cbExitProcessEvent(exitProcess, *_curProcess); - - //process housekeeping - _processes.erase(_debugEvent.dwProcessId); - - //set the current process - _curProcess = nullptr; - } - - void Debugger::createThreadEvent(const CREATE_THREAD_DEBUG_INFO & createThread) - { - //thread housekeeping - ThreadInfo thread(_debugEvent.dwThreadId, createThread.lpThreadLocalBase, createThread.lpStartAddress); - _curProcess->threads.insert({ thread.dwThreadId, thread }); - - //set the current thread - _curProcess->curThread = &_curProcess->threads[thread.dwThreadId]; - - //call the callback - cbCreateThreadEvent(createThread, *_curProcess->curThread); - } - - void Debugger::exitThreadEvent(const EXIT_THREAD_DEBUG_INFO & exitThread) - { - //call the callback - cbExitThreadEvent(exitThread, *_curProcess->curThread); - - //thread housekeeping - _curProcess->threads.erase(_debugEvent.dwThreadId); - - //set the current thread - _curProcess->curThread = nullptr; - } - - void Debugger::loadDllEvent(const LOAD_DLL_DEBUG_INFO & loadDll) - { - //DLL housekeeping - MODULEINFO modinfo; - memset(&modinfo, 0, sizeof(MODULEINFO)); - GetModuleInformation(_curProcess->hProcess, - (HMODULE)loadDll.lpBaseOfDll, - &modinfo, - sizeof(MODULEINFO)); - DllInfo dll(loadDll.lpBaseOfDll, modinfo.SizeOfImage, modinfo.EntryPoint); - _curProcess->dlls.insert({ Range(dll.lpBaseOfDll, dll.lpBaseOfDll + dll.sizeOfImage - 1), dll }); - - //call the callback - cbLoadDllEvent(loadDll, dll); - - //close the file handle - CloseHandle(loadDll.hFile); - } - - void Debugger::unloadDllEvent(const UNLOAD_DLL_DEBUG_INFO & unloadDll) - { - //call the callback - ULONG_PTR lpBaseOfDll = (ULONG_PTR)unloadDll.lpBaseOfDll; - auto dll = _curProcess->dlls.find(Range(lpBaseOfDll, lpBaseOfDll)); - if (dll != _curProcess->dlls.end()) - cbUnloadDllEvent(unloadDll, dll->second); - else - cbUnloadDllEvent(unloadDll, DllInfo(unloadDll.lpBaseOfDll, 0, 0)); - - //DLL housekeeping - if (dll != _curProcess->dlls.end()) - _curProcess->dlls.erase(dll); - } - - void Debugger::exceptionEvent(const EXCEPTION_DEBUG_INFO & exceptionInfo) - { - cbExceptionEvent(exceptionInfo); - } - - void Debugger::debugStringEvent(const OUTPUT_DEBUG_STRING_INFO & debugString) - { - cbDebugStringEvent(debugString); - } - - void Debugger::ripEvent(const RIP_INFO & rip) - { - cbRipEvent(rip); - } - void Debugger::Start() { - _continueStatus = DBG_EXCEPTION_NOT_HANDLED; + //initialize loop variables _breakDebugger = false; + while (!_breakDebugger) { + //wait for a debug event + _isRunning = true; if (!WaitForDebugEvent(&_debugEvent, INFINITE)) break; + _isRunning = false; + + //set default continue status + _continueStatus = DBG_CONTINUE; //set the current process and thread if (_processes.count(_debugEvent.dwProcessId)) @@ -131,6 +30,7 @@ namespace GleeBug else _curProcess = nullptr; + //dispatch the debug event switch (_debugEvent.dwDebugEventCode) { case CREATE_PROCESS_DEBUG_EVENT: @@ -162,10 +62,12 @@ namespace GleeBug break; } + //continue the debug event if (!ContinueDebugEvent(_debugEvent.dwProcessId, _debugEvent.dwThreadId, _continueStatus)) break; } + //cleanup _processes.clear(); _curProcess = nullptr; } diff --git a/GleeBug/Debugger.Process.cpp b/GleeBug/Debugger.Process.cpp index 68434bb..7a126a3 100644 --- a/GleeBug/Debugger.Process.cpp +++ b/GleeBug/Debugger.Process.cpp @@ -5,11 +5,13 @@ namespace GleeBug ProcessInfo::ProcessInfo() { this->curThread = nullptr; + this->systemBreakpoint = false; this->hProcess = INVALID_HANDLE_VALUE; } ProcessInfo::ProcessInfo(DWORD dwProcessId, DWORD dwMainThreadId) { + this->systemBreakpoint = false; this->hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId); this->dwProcessId = dwProcessId; this->dwMainThreadId = dwMainThreadId; diff --git a/GleeBug/Debugger.Process.h b/GleeBug/Debugger.Process.h index f3e8f10..0343da7 100644 --- a/GleeBug/Debugger.Process.h +++ b/GleeBug/Debugger.Process.h @@ -16,8 +16,10 @@ namespace GleeBug DWORD dwProcessId; DWORD dwMainThreadId; - ThreadMap threads; ThreadInfo* curThread; + bool systemBreakpoint; + + ThreadMap threads; DllMap dlls; ProcessInfo(); diff --git a/GleeBug/Debugger.h b/GleeBug/Debugger.h index 027e534..c8327ae 100644 --- a/GleeBug/Debugger.h +++ b/GleeBug/Debugger.h @@ -45,7 +45,7 @@ namespace GleeBug */ void Start(); - protected: //callbacks + protected: //debug event callbacks /** \brief Process creation debug event callback. Provide an implementation to use this callback. \param createProcess Information about the process created. @@ -100,7 +100,20 @@ namespace GleeBug */ virtual void cbRipEvent(const RIP_INFO & rip) {}; - protected: //core functionality + protected: //other callbacks + /** + \brief Unhandled exception callback. Provide an implementation to use this callback. + \param exceptionRecord The exception record. + \param firstChance True if the exception is a first chance exception, false otherwise. + */ + virtual void cbUnhandledException(const EXCEPTION_RECORD & exceptionRecord, const bool firstChance) {}; + + /** + \brief System breakpoint callback. Provide an implementation to use this callback. + */ + virtual void cbSystemBreakpoint() {}; + + protected: //core debug event handlers /** \brief Process creation debug event. Do not override this unless you know what you are doing! \param createProcess Information about the process created. @@ -155,6 +168,21 @@ namespace GleeBug */ virtual void ripEvent(const RIP_INFO & rip); + protected: //core exception handlers + /** + \brief Breakpoint exception handler. Do not override this unless you know what you are doing! + \param exceptionRecord The exception record. + \param firstChance True if the exception is a first chance exception, false otherwise. + */ + virtual void exceptionBreakpoint(const EXCEPTION_RECORD & exceptionRecord, const bool firstChance); + + /** + \brief Single step exception handler. Do not override this unless you know what you are doing! + \param exceptionRecord The exception record. + \param firstChance True if the exception is a first chance exception, false otherwise. + */ + virtual void exceptionSingleStep(const EXCEPTION_RECORD & exceptionRecord, const bool firstChance); + protected: //variables PROCESS_INFORMATION _mainProcess; DWORD _continueStatus; @@ -162,6 +190,7 @@ namespace GleeBug DEBUG_EVENT _debugEvent; ProcessMap _processes; ProcessInfo* _curProcess; + bool _isRunning; }; }; diff --git a/GleeBug/GleeBug.vcxproj b/GleeBug/GleeBug.vcxproj index 01c5a9b..ef28bcf 100644 --- a/GleeBug/GleeBug.vcxproj +++ b/GleeBug/GleeBug.vcxproj @@ -149,6 +149,12 @@ + + + + + + diff --git a/GleeBug/GleeBug.vcxproj.filters b/GleeBug/GleeBug.vcxproj.filters index b53def9..3c4e522 100644 --- a/GleeBug/GleeBug.vcxproj.filters +++ b/GleeBug/GleeBug.vcxproj.filters @@ -30,6 +30,24 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + diff --git a/MyDebugger/MyDebugger.h b/MyDebugger/MyDebugger.h index 764907f..c49741f 100644 --- a/MyDebugger/MyDebugger.h +++ b/MyDebugger/MyDebugger.h @@ -40,7 +40,7 @@ protected: virtual void cbExceptionEvent(const EXCEPTION_DEBUG_INFO & exceptionInfo) { - printf("Exception with code 0x%08X\n", exceptionInfo.ExceptionRecord); + printf("Exception with code 0x%08X\n", exceptionInfo.ExceptionRecord.ExceptionCode); }; virtual void cbDebugStringEvent(const OUTPUT_DEBUG_STRING_INFO & debugString) @@ -52,6 +52,11 @@ protected: { printf("RIP event type 0x%X, error 0x%X", rip.dwType, rip.dwError); }; + + virtual void cbSystemBreakpoint() + { + printf("System breakpoint reached!"); + } }; #endif //_MYDEBUGGER_H \ No newline at end of file