mirror of https://github.com/x64dbg/GleeBug
hardware breakpoints working + fixed possible bug with breakpoints + added unknownEvent callback + added cbUnhandledException in MyDebugger
This commit is contained in:
parent
26c71cc6e4
commit
3bde590956
|
|
@ -17,6 +17,14 @@ namespace GleeBug
|
||||||
ShortInt3
|
ShortInt3
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class HardwareBreakpointSlot
|
||||||
|
{
|
||||||
|
Dr0 = 0,
|
||||||
|
Dr1 = 1,
|
||||||
|
Dr2 = 2,
|
||||||
|
Dr3 = 3
|
||||||
|
};
|
||||||
|
|
||||||
enum class HardwareBreakpointType
|
enum class HardwareBreakpointType
|
||||||
{
|
{
|
||||||
Access,
|
Access,
|
||||||
|
|
@ -26,11 +34,11 @@ namespace GleeBug
|
||||||
|
|
||||||
enum class HardwareBreakpointSize
|
enum class HardwareBreakpointSize
|
||||||
{
|
{
|
||||||
SizeByte,
|
SizeByte = 1,
|
||||||
SizeWord,
|
SizeWord = 2,
|
||||||
SizeDword,
|
SizeDword = 4,
|
||||||
#ifdef _WIN64
|
#ifdef _WIN64
|
||||||
SizeQword
|
SizeQword = 8
|
||||||
#endif //_WIN64
|
#endif //_WIN64
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -54,6 +62,7 @@ namespace GleeBug
|
||||||
} software;
|
} software;
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
|
HardwareBreakpointSlot slot;
|
||||||
HardwareBreakpointType type;
|
HardwareBreakpointType type;
|
||||||
HardwareBreakpointSize size;
|
HardwareBreakpointSize size;
|
||||||
} hardware;
|
} hardware;
|
||||||
|
|
|
||||||
|
|
@ -70,11 +70,68 @@ namespace GleeBug
|
||||||
for (auto cbStep : cbStepCopy)
|
for (auto cbStep : cbStepCopy)
|
||||||
cbStep();
|
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)
|
void Debugger::exceptionEvent(const EXCEPTION_DEBUG_INFO & exceptionInfo)
|
||||||
{
|
{
|
||||||
//let the debuggee handle exceptions per default
|
//let the debuggee handle exceptions per default
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -78,6 +78,9 @@ namespace GleeBug
|
||||||
case RIP_EVENT:
|
case RIP_EVENT:
|
||||||
ripEvent(_debugEvent.u.RipInfo);
|
ripEvent(_debugEvent.u.RipInfo);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
unknownEvent(_debugEvent.dwDebugEventCode);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//write the register context
|
//write the register context
|
||||||
|
|
|
||||||
|
|
@ -44,12 +44,86 @@ namespace GleeBug
|
||||||
|
|
||||||
bool ProcessInfo::SetBreakpoint(ptr address, const BreakpointCallback & cbBreakpoint, bool singleshoot, SoftwareBreakpointType type)
|
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))
|
if (!SetBreakpoint(address, singleshoot, type))
|
||||||
return false;
|
return false;
|
||||||
auto found = breakpointCallbacks.find({ BreakpointType::Software, address });
|
//insert the callback
|
||||||
if (found != breakpointCallbacks.end())
|
|
||||||
return false;
|
|
||||||
breakpointCallbacks.insert({ { BreakpointType::Software, address }, cbBreakpoint });
|
breakpointCallbacks.insert({ { BreakpointType::Software, address }, cbBreakpoint });
|
||||||
return true;
|
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;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -9,5 +9,7 @@ namespace GleeBug
|
||||||
thread(nullptr),
|
thread(nullptr),
|
||||||
systemBreakpoint(false)
|
systemBreakpoint(false)
|
||||||
{
|
{
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
hardwareBreakpoints[i].enabled = false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -21,11 +21,11 @@ namespace GleeBug
|
||||||
ThreadInfo* thread;
|
ThreadInfo* thread;
|
||||||
bool systemBreakpoint;
|
bool systemBreakpoint;
|
||||||
|
|
||||||
ThreadMap threads;
|
ThreadMap threads; //DO NOT COPY THESE OBJECTS!
|
||||||
DllMap dlls;
|
DllMap dlls;
|
||||||
BreakpointMap breakpoints;
|
BreakpointMap breakpoints;
|
||||||
BreakpointCallbackMap breakpointCallbacks;
|
BreakpointCallbackMap breakpointCallbacks;
|
||||||
BreakpointInfo restoreSoftwareBreakpoint;
|
BreakpointInfo hardwareBreakpoints[4];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief Constructor.
|
\brief Constructor.
|
||||||
|
|
@ -62,7 +62,7 @@ namespace GleeBug
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief Sets a software breakpoint.
|
\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 singleshoot (Optional) True to remove the breakpoint after the first hit.
|
||||||
\param type (Optional) The software breakpoint type.
|
\param type (Optional) The software breakpoint type.
|
||||||
\return true if the breakpoint was set, false otherwise.
|
\return true if the breakpoint was set, false otherwise.
|
||||||
|
|
@ -71,7 +71,7 @@ namespace GleeBug
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\brief Sets a software breakpoint.
|
\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 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 singleshoot (Optional) True to remove the breakpoint after the first hit.
|
||||||
\param type (Optional) The software breakpoint type.
|
\param type (Optional) The software breakpoint type.
|
||||||
|
|
@ -82,7 +82,7 @@ namespace GleeBug
|
||||||
/**
|
/**
|
||||||
\brief Sets a software breakpoint.
|
\brief Sets a software breakpoint.
|
||||||
\tparam T Generic type parameter. Must be a subclass of Debugger.
|
\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 debugger This pointer to a subclass of Debugger.
|
||||||
\param callback Pointer to the callback. Written like: &MyDebugger::cb
|
\param callback Pointer to the callback. Written like: &MyDebugger::cb
|
||||||
\param singleshoot (Optional) True to remove the breakpoint after the first hit.
|
\param singleshoot (Optional) True to remove the breakpoint after the first hit.
|
||||||
|
|
@ -95,6 +95,34 @@ namespace GleeBug
|
||||||
static_cast<void>(static_cast<Debugger*>(debugger));
|
static_cast<void>(static_cast<Debugger*>(debugger));
|
||||||
return SetBreakpoint(address, std::bind(callback, debugger, std::placeholders::_1), singleshoot, type);
|
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);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,250 @@
|
||||||
|
#include "Debugger.Thread.h"
|
||||||
|
|
||||||
|
#define BITSET(a,x) (a|=1<<x)
|
||||||
|
#define BITCLEAR(a,x) (a&=~(1<<x))
|
||||||
|
#define BITTOGGLE(a,x) (a^=1<<x)
|
||||||
|
#define BITGET(a,x) (a&(1<<x))
|
||||||
|
|
||||||
|
namespace GleeBug
|
||||||
|
{
|
||||||
|
enum DR7_MODE
|
||||||
|
{
|
||||||
|
MODE_DISABLED = 0, //00
|
||||||
|
MODE_LOCAL = 1, //01
|
||||||
|
MODE_GLOBAL = 2 //10
|
||||||
|
};
|
||||||
|
|
||||||
|
enum DR7_TYPE
|
||||||
|
{
|
||||||
|
TYPE_EXECUTE = 0, //00
|
||||||
|
TYPE_WRITE = 1, //01
|
||||||
|
TYPE_READWRITE = 3 //11
|
||||||
|
};
|
||||||
|
|
||||||
|
enum DR7_SIZE
|
||||||
|
{
|
||||||
|
SIZE_1 = 0, //00
|
||||||
|
SIZE_2 = 1, //01
|
||||||
|
SIZE_8 = 2, //10
|
||||||
|
SIZE_4 = 3 //11
|
||||||
|
};
|
||||||
|
|
||||||
|
#pragma pack(1)
|
||||||
|
struct DR7
|
||||||
|
{
|
||||||
|
BYTE DR7_MODE[4];
|
||||||
|
BYTE DR7_TYPE[4];
|
||||||
|
BYTE DR7_SIZE[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline ptr dr7_ptr(const DR7 & dr7)
|
||||||
|
{
|
||||||
|
ptr result = 0;
|
||||||
|
if (BITGET(dr7.DR7_MODE[0], 0))
|
||||||
|
BITSET(result, 0);
|
||||||
|
if (BITGET(dr7.DR7_MODE[0], 1))
|
||||||
|
BITSET(result, 1);
|
||||||
|
if (BITGET(dr7.DR7_MODE[1], 0))
|
||||||
|
BITSET(result, 2);
|
||||||
|
if (BITGET(dr7.DR7_MODE[1], 1))
|
||||||
|
BITSET(result, 3);
|
||||||
|
if (BITGET(dr7.DR7_MODE[2], 0))
|
||||||
|
BITSET(result, 4);
|
||||||
|
if (BITGET(dr7.DR7_MODE[2], 1))
|
||||||
|
BITSET(result, 5);
|
||||||
|
if (BITGET(dr7.DR7_MODE[3], 0))
|
||||||
|
BITSET(result, 6);
|
||||||
|
if (BITGET(dr7.DR7_MODE[3], 1))
|
||||||
|
BITSET(result, 7);
|
||||||
|
if (BITGET(dr7.DR7_TYPE[0], 0))
|
||||||
|
BITSET(result, 16);
|
||||||
|
if (BITGET(dr7.DR7_TYPE[0], 1))
|
||||||
|
BITSET(result, 17);
|
||||||
|
if (BITGET(dr7.DR7_SIZE[0], 0))
|
||||||
|
BITSET(result, 18);
|
||||||
|
if (BITGET(dr7.DR7_SIZE[0], 1))
|
||||||
|
BITSET(result, 19);
|
||||||
|
if (BITGET(dr7.DR7_TYPE[1], 0))
|
||||||
|
BITSET(result, 20);
|
||||||
|
if (BITGET(dr7.DR7_TYPE[1], 1))
|
||||||
|
BITSET(result, 21);
|
||||||
|
if (BITGET(dr7.DR7_SIZE[1], 0))
|
||||||
|
BITSET(result, 22);
|
||||||
|
if (BITGET(dr7.DR7_SIZE[1], 1))
|
||||||
|
BITSET(result, 23);
|
||||||
|
if (BITGET(dr7.DR7_TYPE[2], 0))
|
||||||
|
BITSET(result, 24);
|
||||||
|
if (BITGET(dr7.DR7_TYPE[2], 1))
|
||||||
|
BITSET(result, 25);
|
||||||
|
if (BITGET(dr7.DR7_SIZE[2], 0))
|
||||||
|
BITSET(result, 26);
|
||||||
|
if (BITGET(dr7.DR7_SIZE[2], 1))
|
||||||
|
BITSET(result, 27);
|
||||||
|
if (BITGET(dr7.DR7_TYPE[3], 0))
|
||||||
|
BITSET(result, 28);
|
||||||
|
if (BITGET(dr7.DR7_TYPE[3], 1))
|
||||||
|
BITSET(result, 29);
|
||||||
|
if (BITGET(dr7.DR7_SIZE[3], 0))
|
||||||
|
BITSET(result, 30);
|
||||||
|
if (BITGET(dr7.DR7_SIZE[3], 1))
|
||||||
|
BITSET(result, 31);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline DR7 ptr_dr7(ptr dr7)
|
||||||
|
{
|
||||||
|
DR7 result;
|
||||||
|
memset(&result, 0, sizeof(DR7));
|
||||||
|
if (BITGET(dr7, 0))
|
||||||
|
BITSET(result.DR7_MODE[0], 0);
|
||||||
|
if (BITGET(dr7, 1))
|
||||||
|
BITSET(result.DR7_MODE[0], 1);
|
||||||
|
if (BITGET(dr7, 2))
|
||||||
|
BITSET(result.DR7_MODE[1], 0);
|
||||||
|
if (BITGET(dr7, 3))
|
||||||
|
BITSET(result.DR7_MODE[1], 1);
|
||||||
|
if (BITGET(dr7, 4))
|
||||||
|
BITSET(result.DR7_MODE[2], 0);
|
||||||
|
if (BITGET(dr7, 5))
|
||||||
|
BITSET(result.DR7_MODE[2], 1);
|
||||||
|
if (BITGET(dr7, 6))
|
||||||
|
BITSET(result.DR7_MODE[3], 0);
|
||||||
|
if (BITGET(dr7, 7))
|
||||||
|
BITSET(result.DR7_MODE[3], 1);
|
||||||
|
if (BITGET(dr7, 16))
|
||||||
|
BITSET(result.DR7_TYPE[0], 0);
|
||||||
|
if (BITGET(dr7, 17))
|
||||||
|
BITSET(result.DR7_TYPE[0], 1);
|
||||||
|
if (BITGET(dr7, 18))
|
||||||
|
BITSET(result.DR7_SIZE[0], 0);
|
||||||
|
if (BITGET(dr7, 19))
|
||||||
|
BITSET(result.DR7_SIZE[0], 1);
|
||||||
|
if (BITGET(dr7, 20))
|
||||||
|
BITSET(result.DR7_TYPE[1], 0);
|
||||||
|
if (BITGET(dr7, 21))
|
||||||
|
BITSET(result.DR7_TYPE[1], 1);
|
||||||
|
if (BITGET(dr7, 22))
|
||||||
|
BITSET(result.DR7_SIZE[1], 0);
|
||||||
|
if (BITGET(dr7, 23))
|
||||||
|
BITSET(result.DR7_SIZE[1], 1);
|
||||||
|
if (BITGET(dr7, 24))
|
||||||
|
BITSET(result.DR7_TYPE[2], 0);
|
||||||
|
if (BITGET(dr7, 25))
|
||||||
|
BITSET(result.DR7_TYPE[2], 1);
|
||||||
|
if (BITGET(dr7, 26))
|
||||||
|
BITSET(result.DR7_SIZE[2], 0);
|
||||||
|
if (BITGET(dr7, 27))
|
||||||
|
BITSET(result.DR7_SIZE[2], 1);
|
||||||
|
if (BITGET(dr7, 28))
|
||||||
|
BITSET(result.DR7_TYPE[3], 0);
|
||||||
|
if (BITGET(dr7, 29))
|
||||||
|
BITSET(result.DR7_TYPE[3], 1);
|
||||||
|
if (BITGET(dr7, 30))
|
||||||
|
BITSET(result.DR7_SIZE[3], 0);
|
||||||
|
if (BITGET(dr7, 31))
|
||||||
|
BITSET(result.DR7_SIZE[3], 1);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline DR7_SIZE size_dr7(HardwareBreakpointSize size)
|
||||||
|
{
|
||||||
|
switch (size)
|
||||||
|
{
|
||||||
|
case HardwareBreakpointSize::SizeByte:
|
||||||
|
return SIZE_1;
|
||||||
|
case HardwareBreakpointSize::SizeWord:
|
||||||
|
return SIZE_2;
|
||||||
|
case HardwareBreakpointSize::SizeDword:
|
||||||
|
return SIZE_4;
|
||||||
|
#ifdef _WIN64
|
||||||
|
case HardwareBreakpointSize::SizeQword:
|
||||||
|
return SIZE_8;
|
||||||
|
#endif //_WIN64
|
||||||
|
default:
|
||||||
|
return SIZE_1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline DR7_TYPE type_dr7(HardwareBreakpointType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case HardwareBreakpointType::Access:
|
||||||
|
return TYPE_READWRITE;
|
||||||
|
case HardwareBreakpointType::Write:
|
||||||
|
return TYPE_WRITE;
|
||||||
|
case HardwareBreakpointType::Execute:
|
||||||
|
return TYPE_EXECUTE;
|
||||||
|
default:
|
||||||
|
return TYPE_EXECUTE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ThreadInfo::SetHardwareBreakpoint(ptr address, HardwareBreakpointSlot slot, HardwareBreakpointType type, HardwareBreakpointSize size)
|
||||||
|
{
|
||||||
|
//check if the alignment is correct
|
||||||
|
if ((address % int(size) != 0))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
//set the address register
|
||||||
|
switch (slot)
|
||||||
|
{
|
||||||
|
case HardwareBreakpointSlot::Dr0:
|
||||||
|
registers.Dr0 = address;
|
||||||
|
break;
|
||||||
|
case HardwareBreakpointSlot::Dr1:
|
||||||
|
registers.Dr1 = address;
|
||||||
|
break;
|
||||||
|
case HardwareBreakpointSlot::Dr2:
|
||||||
|
registers.Dr2 = address;
|
||||||
|
break;
|
||||||
|
case HardwareBreakpointSlot::Dr3:
|
||||||
|
registers.Dr3 = address;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//set the Dr7 register
|
||||||
|
auto dr7 = ptr_dr7(registers.Dr7());
|
||||||
|
auto index = int(slot);
|
||||||
|
dr7.DR7_MODE[index] = MODE_LOCAL;
|
||||||
|
dr7.DR7_SIZE[index] = size_dr7(size);
|
||||||
|
dr7.DR7_TYPE[index] = type_dr7(type);
|
||||||
|
registers.Dr7 = dr7_ptr(dr7);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ThreadInfo::DeleteHardwareBreakpoint(HardwareBreakpointSlot slot)
|
||||||
|
{
|
||||||
|
//zero the address register
|
||||||
|
switch (slot)
|
||||||
|
{
|
||||||
|
case HardwareBreakpointSlot::Dr0:
|
||||||
|
registers.Dr0 = 0;
|
||||||
|
break;
|
||||||
|
case HardwareBreakpointSlot::Dr1:
|
||||||
|
registers.Dr1 = 0;
|
||||||
|
break;
|
||||||
|
case HardwareBreakpointSlot::Dr2:
|
||||||
|
registers.Dr2 = 0;
|
||||||
|
break;
|
||||||
|
case HardwareBreakpointSlot::Dr3:
|
||||||
|
registers.Dr3 = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//set the Dr7 register
|
||||||
|
auto dr7 = ptr_dr7(registers.Dr7());
|
||||||
|
auto index = int(slot);
|
||||||
|
dr7.DR7_MODE[index] = MODE_DISABLED;
|
||||||
|
dr7.DR7_SIZE[index] = SIZE_1;
|
||||||
|
dr7.DR7_TYPE[index] = TYPE_EXECUTE;
|
||||||
|
registers.Dr7 = dr7_ptr(dr7);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "Debugger.Global.h"
|
#include "Debugger.Global.h"
|
||||||
#include "Debugger.Thread.Registers.h"
|
#include "Debugger.Thread.Registers.h"
|
||||||
|
#include "Debugger.Breakpoint.h"
|
||||||
|
|
||||||
namespace GleeBug
|
namespace GleeBug
|
||||||
{
|
{
|
||||||
|
|
@ -99,6 +100,23 @@ namespace GleeBug
|
||||||
StepInternal(std::bind(callback, debugger));
|
StepInternal(std::bind(callback, debugger));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\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, HardwareBreakpointSize size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Deletes a hardware breakpoint.
|
||||||
|
\param slot The slot to remove the hardware breakpoint from.
|
||||||
|
\return true if the hardware breakpoint was deleted, false otherwise.
|
||||||
|
*/
|
||||||
|
bool DeleteHardwareBreakpoint(HardwareBreakpointSlot slot);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CONTEXT _oldContext;
|
CONTEXT _oldContext;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,12 @@ namespace GleeBug
|
||||||
*/
|
*/
|
||||||
virtual void cbRipEvent(const RIP_INFO & rip) {};
|
virtual void cbRipEvent(const RIP_INFO & rip) {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Unknown event callback. Called before the event is internally processed. Provide an implementation to use this callback.
|
||||||
|
\param debugEventCode The debug event code.
|
||||||
|
*/
|
||||||
|
virtual void cbUnknownEvent(DWORD debugEventCode) {};
|
||||||
|
|
||||||
protected: //other callbacks
|
protected: //other callbacks
|
||||||
/**
|
/**
|
||||||
\brief Internal error callback. Provide an implementation to use this callback.
|
\brief Internal error callback. Provide an implementation to use this callback.
|
||||||
|
|
@ -191,6 +197,12 @@ namespace GleeBug
|
||||||
*/
|
*/
|
||||||
virtual void ripEvent(const RIP_INFO & rip);
|
virtual void ripEvent(const RIP_INFO & rip);
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Unknown event. Do not override this unless you know what you are doing!
|
||||||
|
\param debugEventCode The debug event code.
|
||||||
|
*/
|
||||||
|
virtual void unknownEvent(DWORD debugEventCode);
|
||||||
|
|
||||||
protected: //core exception handlers
|
protected: //core exception handlers
|
||||||
/**
|
/**
|
||||||
\brief Breakpoint exception handler. Do not override this unless you know what you are doing!
|
\brief Breakpoint exception handler. Do not override this unless you know what you are doing!
|
||||||
|
|
@ -206,6 +218,12 @@ namespace GleeBug
|
||||||
*/
|
*/
|
||||||
virtual void exceptionSingleStep(const EXCEPTION_RECORD & exceptionRecord, bool firstChance);
|
virtual void exceptionSingleStep(const EXCEPTION_RECORD & exceptionRecord, bool firstChance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
\brief Hardware breakpoint (single step) exception handler. Do not override this unless you know what you are doing!
|
||||||
|
\param exceptionAddress The exception address.
|
||||||
|
*/
|
||||||
|
virtual void exceptionHardwareBreakpoint(ptr exceptionAddress);
|
||||||
|
|
||||||
protected: //variables
|
protected: //variables
|
||||||
PROCESS_INFORMATION _mainProcess;
|
PROCESS_INFORMATION _mainProcess;
|
||||||
uint32 _continueStatus;
|
uint32 _continueStatus;
|
||||||
|
|
|
||||||
|
|
@ -155,10 +155,12 @@
|
||||||
<ClCompile Include="Debugger.Loop.Process.cpp" />
|
<ClCompile Include="Debugger.Loop.Process.cpp" />
|
||||||
<ClCompile Include="Debugger.Loop.Rip.cpp" />
|
<ClCompile Include="Debugger.Loop.Rip.cpp" />
|
||||||
<ClCompile Include="Debugger.Loop.Thread.cpp" />
|
<ClCompile Include="Debugger.Loop.Thread.cpp" />
|
||||||
|
<ClCompile Include="Debugger.Loop.Unknown.cpp" />
|
||||||
<ClCompile Include="Debugger.Process.Breakpoint.cpp" />
|
<ClCompile Include="Debugger.Process.Breakpoint.cpp" />
|
||||||
<ClCompile Include="Debugger.Process.cpp" />
|
<ClCompile Include="Debugger.Process.cpp" />
|
||||||
<ClCompile Include="Debugger.Process.Memory.cpp" />
|
<ClCompile Include="Debugger.Process.Memory.cpp" />
|
||||||
<ClCompile Include="Debugger.Thread.cpp" />
|
<ClCompile Include="Debugger.Thread.cpp" />
|
||||||
|
<ClCompile Include="Debugger.Thread.HardwareBreakpoint.cpp" />
|
||||||
<ClCompile Include="Debugger.Thread.Registers.cpp" />
|
<ClCompile Include="Debugger.Thread.Registers.cpp" />
|
||||||
<ClCompile Include="Debugger.Thread.Registers.GetSet.cpp" />
|
<ClCompile Include="Debugger.Thread.Registers.GetSet.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,12 @@
|
||||||
<ClCompile Include="Debugger.Process.Breakpoint.cpp">
|
<ClCompile Include="Debugger.Process.Breakpoint.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="Debugger.Loop.Unknown.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="Debugger.Thread.HardwareBreakpoint.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="Debugger.h">
|
<ClInclude Include="Debugger.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
|
void cbCreateProcessEvent(const CREATE_PROCESS_DEBUG_INFO & createProcess, const ProcessInfo & process) override
|
||||||
{
|
{
|
||||||
ptr entry = ptr(createProcess.lpStartAddress);
|
ptr entry = ptr(createProcess.lpStartAddress);
|
||||||
printf("Process %d created with entry 0x%p\n",
|
printf("Process %d created with entry 0x%p\n",
|
||||||
_debugEvent.dwProcessId,
|
_debugEvent.dwProcessId,
|
||||||
entry);
|
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);
|
printf("Breakpoint set at 0x%p!\n", entry);
|
||||||
else
|
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
|
void cbExitProcessEvent(const EXIT_PROCESS_DEBUG_INFO & exitProcess, const ProcessInfo & process) override
|
||||||
|
|
@ -96,7 +118,7 @@ protected:
|
||||||
void cbSystemBreakpoint() override
|
void cbSystemBreakpoint() override
|
||||||
{
|
{
|
||||||
printf("System breakpoint reached, GIP: 0x%p\n",
|
printf("System breakpoint reached, GIP: 0x%p\n",
|
||||||
_registers->Gip.Get());
|
_registers->Gip());
|
||||||
_thread->StepInto(this, &MyDebugger::cbStepSystem);
|
_thread->StepInto(this, &MyDebugger::cbStepSystem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,6 +133,15 @@ protected:
|
||||||
printf("Breakpoint on 0x%p!\n",
|
printf("Breakpoint on 0x%p!\n",
|
||||||
info.address);
|
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
|
#endif //_MYDEBUGGER_H
|
||||||
Loading…
Reference in New Issue