Skip to content
Merged
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
25 changes: 25 additions & 0 deletions api/debuggerapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -358,13 +358,26 @@ namespace BinaryNinjaDebuggerAPI {
};


// 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
};


struct DebugBreakpoint
{
std::string module;
uint64_t offset;
uint64_t address;
bool enabled;
std::string condition;
DebugBreakpointType type = SoftwareBreakpoint;
size_t size = 1; // Size in bytes for hardware breakpoints/watchpoints (1, 2, 4, 8)
};


Expand Down Expand Up @@ -735,6 +748,18 @@ namespace BinaryNinjaDebuggerAPI {
std::string GetBreakpointCondition(uint64_t address);
std::string GetBreakpointCondition(const ModuleNameAndOffset& address);

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

// Hardware breakpoint and watchpoint support - module+offset (ASLR-safe)
bool AddHardwareBreakpoint(const ModuleNameAndOffset& location, DebugBreakpointType type, size_t size = 1);
bool RemoveHardwareBreakpoint(const ModuleNameAndOffset& location, DebugBreakpointType type, size_t size = 1);
bool EnableHardwareBreakpoint(const ModuleNameAndOffset& location, DebugBreakpointType type, size_t size = 1);
bool DisableHardwareBreakpoint(const ModuleNameAndOffset& location, DebugBreakpointType type, size_t size = 1);

uint64_t IP();
uint64_t GetLastIP();
bool SetIP(uint64_t address);
Expand Down
52 changes: 52 additions & 0 deletions api/debuggercontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,8 @@ std::vector<DebugBreakpoint> DebuggerController::GetBreakpoints()
bp.address = breakpoints[i].address;
bp.enabled = breakpoints[i].enabled;
bp.condition = breakpoints[i].condition ? breakpoints[i].condition : "";
bp.type = (DebugBreakpointType)breakpoints[i].type;
bp.size = breakpoints[i].size;
result[i] = bp;
}

Expand Down Expand Up @@ -831,6 +833,56 @@ std::string DebuggerController::GetBreakpointCondition(const ModuleNameAndOffset
}


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);
}


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


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


// Hardware breakpoint methods - module+offset (ASLR-safe)

bool DebuggerController::AddHardwareBreakpoint(const ModuleNameAndOffset& location, DebugBreakpointType type, size_t size)
{
return BNDebuggerAddRelativeHardwareBreakpoint(m_object, location.module.c_str(), location.offset, (BNDebugBreakpointType)type, size);
}


bool DebuggerController::RemoveHardwareBreakpoint(const ModuleNameAndOffset& location, DebugBreakpointType type, size_t size)
{
return BNDebuggerRemoveRelativeHardwareBreakpoint(m_object, location.module.c_str(), location.offset, (BNDebugBreakpointType)type, size);
}


bool DebuggerController::EnableHardwareBreakpoint(const ModuleNameAndOffset& location, DebugBreakpointType type, size_t size)
{
return BNDebuggerEnableRelativeHardwareBreakpoint(m_object, location.module.c_str(), location.offset, (BNDebugBreakpointType)type, size);
}


bool DebuggerController::DisableHardwareBreakpoint(const ModuleNameAndOffset& location, DebugBreakpointType type, size_t size)
{
return BNDebuggerDisableRelativeHardwareBreakpoint(m_object, location.module.c_str(), location.offset, (BNDebugBreakpointType)type, size);
}


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


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 struct BNDebugBreakpoint
{
// TODO: we should add an absolute address to this, along with a boolean telling whether it is valid
Expand All @@ -134,6 +144,8 @@ extern "C"
uint64_t address;
bool enabled;
char* condition; // NULL if no condition
BNDebugBreakpointType type;
size_t size; // Size in bytes for hardware breakpoints/watchpoints (1, 2, 4, 8)
} BNDebugBreakpoint;


Expand Down Expand Up @@ -250,16 +262,8 @@ extern "C"
TargetExitedEventType,
DetachedEventType,

AbsoluteBreakpointAddedEvent,
RelativeBreakpointAddedEvent,
AbsoluteBreakpointRemovedEvent,
RelativeBreakpointRemovedEvent,
AbsoluteBreakpointEnabledEvent,
RelativeBreakpointEnabledEvent,
AbsoluteBreakpointDisabledEvent,
RelativeBreakpointDisabledEvent,
AbsoluteBreakpointConditionChangedEvent,
RelativeBreakpointConditionChangedEvent,
// Unified breakpoint change event - use this for all breakpoint changes (add/remove/enable/disable)
BreakpointChangedEvent,

ActiveThreadChangedEvent,

Expand Down Expand Up @@ -610,6 +614,26 @@ extern "C"
DEBUGGER_FFI_API char* BNDebuggerGetBreakpointConditionRelative(
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 bool BNDebuggerEnableHardwareBreakpoint(BNDebuggerController* controller, uint64_t address,
BNDebugBreakpointType type, size_t size);
DEBUGGER_FFI_API bool BNDebuggerDisableHardwareBreakpoint(BNDebuggerController* controller, uint64_t address,
BNDebugBreakpointType type, size_t size);

// Hardware breakpoint methods - module+offset (ASLR-safe)
DEBUGGER_FFI_API bool BNDebuggerAddRelativeHardwareBreakpoint(BNDebuggerController* controller, const char* module,
uint64_t offset, BNDebugBreakpointType type, size_t size);
DEBUGGER_FFI_API bool BNDebuggerRemoveRelativeHardwareBreakpoint(BNDebuggerController* controller, const char* module,
uint64_t offset, BNDebugBreakpointType type, size_t size);
DEBUGGER_FFI_API bool BNDebuggerEnableRelativeHardwareBreakpoint(BNDebuggerController* controller, const char* module,
uint64_t offset, BNDebugBreakpointType type, size_t size);
DEBUGGER_FFI_API bool BNDebuggerDisableRelativeHardwareBreakpoint(BNDebuggerController* controller, const char* module,
uint64_t offset, 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
97 changes: 69 additions & 28 deletions api/python/debuggercontroller.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import ctypes
import traceback
from dataclasses import dataclass

import binaryninja
# import debugger
Expand Down Expand Up @@ -362,6 +363,7 @@ def __iter__(self):
return iter(self.breakpoints)


@dataclass(frozen=True)
class DebugBreakpoint:
"""
DebugBreakpoint represents a breakpoint in the target. It has the following fields:
Expand All @@ -371,39 +373,37 @@ class DebugBreakpoint:
* ``address``: the absolute address of the breakpoint
* ``enabled``: whether the breakpoint is enabled (read-only)
* ``condition``: the condition expression for the breakpoint (empty if no condition)
* ``type``: the type of breakpoint (Software, HardwareExecute, HardwareRead, HardwareWrite, HardwareAccess)
* ``size``: the size in bytes for hardware breakpoints/watchpoints (1, 2, 4, or 8)

"""
def __init__(self, module, offset, address, enabled, condition=""):
self.module = module
self.offset = offset
self.address = address
self.enabled = enabled
self.condition = condition

def __eq__(self, other):
if not isinstance(other, self.__class__):
return NotImplemented
return self.module == other.module and self.offset == other.offset and self.address == other.address \
and self.enabled == other.enabled

def __ne__(self, other):
if not isinstance(other, self.__class__):
return NotImplemented
return not (self == other)

def __hash__(self):
return hash((self.module, self.offset, self.address, self.enabled))

def __setattr__(self, name, value):
try:
object.__setattr__(self, name, value)
except AttributeError:
raise AttributeError(f"attribute '{name}' is read only")
module: str
offset: int
address: int
enabled: bool
condition: str = ""
type: DebugBreakpointType = DebugBreakpointType.BNSoftwareBreakpoint
size: int = 1

def __repr__(self):
status = "enabled" if self.enabled else "disabled"
cond_str = f", condition='{self.condition}'" if self.condition else ""
return f"<DebugBreakpoint: {self.module}:{self.offset:#x}, {self.address:#x}, {status}{cond_str}>"

# Get type string (S, HE, HR, HW, HA)
if self.type == DebugBreakpointType.BNSoftwareBreakpoint:
type_str = "S"
elif self.type == DebugBreakpointType.BNHardwareExecuteBreakpoint:
type_str = "HE"
elif self.type == DebugBreakpointType.BNHardwareReadBreakpoint:
type_str = "HR"
elif self.type == DebugBreakpointType.BNHardwareWriteBreakpoint:
type_str = "HW"
elif self.type == DebugBreakpointType.BNHardwareAccessBreakpoint:
type_str = "HA"
else:
type_str = "?"

return f"<DebugBreakpoint: {self.module}:{self.offset:#x}, {self.address:#x}, type={type_str}, {status}{cond_str}>"


class ModuleNameAndOffset:
Expand Down Expand Up @@ -2059,7 +2059,8 @@ def breakpoints(self) -> DebugBreakpoints:
result = []
for i in range(0, count.value):
condition = breakpoints[i].condition if breakpoints[i].condition else ""
bp = DebugBreakpoint(breakpoints[i].module, breakpoints[i].offset, breakpoints[i].address, breakpoints[i].enabled, condition)
bp = DebugBreakpoint(breakpoints[i].module, breakpoints[i].offset, breakpoints[i].address,
breakpoints[i].enabled, condition, breakpoints[i].type, breakpoints[i].size)
result.append(bp)

dbgcore.BNDebuggerFreeBreakpoints(breakpoints, count.value)
Expand Down Expand Up @@ -2097,6 +2098,46 @@ def add_breakpoint(self, address):
else:
raise NotImplementedError

def add_hardware_breakpoint(self, address, bp_type: DebugBreakpointType, size: int = 1) -> bool:
"""
Add a hardware breakpoint

The input can be either an absolute address, or a ModuleNameAndOffset, which specifies a relative address to the
start of a module. The latter is useful for ASLR.

:param address: the address of breakpoint to add
:param bp_type: the type of hardware breakpoint (DebugBreakpointType.BNHardwareExecuteBreakpoint,
BNHardwareReadBreakpoint, BNHardwareWriteBreakpoint, or BNHardwareAccessBreakpoint)
:param size: the size in bytes for the watchpoint (1, 2, 4, or 8)
:return: True if successful, False otherwise
"""
if isinstance(address, int):
return dbgcore.BNDebuggerAddHardwareBreakpoint(self.handle, address, bp_type, size)
elif isinstance(address, ModuleNameAndOffset):
return dbgcore.BNDebuggerAddRelativeHardwareBreakpoint(self.handle, address.module, address.offset, bp_type, size)
else:
raise NotImplementedError

def delete_hardware_breakpoint(self, address, bp_type: DebugBreakpointType, size: int = 1) -> bool:
"""
Delete a hardware breakpoint

The input can be either an absolute address, or a ModuleNameAndOffset, which specifies a relative address to the
start of a module. The latter is useful for ASLR.

:param address: the address of breakpoint to delete
:param bp_type: the type of hardware breakpoint (DebugBreakpointType.BNHardwareExecuteBreakpoint,
BNHardwareReadBreakpoint, BNHardwareWriteBreakpoint, or BNHardwareAccessBreakpoint)
:param size: the size in bytes for the watchpoint (1, 2, 4, or 8)
:return: True if successful, False otherwise
"""
if isinstance(address, int):
return dbgcore.BNDebuggerRemoveHardwareBreakpoint(self.handle, address, bp_type, size)
elif isinstance(address, ModuleNameAndOffset):
return dbgcore.BNDebuggerRemoveRelativeHardwareBreakpoint(self.handle, address.module, address.offset, bp_type, size)
else:
raise NotImplementedError

def has_breakpoint(self, address) -> bool:
"""
Checks whether a breakpoint exists at the specified address
Expand Down
26 changes: 25 additions & 1 deletion cli/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,31 @@ int main(int argc, const char* argv[])
size_t i = 0;
for (const auto& breakpoint : debugger->GetBreakpoints())
{
Log::print(" breakpoint[{}] @ 0x{:X} is {}{}\n", i, breakpoint.address,
// Convert breakpoint type to short string representation
std::string typeStr;
switch (breakpoint.type)
{
case BinaryNinjaDebuggerAPI::SoftwareBreakpoint:
typeStr = "S";
break;
case BinaryNinjaDebuggerAPI::HardwareExecuteBreakpoint:
typeStr = "HE";
break;
case BinaryNinjaDebuggerAPI::HardwareReadBreakpoint:
typeStr = "HR";
break;
case BinaryNinjaDebuggerAPI::HardwareWriteBreakpoint:
typeStr = "HW";
break;
case BinaryNinjaDebuggerAPI::HardwareAccessBreakpoint:
typeStr = "HA";
break;
default:
typeStr = "?";
break;
}

Log::print(" breakpoint[{}] @ 0x{:X} type={} is {}{}\n", i, breakpoint.address, typeStr,
breakpoint.enabled ? Log::Style(0, 255, 0) : Log::Style(255, 0, 0),
breakpoint.enabled ? "active" : "inactive");
i++;
Expand Down
Loading