diff --git a/GleeBug/Debugger.Breakpoint.h b/GleeBug/Debugger.Breakpoint.h index 761fe73..d25afdc 100644 --- a/GleeBug/Debugger.Breakpoint.h +++ b/GleeBug/Debugger.Breakpoint.h @@ -17,6 +17,14 @@ namespace GleeBug ShortInt3 }; + enum class HardwareBreakpointSlot + { + Dr0 = 0, + Dr1 = 1, + Dr2 = 2, + Dr3 = 3 + }; + enum class HardwareBreakpointType { Access, @@ -26,11 +34,11 @@ namespace GleeBug enum class HardwareBreakpointSize { - SizeByte, - SizeWord, - SizeDword, + SizeByte = 1, + SizeWord = 2, + SizeDword = 4, #ifdef _WIN64 - SizeQword + SizeQword = 8 #endif //_WIN64 }; @@ -54,6 +62,7 @@ namespace GleeBug } software; struct { + HardwareBreakpointSlot slot; HardwareBreakpointType type; HardwareBreakpointSize size; } hardware; diff --git a/GleeBug/Debugger.Loop.Exception.cpp b/GleeBug/Debugger.Loop.Exception.cpp index b5a91d4..478586b 100644 --- a/GleeBug/Debugger.Loop.Exception.cpp +++ b/GleeBug/Debugger.Loop.Exception.cpp @@ -27,7 +27,7 @@ namespace GleeBug //set back the instruction pointer _registers->Gip = info.address; - //restore the original breakpoint byte and do an internal step + //restore the original breakpoint byte and do an internal step _process->MemWrite(info.address, info.internal.software.oldbytes, info.internal.software.size); _thread->StepInternal(std::bind([this, info]() { @@ -70,11 +70,68 @@ namespace GleeBug for (auto cbStep : cbStepCopy) cbStep(); } - else //handle other single step exceptions + else //handle hardware breakpoint single step exceptions { + exceptionHardwareBreakpoint(ptr(exceptionRecord.ExceptionAddress)); } } + void Debugger::exceptionHardwareBreakpoint(ptr exceptionAddress) + { + //determine the hardware breakpoint triggered + ptr dr6 = _registers->Dr6(); + HardwareBreakpointSlot breakpointSlot; + ptr breakpointAddress; + if (exceptionAddress == _registers->Dr0() || dr6 & 0x1) + { + breakpointAddress = _registers->Dr0(); + breakpointSlot = HardwareBreakpointSlot::Dr0; + } + else if (exceptionAddress == _registers->Dr1() || dr6 & 0x2) + { + breakpointAddress = _registers->Dr1(); + breakpointSlot = HardwareBreakpointSlot::Dr1; + } + else if (exceptionAddress == _registers->Dr2() || dr6 & 0x4) + { + breakpointAddress = _registers->Dr2(); + breakpointSlot = HardwareBreakpointSlot::Dr2; + } + else if (exceptionAddress == _registers->Dr3() || dr6 & 0x8) + { + breakpointAddress = _registers->Dr3(); + breakpointSlot = HardwareBreakpointSlot::Dr3; + } + else + return; //not a hardware breakpoint + + //find the breakpoint in the internal structures + auto foundInfo = _process->breakpoints.find({ BreakpointType::Hardware, breakpointAddress }); + if (foundInfo == _process->breakpoints.end()) + return; //not a valid hardware breakpoint + const auto & info = foundInfo->second; + if (info.internal.hardware.slot != breakpointSlot) + return; //not a valid hardware breakpoint + + //set continue status + _continueStatus = DBG_CONTINUE; + + //delete the hardware breakpoint and do an internal step (TODO: maybe delete from all threads?) + _thread->DeleteHardwareBreakpoint(breakpointSlot); + _thread->StepInternal(std::bind([this, info]() + { + _thread->SetHardwareBreakpoint(info.address, info.internal.hardware.slot, info.internal.hardware.type, info.internal.hardware.size); + })); + + //call the generic callback + cbBreakpoint(info); + + //call the user callback + auto foundCallback = _process->breakpointCallbacks.find({ BreakpointType::Hardware, info.address }); + if (foundCallback != _process->breakpointCallbacks.end()) + foundCallback->second(info); + } + void Debugger::exceptionEvent(const EXCEPTION_DEBUG_INFO & exceptionInfo) { //let the debuggee handle exceptions per default diff --git a/GleeBug/Debugger.Loop.Unknown.cpp b/GleeBug/Debugger.Loop.Unknown.cpp new file mode 100644 index 0000000..d4606a7 --- /dev/null +++ b/GleeBug/Debugger.Loop.Unknown.cpp @@ -0,0 +1,13 @@ +#include "Debugger.h" + +namespace GleeBug +{ + void Debugger::unknownEvent(DWORD debugEventCode) + { + //prevent possible anti-debug trick + _continueStatus = DBG_EXCEPTION_NOT_HANDLED; + + //call the debug event callback + cbUnknownEvent(debugEventCode); + } +}; \ No newline at end of file diff --git a/GleeBug/Debugger.Loop.cpp b/GleeBug/Debugger.Loop.cpp index 88ff9f0..00ed39d 100644 --- a/GleeBug/Debugger.Loop.cpp +++ b/GleeBug/Debugger.Loop.cpp @@ -78,6 +78,9 @@ namespace GleeBug case RIP_EVENT: ripEvent(_debugEvent.u.RipInfo); break; + default: + unknownEvent(_debugEvent.dwDebugEventCode); + break; } //write the register context diff --git a/GleeBug/Debugger.Process.Breakpoint.cpp b/GleeBug/Debugger.Process.Breakpoint.cpp index c49bcbb..1f5332d 100644 --- a/GleeBug/Debugger.Process.Breakpoint.cpp +++ b/GleeBug/Debugger.Process.Breakpoint.cpp @@ -44,12 +44,86 @@ namespace GleeBug bool ProcessInfo::SetBreakpoint(ptr address, const BreakpointCallback & cbBreakpoint, bool singleshoot, SoftwareBreakpointType type) { + //check if a callback on this address was already found + if (breakpointCallbacks.find({ BreakpointType::Software, address }) != breakpointCallbacks.end()) + return false; + //set the breakpoint if (!SetBreakpoint(address, singleshoot, type)) return false; - auto found = breakpointCallbacks.find({ BreakpointType::Software, address }); - if (found != breakpointCallbacks.end()) - return false; + //insert the callback breakpointCallbacks.insert({ { BreakpointType::Software, address }, cbBreakpoint }); return true; } + + bool ProcessInfo::GetFreeHardwareBreakpointSlot(HardwareBreakpointSlot & slot) + { + //find a free hardware breakpoint slot + for (int i = 0; i < 4;i++) + { + if (!hardwareBreakpoints[i].enabled) + { + slot = HardwareBreakpointSlot(i); + return true; + } + } + return false; + } + + bool ProcessInfo::SetHardwareBreakpoint(ptr address, HardwareBreakpointSlot slot, HardwareBreakpointType type, HardwareBreakpointSize size) + { + //check the address + if (!MemIsValidPtr(address) || + breakpoints.find({ BreakpointType::Hardware, address }) != breakpoints.end()) + return false; + + //attempt to set the hardware breakpoint in every thread + bool success = true; + for (auto & thread : threads) + { + if (!thread.second.SetHardwareBreakpoint(address, slot, type, size)) + { + success = false; + break; + } + } + + //if setting failed, unset all + if (!success) + { + for (auto & thread : threads) + thread.second.DeleteHardwareBreakpoint(slot); + return false; + } + + //setup the breakpoint information struct + BreakpointInfo info = {}; + info.address = address; + info.enabled = true; + info.singleshoot = false; + info.type = BreakpointType::Hardware; + info.internal.hardware.slot = slot; + info.internal.hardware.type = type; + info.internal.hardware.size = size; + + //insert in the breakpoint map + breakpoints.insert({ { info.type, info.address }, info }); + + //insert in the hardware breakpoint cache + hardwareBreakpoints[int(slot)] = info; + + return true; + } + + bool ProcessInfo::SetHardwareBreakpoint(ptr address, HardwareBreakpointSlot slot, const BreakpointCallback & cbBreakpoint, HardwareBreakpointType type, HardwareBreakpointSize size) + { + //check if a callback on this address was already found + if (breakpointCallbacks.find({ BreakpointType::Hardware, address }) != breakpointCallbacks.end()) + return false; + //set the hardware breakpoint + if (!SetHardwareBreakpoint(address, slot, type, size)) + return false; + //insert the callback + breakpointCallbacks.insert({ { BreakpointType::Hardware, address }, cbBreakpoint }); + return true; + } }; \ No newline at end of file diff --git a/GleeBug/Debugger.Process.cpp b/GleeBug/Debugger.Process.cpp index 167872d..3e37ce7 100644 --- a/GleeBug/Debugger.Process.cpp +++ b/GleeBug/Debugger.Process.cpp @@ -9,5 +9,7 @@ namespace GleeBug thread(nullptr), systemBreakpoint(false) { + for (int i = 0; i < 4; i++) + hardwareBreakpoints[i].enabled = false; } }; \ No newline at end of file diff --git a/GleeBug/Debugger.Process.h b/GleeBug/Debugger.Process.h index 317ab3d..22f5188 100644 --- a/GleeBug/Debugger.Process.h +++ b/GleeBug/Debugger.Process.h @@ -21,11 +21,11 @@ namespace GleeBug ThreadInfo* thread; bool systemBreakpoint; - ThreadMap threads; + ThreadMap threads; //DO NOT COPY THESE OBJECTS! DllMap dlls; BreakpointMap breakpoints; BreakpointCallbackMap breakpointCallbacks; - BreakpointInfo restoreSoftwareBreakpoint; + BreakpointInfo hardwareBreakpoints[4]; /** \brief Constructor. @@ -62,7 +62,7 @@ namespace GleeBug /** \brief Sets a software breakpoint. - \param address The address to put the breakpoint on. + \param address The address to set 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. @@ -71,7 +71,7 @@ namespace GleeBug /** \brief Sets a software breakpoint. - \param address The address to put the breakpoint on. + \param address The address to set 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. @@ -82,7 +82,7 @@ namespace GleeBug /** \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 address The address to set 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. @@ -95,6 +95,34 @@ namespace GleeBug static_cast(static_cast(debugger)); return SetBreakpoint(address, std::bind(callback, debugger, std::placeholders::_1), singleshoot, type); } + + /** + \brief Attempts to find a free hardware breakpoint slot. + \param [out] slot First free slot found, has no meaning when the function fails. + \return true if a free slot was found, false otherwise. + */ + bool GetFreeHardwareBreakpointSlot(HardwareBreakpointSlot & slot); + + /** + \brief Sets a hardware breakpoint. + \param address The address to set the hardware breakpoint on. + \param slot The hardware breakpoint register slot. Use ProcessInfo::GetFreeHardwareBreakpointSlot. + \param type The hardware breakpoint type. + \param size The hardware breakpoint size. + \return true if the hardware breakpoint was set, false otherwise. + */ + bool SetHardwareBreakpoint(ptr address, HardwareBreakpointSlot slot, HardwareBreakpointType type = HardwareBreakpointType::Execute, HardwareBreakpointSize size = HardwareBreakpointSize::SizeByte); + + /** + \brief Sets a hardware breakpoint. + \param address The address to set the hardware breakpoint on. + \param slot The hardware breakpoint register slot. Use ProcessInfo::GetFreeHardwareBreakpointSlot. + \param cbBreakpoint The breakpoint callback. Can be written using BIND1(this, MyDebugger::cb). + \param type The hardware breakpoint type. + \param size The hardware breakpoint size. + \return true if the hardware breakpoint was set, false otherwise. + */ + bool SetHardwareBreakpoint(ptr address, HardwareBreakpointSlot slot, const BreakpointCallback & cbBreakpoint, HardwareBreakpointType type = HardwareBreakpointType::Execute, HardwareBreakpointSize size = HardwareBreakpointSize::SizeByte); }; }; diff --git a/GleeBug/Debugger.Thread.HardwareBreakpoint.cpp b/GleeBug/Debugger.Thread.HardwareBreakpoint.cpp new file mode 100644 index 0000000..3ebd3da --- /dev/null +++ b/GleeBug/Debugger.Thread.HardwareBreakpoint.cpp @@ -0,0 +1,250 @@ +#include "Debugger.Thread.h" + +#define BITSET(a,x) (a|=1< + + diff --git a/GleeBug/GleeBug.vcxproj.filters b/GleeBug/GleeBug.vcxproj.filters index 56eca6d..5c54e69 100644 --- a/GleeBug/GleeBug.vcxproj.filters +++ b/GleeBug/GleeBug.vcxproj.filters @@ -56,6 +56,12 @@ Source Files + + Source Files + + + Source Files + diff --git a/MyDebugger/MyDebugger.h b/MyDebugger/MyDebugger.h index 7dfca27..2012dd9 100644 --- a/MyDebugger/MyDebugger.h +++ b/MyDebugger/MyDebugger.h @@ -19,16 +19,38 @@ protected: })); } + void cbEntryHardwareBreakpoint(const BreakpointInfo & info) + { + printf("Reached entry hardware breakpoint! GIP: 0x%p\n", + _registers->Gip()); + _thread->StepInto(std::bind([this]() + { + printf("Step after entry hardware breakpoint! GIP: 0x%p\n", + _registers->Gip()); + })); + } + 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, entry); - if(_process->SetBreakpoint(entry, this, &MyDebugger::cbEntryBreakpoint)) + HardwareBreakpointSlot slot; + if (_process->GetFreeHardwareBreakpointSlot(slot)) + { + if (_process->SetHardwareBreakpoint(entry, slot, BIND1(this, MyDebugger::cbEntryHardwareBreakpoint), HardwareBreakpointType::Execute, HardwareBreakpointSize::SizeByte)) + printf("Hardware breakpoint set at 0x%p!\n", entry); + else + printf("Failed to set hardware breakpoint at 0x%p\n", entry); + } + else + printf("No free hardware breakpoint slot...\n"); + + /*if(_process->SetBreakpoint(entry, this, &MyDebugger::cbEntryBreakpoint)) printf("Breakpoint set at 0x%p!\n", entry); else - printf("Failed to set breakpoint at 0x%p...\b", entry); + printf("Failed to set breakpoint at 0x%p...\b", entry);*/ } void cbExitProcessEvent(const EXIT_PROCESS_DEBUG_INFO & exitProcess, const ProcessInfo & process) override @@ -96,7 +118,7 @@ protected: void cbSystemBreakpoint() override { printf("System breakpoint reached, GIP: 0x%p\n", - _registers->Gip.Get()); + _registers->Gip()); _thread->StepInto(this, &MyDebugger::cbStepSystem); } @@ -111,6 +133,15 @@ protected: printf("Breakpoint on 0x%p!\n", info.address); } + + void cbUnhandledException(const EXCEPTION_RECORD & exceptionRecord, bool firstChance) override + { + printf("Unhandled exception (%s) 0x%08X on 0x%p, GIP: 0x%p\n", + firstChance ? "first chance" : "second chance", + exceptionRecord.ExceptionCode, + exceptionRecord.ExceptionAddress, + _registers->Gip()); + } }; #endif //_MYDEBUGGER_H \ No newline at end of file