From 439fd82767f13bb31360a5bc0c03e7ce521c6160 Mon Sep 17 00:00:00 2001 From: mrexodia Date: Sat, 19 Dec 2015 05:17:04 +0100 Subject: [PATCH] singleshoot hardware/software breakpoints working + delete hardware/software breakpoints + nice callback set function for hardware breakpoints --- GleeBug/Debugger.Loop.Exception.cpp | 22 ++++++-- GleeBug/Debugger.Process.Breakpoint.cpp | 74 +++++++++++++++++++++++-- GleeBug/Debugger.Process.h | 54 ++++++++++++++++-- MyDebugger/MyDebugger.h | 10 +++- 4 files changed, 142 insertions(+), 18 deletions(-) diff --git a/GleeBug/Debugger.Loop.Exception.cpp b/GleeBug/Debugger.Loop.Exception.cpp index 478586b..97cefd9 100644 --- a/GleeBug/Debugger.Loop.Exception.cpp +++ b/GleeBug/Debugger.Loop.Exception.cpp @@ -19,7 +19,7 @@ namespace GleeBug auto foundInfo = _process->breakpoints.find({ BreakpointType::Software, ptr(exceptionRecord.ExceptionAddress) }); if (foundInfo == _process->breakpoints.end()) return; - const auto & info = foundInfo->second; + const auto info = foundInfo->second; //set continue status _continueStatus = DBG_CONTINUE; @@ -31,7 +31,9 @@ namespace GleeBug _process->MemWrite(info.address, info.internal.software.oldbytes, info.internal.software.size); _thread->StepInternal(std::bind([this, info]() { - _process->MemWrite(info.address, info.internal.software.newbytes, info.internal.software.size); + //only restore the bytes if the breakpoint still exists + if (_process->breakpoints.find({ BreakpointType::Software, info.address }) != _process->breakpoints.end()) + _process->MemWrite(info.address, info.internal.software.newbytes, info.internal.software.size); })); //call the generic callback @@ -41,6 +43,10 @@ namespace GleeBug auto foundCallback = _process->breakpointCallbacks.find({ BreakpointType::Software, info.address }); if (foundCallback != _process->breakpointCallbacks.end()) foundCallback->second(info); + + //delete the breakpoint if it is singleshoot + if (info.singleshoot) + _process->DeleteGenericBreakpoint(info); } } @@ -109,18 +115,20 @@ namespace GleeBug auto foundInfo = _process->breakpoints.find({ BreakpointType::Hardware, breakpointAddress }); if (foundInfo == _process->breakpoints.end()) return; //not a valid hardware breakpoint - const auto & info = foundInfo->second; + 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?) + //delete the hardware breakpoint from the thread (not the breakpoint buffer) 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); + //only restore if the breakpoint still exists + if (_process->breakpoints.find({ BreakpointType::Hardware, info.address }) != _process->breakpoints.end()) + _thread->SetHardwareBreakpoint(info.address, info.internal.hardware.slot, info.internal.hardware.type, info.internal.hardware.size); })); //call the generic callback @@ -130,6 +138,10 @@ namespace GleeBug auto foundCallback = _process->breakpointCallbacks.find({ BreakpointType::Hardware, info.address }); if (foundCallback != _process->breakpointCallbacks.end()) foundCallback->second(info); + + //delete the breakpoint if it is singleshoot + if (info.singleshoot) + _process->DeleteGenericBreakpoint(info); } void Debugger::exceptionEvent(const EXCEPTION_DEBUG_INFO & exceptionInfo) diff --git a/GleeBug/Debugger.Process.Breakpoint.cpp b/GleeBug/Debugger.Process.Breakpoint.cpp index 7b57568..c345f43 100644 --- a/GleeBug/Debugger.Process.Breakpoint.cpp +++ b/GleeBug/Debugger.Process.Breakpoint.cpp @@ -6,7 +6,7 @@ namespace GleeBug { //check the address if (!MemIsValidPtr(address) || - breakpoints.find({BreakpointType::Software, address}) != breakpoints.end()) + breakpoints.find({ BreakpointType::Software, address }) != breakpoints.end()) return false; //setup the breakpoint information struct @@ -15,7 +15,7 @@ namespace GleeBug info.enabled = true; info.singleshoot = singleshoot; info.type = BreakpointType::Software; - + //determine breakpoint byte and size from the type switch (type) { @@ -55,6 +55,28 @@ namespace GleeBug return true; } + bool ProcessInfo::DeleteBreakpoint(ptr address) + { + //find the breakpoint + auto found = breakpoints.find({ BreakpointType::Software, address }); + if (found == breakpoints.end()) + return false; + const auto & info = found->second; + + //restore the breakpoint bytes if the breakpoint is enabled + if (info.enabled) + { + if (!MemWrite(address, info.internal.software.oldbytes, info.internal.software.size)) + return false; + FlushInstructionCache(hProcess, nullptr, 0); + } + + //remove the breakpoint from the maps + breakpoints.erase(found); + breakpointCallbacks.erase({ BreakpointType::Software, address }); + return true; + } + bool ProcessInfo::GetFreeHardwareBreakpointSlot(HardwareBreakpointSlot & slot) const { //find a free hardware breakpoint slot @@ -69,7 +91,7 @@ namespace GleeBug return false; } - bool ProcessInfo::SetHardwareBreakpoint(ptr address, HardwareBreakpointSlot slot, HardwareBreakpointType type, HardwareBreakpointSize size) + bool ProcessInfo::SetHardwareBreakpoint(ptr address, HardwareBreakpointSlot slot, HardwareBreakpointType type, HardwareBreakpointSize size, bool singleshoot) { //check the address if (!MemIsValidPtr(address) || @@ -99,7 +121,7 @@ namespace GleeBug BreakpointInfo info = {}; info.address = address; info.enabled = true; - info.singleshoot = false; + info.singleshoot = singleshoot; info.type = BreakpointType::Hardware; info.internal.hardware.slot = slot; info.internal.hardware.type = type; @@ -114,16 +136,56 @@ namespace GleeBug return true; } - bool ProcessInfo::SetHardwareBreakpoint(ptr address, HardwareBreakpointSlot slot, const BreakpointCallback & cbBreakpoint, HardwareBreakpointType type, HardwareBreakpointSize size) + bool ProcessInfo::SetHardwareBreakpoint(ptr address, HardwareBreakpointSlot slot, const BreakpointCallback & cbBreakpoint, HardwareBreakpointType type, HardwareBreakpointSize size, bool singleshoot) { //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)) + if (!SetHardwareBreakpoint(address, slot, type, size, singleshoot)) return false; //insert the callback breakpointCallbacks.insert({ { BreakpointType::Hardware, address }, cbBreakpoint }); return true; } + + bool ProcessInfo::DeleteHardwareBreakpoint(ptr address) + { + //find the hardware breakpoint + auto found = breakpoints.find({ BreakpointType::Hardware, address }); + if (found == breakpoints.end()) + return false; + const auto & info = found->second; + + //delete the hardware breakpoint from the internal buffer + hardwareBreakpoints[int(info.internal.hardware.slot)].enabled = false; + + //delete the hardware breakpoint from the registers + bool success = true; + for (auto & thread : threads) + { + if (!thread.second.DeleteHardwareBreakpoint(info.internal.hardware.slot)) + success = false; + } + + //delete the breakpoint from the maps + breakpoints.erase(found); + breakpointCallbacks.erase({ BreakpointType::Hardware, address }); + return success; + } + + bool ProcessInfo::DeleteGenericBreakpoint(const BreakpointInfo & info) + { + switch (info.type) + { + case BreakpointType::Software: + return DeleteBreakpoint(info.address); + case BreakpointType::Hardware: + return DeleteHardwareBreakpoint(info.address); + case BreakpointType::Memory: + return false; //TODO implement this + default: + return false; + } + } }; \ No newline at end of file diff --git a/GleeBug/Debugger.Process.h b/GleeBug/Debugger.Process.h index 5861b8b..eaf6281 100644 --- a/GleeBug/Debugger.Process.h +++ b/GleeBug/Debugger.Process.h @@ -96,6 +96,13 @@ namespace GleeBug return SetBreakpoint(address, std::bind(callback, debugger, std::placeholders::_1), singleshoot, type); } + /** + \brief Deletes a software breakpoint. + \param address The address to delete the breakpoint from. + \return true if the breakpoint was deleted, false otherwise. + */ + bool DeleteBreakpoint(ptr address); + /** \brief Attempts to find a free hardware breakpoint slot. \param [out] slot First free slot found, has no meaning when the function fails. @@ -107,22 +114,57 @@ namespace GleeBug \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. + \param type (Optional) The hardware breakpoint type. + \param size (Optional) The hardware breakpoint size. + \param singleshoot (Optional) True to remove the breakpoint after the first hit. \return true if the hardware breakpoint was set, false otherwise. */ - bool SetHardwareBreakpoint(ptr address, HardwareBreakpointSlot slot, HardwareBreakpointType type = HardwareBreakpointType::Execute, HardwareBreakpointSize size = HardwareBreakpointSize::SizeByte); + bool SetHardwareBreakpoint(ptr address, HardwareBreakpointSlot slot, HardwareBreakpointType type = HardwareBreakpointType::Execute, HardwareBreakpointSize size = HardwareBreakpointSize::SizeByte, bool singleshoot = false); /** \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. + \param type (Optional) The hardware breakpoint type. + \param size (Optional) The hardware breakpoint size. + \param singleshoot (Optional) True to remove the breakpoint after the first hit. \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); + bool SetHardwareBreakpoint(ptr address, HardwareBreakpointSlot slot, const BreakpointCallback & cbBreakpoint, HardwareBreakpointType type = HardwareBreakpointType::Execute, HardwareBreakpointSize size = HardwareBreakpointSize::SizeByte, bool singleshoot = false); + + /** + \brief Sets a hardware breakpoint. + \tparam T Generic type parameter. Must be a subclass of Debugger. + \param address The address to set the hardware breakpoint on. + \param slot The hardware breakpoint register slot. Use ProcessInfo::GetFreeHardwareBreakpointSlot. + \param debugger This pointer to a subclass of Debugger. + \param callback Pointer to the callback. Written like: &MyDebugger::cb + \param type (Optional) The hardware breakpoint type. + \param size (Optional) The hardware breakpoint size. + \param singleshoot (Optional) True to remove the breakpoint after the first hit. + \return true if the hardware breakpoint was set, false otherwise. + */ + template + bool SetHardwareBreakpoint(ptr address, HardwareBreakpointSlot slot, T* debugger, void(T::*callback)(const BreakpointInfo & info), HardwareBreakpointType type = HardwareBreakpointType::Execute, HardwareBreakpointSize size = HardwareBreakpointSize::SizeByte, bool singleshoot = false) + { + static_cast(static_cast(debugger)); + return SetHardwareBreakpoint(address, slot, std::bind(callback, debugger, std::placeholders::_1), type, size, singleshoot); + } + + /** + \brief Deletes a hardware breakpoint. + \param address The address the hardware breakpoint is set on. + \return true if the hardware breakpoint was deleted, false otherwise. + */ + bool DeleteHardwareBreakpoint(ptr address); + + /** + \brief Deletes a breakpoint. + \param info The breakpoint information. + \return true if the breakpoint was deleted, false otherwise. + */ + bool DeleteGenericBreakpoint(const BreakpointInfo & info); }; }; diff --git a/MyDebugger/MyDebugger.h b/MyDebugger/MyDebugger.h index de18dd2..eb38ca2 100644 --- a/MyDebugger/MyDebugger.h +++ b/MyDebugger/MyDebugger.h @@ -12,6 +12,10 @@ protected: { printf("Reached entry breakpoint! GIP: 0x%p\n", _registers->Gip()); + if (_process->DeleteBreakpoint(info.address)) + printf("Entry breakpoint deleted!\n"); + else + printf("Failed to delete entry breakpoint...\n"); _thread->StepInto(std::bind([this]() { printf("Step after entry breakpoint! GIP: 0x%p\n", @@ -23,6 +27,10 @@ protected: { printf("Reached entry hardware breakpoint! GIP: 0x%p\n", _registers->Gip()); + if (_process->DeleteHardwareBreakpoint(info.address)) + printf("Entry hardware breakpoint deleted!\n"); + else + printf("Failed to delete entry hardware breakpoint...\n"); _thread->StepInto(std::bind([this]() { printf("Step after entry hardware breakpoint! GIP: 0x%p\n", @@ -45,7 +53,7 @@ protected: HardwareBreakpointSlot slot; if (_process->GetFreeHardwareBreakpointSlot(slot)) { - if (_process->SetHardwareBreakpoint(entry, slot, BIND1(this, MyDebugger::cbEntryHardwareBreakpoint), HardwareBreakpointType::Execute, HardwareBreakpointSize::SizeByte)) + if (_process->SetHardwareBreakpoint(entry, slot, 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);