Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions api/debuggerapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,18 @@ namespace BinaryNinjaDebuggerAPI {
uint64_t offset;
uint64_t address;
bool enabled;
DebugBreakpointType type = SoftwareBreakpoint;
};


// Breakpoint types - used to specify the type of breakpoint to set
enum DebugBreakpointType
{
SoftwareBreakpoint = 0, // Default software breakpoint
HardwareExecuteBreakpoint = 1, // Hardware execution breakpoint
HardwareReadBreakpoint = 2, // Hardware read watchpoint
HardwareWriteBreakpoint = 3, // Hardware write watchpoint
HardwareAccessBreakpoint = 4 // Hardware read/write watchpoint
};


Expand Down Expand Up @@ -647,6 +659,10 @@ namespace BinaryNinjaDebuggerAPI {
bool ContainsBreakpoint(uint64_t address);
bool ContainsBreakpoint(const ModuleNameAndOffset& breakpoint);

// Hardware breakpoint and watchpoint support
bool AddHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size = 1);
bool RemoveHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size = 1);

uint64_t IP();
uint64_t GetLastIP();
bool SetIP(uint64_t address);
Expand Down
12 changes: 12 additions & 0 deletions api/debuggercontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,18 @@ bool DebuggerController::ContainsBreakpoint(const ModuleNameAndOffset& breakpoin
}


bool DebuggerController::AddHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size)
{
return BNDebuggerAddHardwareBreakpoint(m_object, address, (BNDebugBreakpointType)type, size);
}


bool DebuggerController::RemoveHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size)
{
return BNDebuggerRemoveHardwareBreakpoint(m_object, address, (BNDebugBreakpointType)type, size);
}


uint64_t DebuggerController::RelativeAddressToAbsolute(const ModuleNameAndOffset& address)
{
return BNDebuggerRelativeAddressToAbsolute(m_object, address.module.c_str(), address.offset);
Expand Down
16 changes: 16 additions & 0 deletions api/ffi.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,16 @@ extern "C"
} BNDebugAdapterTargetStatus;


typedef enum BNDebugBreakpointType
{
BNSoftwareBreakpoint = 0, // Default software breakpoint
BNHardwareExecuteBreakpoint = 1, // Hardware execution breakpoint
BNHardwareReadBreakpoint = 2, // Hardware read watchpoint
BNHardwareWriteBreakpoint = 3, // Hardware write watchpoint
BNHardwareAccessBreakpoint = 4 // Hardware read/write watchpoint
} BNDebugBreakpointType;


typedef enum BNDebuggerEventType
{
LaunchEventType,
Expand Down Expand Up @@ -522,6 +532,12 @@ extern "C"
DEBUGGER_FFI_API bool BNDebuggerContainsRelativeBreakpoint(
BNDebuggerController* controller, const char* module, uint64_t offset);

// Hardware breakpoint and watchpoint support
DEBUGGER_FFI_API bool BNDebuggerAddHardwareBreakpoint(BNDebuggerController* controller, uint64_t address,
BNDebugBreakpointType type, size_t size);
DEBUGGER_FFI_API bool BNDebuggerRemoveHardwareBreakpoint(BNDebuggerController* controller, uint64_t address,
BNDebugBreakpointType type, size_t size);

DEBUGGER_FFI_API uint64_t BNDebuggerGetIP(BNDebuggerController* controller);
DEBUGGER_FFI_API uint64_t BNDebuggerGetLastIP(BNDebuggerController* controller);
DEBUGGER_FFI_API bool BNDebuggerSetIP(BNDebuggerController* controller, uint64_t address);
Expand Down
Binary file added breakpoint_widget_enhanced.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
93 changes: 92 additions & 1 deletion core/adapters/dbgengadapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -996,6 +996,15 @@ bool DbgEngAdapter::ResumeThread(std::uint32_t tid)

DebugBreakpoint DbgEngAdapter::AddBreakpoint(const std::uintptr_t address, unsigned long breakpoint_flags)
{
// Handle hardware breakpoint types
if (breakpoint_flags != SoftwareBreakpoint)
{
if (AddHardwareBreakpoint(address, (DebugBreakpointType)breakpoint_flags))
return DebugBreakpoint(address, 0, true, (DebugBreakpointType)breakpoint_flags);
else
return DebugBreakpoint{};
}

IDebugBreakpoint2* debug_breakpoint {};

/* attempt to read at breakpoint location to confirm its valid */
Expand All @@ -1021,7 +1030,7 @@ DebugBreakpoint DbgEngAdapter::AddBreakpoint(const std::uintptr_t address, unsig
if (debug_breakpoint->SetFlags(DEBUG_BREAKPOINT_ENABLED | breakpoint_flags) != S_OK)
return {};

const auto new_breakpoint = DebugBreakpoint(address, id, true);
const auto new_breakpoint = DebugBreakpoint(address, id, true, SoftwareBreakpoint);
this->m_debug_breakpoints.push_back(new_breakpoint);

return new_breakpoint;
Expand Down Expand Up @@ -1125,6 +1134,88 @@ std::vector<DebugBreakpoint> DbgEngAdapter::GetBreakpointList() const
return {};
}


bool DbgEngAdapter::AddHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size)
{
if (!m_dbgengInitialized)
return false;

std::string command;
switch (type)
{
case HardwareExecuteBreakpoint:
// ba e<size> <address>: hardware execution breakpoint
command = fmt::format("ba e{} 0x{:x}", size, address);
break;
case HardwareReadBreakpoint:
// ba r<size> <address>: hardware read breakpoint
command = fmt::format("ba r{} 0x{:x}", size, address);
break;
case HardwareWriteBreakpoint:
// ba w<size> <address>: hardware write breakpoint
command = fmt::format("ba w{} 0x{:x}", size, address);
break;
case HardwareAccessBreakpoint:
// ba a<size> <address>: hardware access (read/write) breakpoint
command = fmt::format("ba a{} 0x{:x}", size, address);
break;
default:
return false;
}

// Execute the command and check if it succeeded
auto result = InvokeBackendCommand(command);
// DbgEng typically returns an empty string or specific success message for successful ba commands
// If the command fails, it usually contains an error message
return result.find("error") == std::string::npos && result.find("Error") == std::string::npos;
}


bool DbgEngAdapter::RemoveHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size)
{
if (!m_dbgengInitialized)
return false;

// List all breakpoints to find the ID of the hardware breakpoint at this address
auto result = InvokeBackendCommand("bl");

// Parse the breakpoint list to find the ID
// DbgEng breakpoint list format is typically:
// 0 e <address> <info>
// 1 r <address> <info> etc.
std::stringstream ss(result);
std::string line;

while (std::getline(ss, line))
{
// Look for lines containing our address
if (line.find(fmt::format("{:x}", address)) != std::string::npos)
{
// Extract breakpoint ID (first number in the line)
std::istringstream iss(line);
std::string id_str;
if (iss >> id_str)
{
try
{
int bp_id = std::stoi(id_str);
// Remove the breakpoint using bc (breakpoint clear) command
auto clear_result = InvokeBackendCommand(fmt::format("bc {}", bp_id));
return clear_result.find("error") == std::string::npos &&
clear_result.find("Error") == std::string::npos;
}
catch (...)
{
// Continue searching if this line doesn't contain a valid ID
continue;
}
}
}
}

return false;
}

void DbgEngAdapter::ApplyBreakpoints()
{
for (const auto bp : m_pendingBreakpoints)
Expand Down
4 changes: 4 additions & 0 deletions core/adapters/dbgengadapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,10 @@ namespace BinaryNinjaDebugger {

std::vector<DebugBreakpoint> GetBreakpointList() const override;

// Hardware breakpoint and watchpoint support
bool AddHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size = 1) override;
bool RemoveHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size = 1) override;

std::string GetRegisterNameByIndex(std::uint32_t index) const;
std::unordered_map<std::string, DebugRegister> ReadAllRegisters() override;
DebugRegister ReadRegister(const std::string& reg) override;
Expand Down
79 changes: 74 additions & 5 deletions core/adapters/esrevenadapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,15 @@ DebugBreakpoint EsrevenAdapter::AddBreakpoint(const std::uintptr_t address, unsi
DebugBreakpoint(address)) != this->m_debugBreakpoints.end())
return {};

// Handle hardware breakpoint types
if (breakpoint_type != SoftwareBreakpoint)
{
if (AddHardwareBreakpoint(address, (DebugBreakpointType)breakpoint_type))
return DebugBreakpoint(address, 0, true, (DebugBreakpointType)breakpoint_type);
else
return DebugBreakpoint{};
}

/* TODO: replace %d with the actual breakpoint size as it differs per architecture */
size_t kind = 1;
if (m_remoteArch == "aarch64")
Expand All @@ -421,7 +430,7 @@ DebugBreakpoint EsrevenAdapter::AddBreakpoint(const std::uintptr_t address, unsi
if (this->m_rspConnector->TransmitAndReceive(RspData("Z0,{:x},{}", address, kind)).AsString() != "OK" )
return DebugBreakpoint{};

const auto new_breakpoint = DebugBreakpoint(address, this->m_internalBreakpointId++, true);
const auto new_breakpoint = DebugBreakpoint(address, this->m_internalBreakpointId++, true, SoftwareBreakpoint);
this->m_debugBreakpoints.push_back(new_breakpoint);

return new_breakpoint;
Expand Down Expand Up @@ -1068,20 +1077,80 @@ bool EsrevenAdapter::StepOverReverse()
return status != InternalError;
}

bool EsrevenAdapter::AddHardwareWriteBreakpoint(uint64_t address)
bool EsrevenAdapter::AddHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size)
{
if (m_isTargetRunning || !m_rspConnector)
return false;

return this->m_rspConnector->TransmitAndReceive(RspData("Z2,{:x},{}", address, 1)).AsString() != "OK";
std::string command;
switch (type)
{
case HardwareExecuteBreakpoint:
// Z1 = hardware execution breakpoint
command = fmt::format("Z1,{:x},{}", address, size);
break;
case HardwareReadBreakpoint:
// Z3 = hardware read watchpoint
command = fmt::format("Z3,{:x},{}", address, size);
break;
case HardwareWriteBreakpoint:
// Z2 = hardware write watchpoint
command = fmt::format("Z2,{:x},{}", address, size);
break;
case HardwareAccessBreakpoint:
// Z4 = hardware access watchpoint (read/write)
command = fmt::format("Z4,{:x},{}", address, size);
break;
default:
return false;
}

return m_rspConnector->TransmitAndReceive(RspData(command)).AsString() == "OK";
}

bool EsrevenAdapter::RemoveHardwareWriteBreakpoint(uint64_t address)

bool EsrevenAdapter::RemoveHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size)
{
if (m_isTargetRunning || !m_rspConnector)
return false;

return this->m_rspConnector->TransmitAndReceive(RspData("Z2,{:x},{}", address, 1)).AsString() != "OK";
std::string command;
switch (type)
{
case HardwareExecuteBreakpoint:
// z1 = remove hardware execution breakpoint
command = fmt::format("z1,{:x},{}", address, size);
break;
case HardwareReadBreakpoint:
// z3 = remove hardware read watchpoint
command = fmt::format("z3,{:x},{}", address, size);
break;
case HardwareWriteBreakpoint:
// z2 = remove hardware write watchpoint
command = fmt::format("z2,{:x},{}", address, size);
break;
case HardwareAccessBreakpoint:
// z4 = remove hardware access watchpoint (read/write)
command = fmt::format("z4,{:x},{}", address, size);
break;
default:
return false;
}

return m_rspConnector->TransmitAndReceive(RspData(command)).AsString() == "OK";
}


bool EsrevenAdapter::AddHardwareWriteBreakpoint(uint64_t address)
{
// Delegate to new standardized method
return AddHardwareBreakpoint(address, HardwareWriteBreakpoint, 1);
}

bool EsrevenAdapter::RemoveHardwareWriteBreakpoint(uint64_t address)
{
// Delegate to new standardized method
return RemoveHardwareBreakpoint(address, HardwareWriteBreakpoint, 1);
}

bool EsrevenAdapter::StepReturnReverse()
Expand Down
6 changes: 5 additions & 1 deletion core/adapters/esrevenadapter.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,11 @@ namespace BinaryNinjaDebugger
bool ResumeThread(std::uint32_t tid) override;
DebugBreakpoint AddBreakpoint(const ModuleNameAndOffset& address, unsigned long breakpoint_type = 0) override;

// Temporary internal methods
// Hardware breakpoint and watchpoint support
bool AddHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size = 1) override;
bool RemoveHardwareBreakpoint(uint64_t address, DebugBreakpointType type, size_t size = 1) override;

// Legacy methods - kept for backward compatibility
bool AddHardwareWriteBreakpoint(uint64_t address);
bool RemoveHardwareWriteBreakpoint(uint64_t address);

Expand Down
Loading