diff --git a/GleeBug/Debugger.Breakpoint.h b/GleeBug/Debugger.Breakpoint.h new file mode 100644 index 0000000..761fe73 --- /dev/null +++ b/GleeBug/Debugger.Breakpoint.h @@ -0,0 +1,78 @@ +#ifndef _DEBUGGER_BREAKPOINT_H +#define _DEBUGGER_BREAKPOINT_H + +#include "Debugger.Global.h" + +namespace GleeBug +{ + enum class BreakpointType + { + Software, + Hardware, + Memory + }; + + enum class SoftwareBreakpointType + { + ShortInt3 + }; + + enum class HardwareBreakpointType + { + Access, + Write, + Execute + }; + + enum class HardwareBreakpointSize + { + SizeByte, + SizeWord, + SizeDword, +#ifdef _WIN64 + SizeQword +#endif //_WIN64 + }; + + enum class MemoryBreakpointType + { + Acess, + Write, + Execute + }; + + struct BreakpointInternalInfo + { + union + { + struct + { + SoftwareBreakpointType type; + ptr size; + uint8 newbytes[2]; + uint8 oldbytes[2]; + } software; + struct + { + HardwareBreakpointType type; + HardwareBreakpointSize size; + } hardware; + struct + { + MemoryBreakpointType type; + ptr size; + } memory; + }; + }; + + struct BreakpointInfo + { + ptr address; + bool enabled; + bool singleshoot; + BreakpointType type; + BreakpointInternalInfo internal; + }; +}; + +#endif //_DEBUGGER_BREAKPOINT_H \ No newline at end of file diff --git a/GleeBug/Debugger.Global.h b/GleeBug/Debugger.Global.h index 3690ee2..a3e3cb8 100644 --- a/GleeBug/Debugger.Global.h +++ b/GleeBug/Debugger.Global.h @@ -10,14 +10,22 @@ namespace GleeBug class ProcessInfo; class DllInfo; class ThreadInfo; + enum class BreakpointType; + struct BreakpointInfo; + + //key typedefs + typedef std::pair BreakpointKey; + + //callback function typedefs + typedef std::function StepCallback; + typedef std::function BreakpointCallback; //map typedefs typedef std::map ProcessMap; typedef std::map DllMap; typedef std::map ThreadMap; - - //callback function typedefs - typedef std::function StepCallback; + typedef std::map BreakpointMap; + typedef std::map BreakpointCallbackMap; //vector typedefs typedef std::vector StepCallbackVector; diff --git a/GleeBug/Debugger.Loop.Exception.cpp b/GleeBug/Debugger.Loop.Exception.cpp index b815263..7b5f61d 100644 --- a/GleeBug/Debugger.Loop.Exception.cpp +++ b/GleeBug/Debugger.Loop.Exception.cpp @@ -6,27 +6,50 @@ namespace GleeBug { if (!_process->systemBreakpoint) //handle system breakpoint { + //set internal state _process->systemBreakpoint = true; _continueStatus = DBG_CONTINUE; //call the callback cbSystemBreakpoint(); } + else + { + //check if the breakpoint exists + auto foundInfo = _process->breakpoints.find({ BreakpointType::Software, ptr(exceptionRecord.ExceptionAddress) }); + if (foundInfo == _process->breakpoints.end()) + return; + const auto & info = foundInfo->second; + + //set continue status + _continueStatus = DBG_CONTINUE; + + //call the generic callback + cbBreakpoint(info); + + //call the user callback + auto foundCallback = _process->breakpointCallbacks.find({ BreakpointType::Software, info.address }); + if (foundCallback != _process->breakpointCallbacks.end()) + foundCallback->second(info); + } } void Debugger::exceptionSingleStep(const EXCEPTION_RECORD & exceptionRecord, const bool firstChance) { if (_thread->isSingleStepping) //handle single step { + //set internal status _thread->isSingleStepping = false; _continueStatus = DBG_CONTINUE; - //call the callbacks - StepCallbackVector cbStepCopy = _thread->stepCallbacks; + //call the generic callback + cbStep(); + + //call the user callbacks + auto cbStepCopy = _thread->stepCallbacks; _thread->stepCallbacks.clear(); for (auto cbStep : cbStepCopy) cbStep(); - cbStep(); } else //handle other single step exceptions { @@ -39,7 +62,10 @@ namespace GleeBug _continueStatus = DBG_EXCEPTION_NOT_HANDLED; const EXCEPTION_RECORD & exceptionRecord = exceptionInfo.ExceptionRecord; - const bool firstChance = exceptionInfo.dwFirstChance == 1; + bool firstChance = exceptionInfo.dwFirstChance == 1; + + //call the debug event callback + cbExceptionEvent(exceptionInfo); //dispatch the exception switch (exceptionInfo.ExceptionRecord.ExceptionCode) @@ -55,8 +81,5 @@ namespace GleeBug //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.Process.Breakpoint.cpp b/GleeBug/Debugger.Process.Breakpoint.cpp new file mode 100644 index 0000000..d8e6668 --- /dev/null +++ b/GleeBug/Debugger.Process.Breakpoint.cpp @@ -0,0 +1,54 @@ +#include "Debugger.Process.h" + +namespace GleeBug +{ + bool ProcessInfo::SetBreakpoint(ptr address, bool singleshoot, SoftwareBreakpointType type) + { + //check the address + if (!MemIsValidPtr(address) || + breakpoints.find({BreakpointType::Software, address}) != breakpoints.end()) + return false; + + //setup the breakpoint information struct + BreakpointInfo info = {}; + info.address = address; + info.enabled = true; + info.singleshoot = singleshoot; + info.type = BreakpointType::Software; + + //determine breakpoint byte and size from the type + switch (type) + { + case SoftwareBreakpointType::ShortInt3: + info.internal.software.newbytes[0] = 0xCC; + info.internal.software.size = 1; + break; + + default: + return false; + } + + //read/write the breakpoint + if (!MemRead(address, info.internal.software.oldbytes, info.internal.software.size)) + return false; + + if (!MemWrite(address, info.internal.software.newbytes, info.internal.software.size)) + return false; + + //insert in the breakpoint map + breakpoints.insert({ { info.type, info.address }, info }); + + return true; + } + + bool ProcessInfo::SetBreakpoint(ptr address, const BreakpointCallback & cbBreakpoint, bool singleshoot, SoftwareBreakpointType type) + { + if (!SetBreakpoint(address, singleshoot, type)) + return false; + auto found = breakpointCallbacks.find({ BreakpointType::Software, address }); + if (found != breakpointCallbacks.end()) + return false; + breakpointCallbacks.insert({ { BreakpointType::Software, address }, cbBreakpoint }); + return true; + } +}; \ No newline at end of file diff --git a/GleeBug/Debugger.Process.Memory.cpp b/GleeBug/Debugger.Process.Memory.cpp new file mode 100644 index 0000000..62a0d5d --- /dev/null +++ b/GleeBug/Debugger.Process.Memory.cpp @@ -0,0 +1,20 @@ +#include "Debugger.Process.h" + +namespace GleeBug +{ + bool ProcessInfo::MemRead(ptr address, void* buffer, ptr size) const + { + return !!ReadProcessMemory(this->hProcess, reinterpret_cast(address), buffer, size, nullptr); + } + + bool ProcessInfo::MemWrite(ptr address, const void* buffer, ptr size) + { + return !!WriteProcessMemory(this->hProcess, reinterpret_cast(address), buffer, size, nullptr); + } + + bool ProcessInfo::MemIsValidPtr(ptr address) const + { + uint8 byte; + return MemRead(address, &byte, sizeof(byte)); + } +}; \ No newline at end of file diff --git a/GleeBug/Debugger.Process.cpp b/GleeBug/Debugger.Process.cpp index 54ccba7..98a2de7 100644 --- a/GleeBug/Debugger.Process.cpp +++ b/GleeBug/Debugger.Process.cpp @@ -9,14 +9,4 @@ namespace GleeBug this->dwProcessId = dwProcessId; this->dwMainThreadId = dwMainThreadId; } - - bool ProcessInfo::MemRead(ptr address, void* buffer, const size_t size) const - { - return !!ReadProcessMemory(this->hProcess, reinterpret_cast(address), buffer, size, nullptr); - } - - bool ProcessInfo::MemWrite(ptr address, const void* buffer, const size_t size) const - { - return !!WriteProcessMemory(this->hProcess, reinterpret_cast(address), buffer, size, nullptr); - } }; \ No newline at end of file diff --git a/GleeBug/Debugger.Process.h b/GleeBug/Debugger.Process.h index d4b3d89..bad63d1 100644 --- a/GleeBug/Debugger.Process.h +++ b/GleeBug/Debugger.Process.h @@ -4,6 +4,7 @@ #include "Debugger.Global.h" #include "Debugger.Thread.h" #include "Debugger.Dll.h" +#include "Debugger.Breakpoint.h" namespace GleeBug { @@ -22,6 +23,8 @@ namespace GleeBug ThreadMap threads; DllMap dlls; + BreakpointMap breakpoints; + BreakpointCallbackMap breakpointCallbacks; /** \brief Constructor. @@ -37,7 +40,7 @@ namespace GleeBug \param size The size to read. \return true if it succeeds, false if it fails. */ - bool MemRead(ptr address, void* buffer, const size_t size) const; + bool MemRead(ptr address, void* buffer, ptr size) const; /** \brief Write memory to the process. @@ -46,7 +49,50 @@ namespace GleeBug \param size The size to write. \return true if it succeeds, false if it fails. */ - bool MemWrite(ptr address, const void* buffer, const size_t size) const; + bool MemWrite(ptr address, const void* buffer, ptr size); + + /** + \brief Check if an address is a valid read pointer. + \param address The address to check. + \return true if the address is valid, false otherwise. + */ + bool MemIsValidPtr(ptr address) const; + + /** + \brief Sets a software breakpoint. + \param address The address to put the breakpoint on. + \param singleshoot (Optional) True to remove the breakpoint after the first hit. + \param type (Optional) The software breakpoint type. + \return true if the breakpoint was set, false otherwise. + */ + bool SetBreakpoint(ptr address, bool singleshoot = false, SoftwareBreakpointType type = SoftwareBreakpointType::ShortInt3); + + /** + \brief Sets a software breakpoint. + \param address The address to put the breakpoint on. + \param cbBreakpoint The breakpoint callback. Can be written using BIND1(this, MyDebugger::cb). + \param singleshoot (Optional) True to remove the breakpoint after the first hit. + \param type (Optional) The software breakpoint type. + \return true if the breakpoint was set, false otherwise. + */ + bool SetBreakpoint(ptr address, const BreakpointCallback & cbBreakpoint, bool singleshoot = false, SoftwareBreakpointType type = SoftwareBreakpointType::ShortInt3); + + /** + \brief Sets a software breakpoint. + \tparam T Generic type parameter. Must be a subclass of Debugger. + \param address The address to put the breakpoint on. + \param debugger This pointer to a subclass of Debugger. + \param callback Pointer to the callback. Written like: &MyDebugger::cb + \param singleshoot (Optional) True to remove the breakpoint after the first hit. + \param type (Optional) The software breakpoint type. + \return true if the breakpoint was set, false otherwise. + */ + template + bool SetBreakpoint(ptr address, T* debugger, void(T::*callback)(const BreakpointInfo & info), bool singleshoot = false, SoftwareBreakpointType type = SoftwareBreakpointType::ShortInt3) + { + static_cast(static_cast(debugger)); + return SetBreakpoint(address, std::bind(callback, debugger, std::placeholders::_1), singleshoot, type); + } }; }; diff --git a/GleeBug/Debugger.h b/GleeBug/Debugger.h index 23bbc71..049a7c1 100644 --- a/GleeBug/Debugger.h +++ b/GleeBug/Debugger.h @@ -3,6 +3,7 @@ #include "Debugger.Global.h" #include "Debugger.Process.h" +#include "Debugger.Breakpoint.h" namespace GleeBug { @@ -52,55 +53,55 @@ namespace GleeBug protected: //debug event callbacks /** - \brief Process creation debug event callback. Provide an implementation to use this callback. + \brief Process creation debug event callback. Called after the event is internally processed. Provide an implementation to use this callback. \param createProcess Information about the process created. */ virtual void cbCreateProcessEvent(const CREATE_PROCESS_DEBUG_INFO & createProcess, const ProcessInfo & process) {}; /** - \brief Process termination debug event callback. Provide an implementation to use this callback. + \brief Process termination debug event callback. Called before the event is internally processed. Provide an implementation to use this callback. \param exitProcess Information about the process terminated. */ virtual void cbExitProcessEvent(const EXIT_PROCESS_DEBUG_INFO & exitProcess, const ProcessInfo & process) {}; /** - \brief Thread creation debug event callback. Provide an implementation to use this callback. + \brief Thread creation debug event callback. Called after the event is internally processed. Provide an implementation to use this callback. \param createThread Information about the thread created. */ virtual void cbCreateThreadEvent(const CREATE_THREAD_DEBUG_INFO & createThread, const ThreadInfo & thread) {}; /** - \brief Thread termination debug event callback. Provide an implementation to use this callback. + \brief Thread termination debug event callback. Called before the event is internally processed. Provide an implementation to use this callback. \param exitThread Information about the thread terminated. */ virtual void cbExitThreadEvent(const EXIT_THREAD_DEBUG_INFO & exitThread, const ThreadInfo & thread) {}; /** - \brief DLL load debug event callback. Provide an implementation to use this callback. + \brief DLL load debug event callback. Called after event is internally processed. Provide an implementation to use this callback. \param loadDll Information about the DLL loaded. */ virtual void cbLoadDllEvent(const LOAD_DLL_DEBUG_INFO & loadDll, const DllInfo & dll) {}; /** - \brief DLL unload debug event callback. Provide an implementation to use this callback. + \brief DLL unload debug event callback. Called before event is internally processed. Provide an implementation to use this callback. \param unloadDll Information about the DLL unloaded. */ virtual void cbUnloadDllEvent(const UNLOAD_DLL_DEBUG_INFO & unloadDll, const DllInfo & dll) {}; /** - \brief Exception debug event callback. Provide an implementation to use this callback. + \brief Exception debug event callback. Called before the event is internally processed. Provide an implementation to use this callback. \param exceptionInfo Information about the exception. */ virtual void cbExceptionEvent(const EXCEPTION_DEBUG_INFO & exceptionInfo) {}; /** - \brief Debug string debug event callback. Provide an implementation to use this callback. + \brief Debug string debug event callback. Called before the event is internally processed. Provide an implementation to use this callback. \param debugString Information about the debug string. */ virtual void cbDebugStringEvent(const OUTPUT_DEBUG_STRING_INFO & debugString) {}; /** - \brief RIP debug event callback. Provide an implementation to use this callback. + \brief RIP debug event callback. Called before the event is internally processed. Provide an implementation to use this callback. \param rip Information about the RIP event. */ virtual void cbRipEvent(const RIP_INFO & rip) {}; @@ -113,22 +114,28 @@ namespace GleeBug virtual void cbInternalError(const std::string & error) {}; /** - \brief Unhandled exception callback. Provide an implementation to use this callback. + \brief Unhandled exception callback. Called after the exception event is processed. 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) {}; + virtual void cbUnhandledException(const EXCEPTION_RECORD & exceptionRecord, bool firstChance) {}; /** - \brief System breakpoint callback. Provide an implementation to use this callback. + \brief System breakpoint callback. Called after the event is internally processed. Provide an implementation to use this callback. */ virtual void cbSystemBreakpoint() {}; /** - \brief Step callback. Provide an implementation to use this callback. + \brief Step callback. Called before any user callbacks. Provide an implementation to use this callback. */ virtual void cbStep() {}; + /** + \brief Breakpoint callback. Called before any user callbacks. Provide an implementation to use this callback. + \param info The breakpoint information. + */ + virtual void cbBreakpoint(const BreakpointInfo & info) {} + protected: //core debug event handlers /** \brief Process creation debug event. Do not override this unless you know what you are doing! @@ -190,14 +197,14 @@ namespace GleeBug \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); + virtual void exceptionBreakpoint(const EXCEPTION_RECORD & exceptionRecord, 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); + virtual void exceptionSingleStep(const EXCEPTION_RECORD & exceptionRecord, bool firstChance); protected: //variables PROCESS_INFORMATION _mainProcess; diff --git a/GleeBug/GleeBug.h b/GleeBug/GleeBug.h index b4cf238..789b339 100644 --- a/GleeBug/GleeBug.h +++ b/GleeBug/GleeBug.h @@ -15,6 +15,7 @@ //macros #define BIND(thisPtr, funcPtr) std::bind(&funcPtr, thisPtr) +#define BIND1(thisPtr, funcPtr) std::bind(&funcPtr, thisPtr, std::placeholders::_1) namespace GleeBug { diff --git a/GleeBug/GleeBug.vcxproj b/GleeBug/GleeBug.vcxproj index 0536743..c0d3892 100644 --- a/GleeBug/GleeBug.vcxproj +++ b/GleeBug/GleeBug.vcxproj @@ -155,12 +155,15 @@ + + + diff --git a/GleeBug/GleeBug.vcxproj.filters b/GleeBug/GleeBug.vcxproj.filters index c61c0e4..56eca6d 100644 --- a/GleeBug/GleeBug.vcxproj.filters +++ b/GleeBug/GleeBug.vcxproj.filters @@ -50,6 +50,12 @@ Source Files + + Source Files + + + Source Files + @@ -79,5 +85,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/MyDebugger/MyDebugger.h b/MyDebugger/MyDebugger.h index 647ad42..a86981d 100644 --- a/MyDebugger/MyDebugger.h +++ b/MyDebugger/MyDebugger.h @@ -8,11 +8,21 @@ using namespace GleeBug; class MyDebugger : public Debugger { protected: + void myBreakpoint(const BreakpointInfo & info) + { + puts("myBreakpoint()"); + } + void cbCreateProcessEvent(const CREATE_PROCESS_DEBUG_INFO & createProcess, const ProcessInfo & process) override { + ptr entry = ptr(createProcess.lpStartAddress); printf("Process %d created with entry 0x%p\n", _debugEvent.dwProcessId, - createProcess.lpStartAddress); + entry); + if(_process->SetBreakpoint(entry, this, &MyDebugger::myBreakpoint)) + printf("Breakpoint set at 0x%p!\n", entry); + else + printf("Failed to set breakpoint at 0x%p...\b", entry); } void cbExitProcessEvent(const EXIT_PROCESS_DEBUG_INFO & exitProcess, const ProcessInfo & process) override @@ -50,7 +60,9 @@ protected: void cbExceptionEvent(const EXCEPTION_DEBUG_INFO & exceptionInfo) override { - printf("Exception with code 0x%08X at 0x%p\n", + const char* exceptionType = exceptionInfo.dwFirstChance ? "First Chance" : "Second Chance"; + printf("%s exception with code 0x%08X at 0x%p\n", + exceptionType, exceptionInfo.ExceptionRecord.ExceptionCode, exceptionInfo.ExceptionRecord.ExceptionAddress); } @@ -87,7 +99,7 @@ protected: { printf("System breakpoint reached, CIP: 0x%p\n", _registers->Gip.Get()); - _thread->StepInto(BIND(this, MyDebugger::boobs)); + _thread->StepInto(this, &MyDebugger::boobs); } void cbInternalError(const std::string & error) override @@ -95,6 +107,12 @@ protected: printf("Internal Error: %s\n", error.c_str()); } + + void cbBreakpoint(const BreakpointInfo & info) override + { + printf("Breakpoint on 0x%p!\n", + info.address); + } }; #endif //_MYDEBUGGER_H \ No newline at end of file