Skip to content

Commit 4e7756c

Browse files
authored
Support enabling and disabling breakpoints (#784)
1 parent 9c73bb6 commit 4e7756c

16 files changed

+649
-80
lines changed

api/debuggerapi.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,10 @@ namespace BinaryNinjaDebuggerAPI {
719719
void DeleteBreakpoint(const ModuleNameAndOffset& breakpoint);
720720
void AddBreakpoint(uint64_t address);
721721
void AddBreakpoint(const ModuleNameAndOffset& breakpoint);
722+
void EnableBreakpoint(uint64_t address);
723+
void EnableBreakpoint(const ModuleNameAndOffset& breakpoint);
724+
void DisableBreakpoint(uint64_t address);
725+
void DisableBreakpoint(const ModuleNameAndOffset& breakpoint);
722726
bool ContainsBreakpoint(uint64_t address);
723727
bool ContainsBreakpoint(const ModuleNameAndOffset& breakpoint);
724728

api/debuggercontroller.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,30 @@ void DebuggerController::AddBreakpoint(const ModuleNameAndOffset& breakpoint)
760760
}
761761

762762

763+
void DebuggerController::EnableBreakpoint(uint64_t address)
764+
{
765+
BNDebuggerEnableAbsoluteBreakpoint(m_object, address);
766+
}
767+
768+
769+
void DebuggerController::EnableBreakpoint(const ModuleNameAndOffset& breakpoint)
770+
{
771+
BNDebuggerEnableRelativeBreakpoint(m_object, breakpoint.module.c_str(), breakpoint.offset);
772+
}
773+
774+
775+
void DebuggerController::DisableBreakpoint(uint64_t address)
776+
{
777+
BNDebuggerDisableAbsoluteBreakpoint(m_object, address);
778+
}
779+
780+
781+
void DebuggerController::DisableBreakpoint(const ModuleNameAndOffset& breakpoint)
782+
{
783+
BNDebuggerDisableRelativeBreakpoint(m_object, breakpoint.module.c_str(), breakpoint.offset);
784+
}
785+
786+
763787
bool DebuggerController::ContainsBreakpoint(uint64_t address)
764788
{
765789
return BNDebuggerContainsAbsoluteBreakpoint(m_object, address);

api/ffi.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,10 @@ extern "C"
252252
RelativeBreakpointAddedEvent,
253253
AbsoluteBreakpointRemovedEvent,
254254
RelativeBreakpointRemovedEvent,
255+
AbsoluteBreakpointEnabledEvent,
256+
RelativeBreakpointEnabledEvent,
257+
AbsoluteBreakpointDisabledEvent,
258+
RelativeBreakpointDisabledEvent,
255259

256260
ActiveThreadChangedEvent,
257261

@@ -583,6 +587,12 @@ extern "C"
583587
DEBUGGER_FFI_API void BNDebuggerAddAbsoluteBreakpoint(BNDebuggerController* controller, uint64_t address);
584588
DEBUGGER_FFI_API void BNDebuggerAddRelativeBreakpoint(
585589
BNDebuggerController* controller, const char* module, uint64_t offset);
590+
DEBUGGER_FFI_API void BNDebuggerEnableAbsoluteBreakpoint(BNDebuggerController* controller, uint64_t address);
591+
DEBUGGER_FFI_API void BNDebuggerEnableRelativeBreakpoint(
592+
BNDebuggerController* controller, const char* module, uint64_t offset);
593+
DEBUGGER_FFI_API void BNDebuggerDisableAbsoluteBreakpoint(BNDebuggerController* controller, uint64_t address);
594+
DEBUGGER_FFI_API void BNDebuggerDisableRelativeBreakpoint(
595+
BNDebuggerController* controller, const char* module, uint64_t offset);
586596
DEBUGGER_FFI_API bool BNDebuggerContainsAbsoluteBreakpoint(BNDebuggerController* controller, uint64_t address);
587597
DEBUGGER_FFI_API bool BNDebuggerContainsRelativeBreakpoint(
588598
BNDebuggerController* controller, const char* module, uint64_t offset);

api/python/debuggercontroller.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ class DebugBreakpoint:
260260
* ``module``: the name of the module for which the breakpoint is in
261261
* ``offset``: the offset of the breakpoint to the start of the module
262262
* ``address``: the absolute address of the breakpoint
263-
* ``enabled``: not used
263+
* ``enabled``: whether the breakpoint is enabled (read-only)
264264
265265
"""
266266
def __init__(self, module, offset, address, enabled):
@@ -281,7 +281,7 @@ def __ne__(self, other):
281281
return not (self == other)
282282

283283
def __hash__(self):
284-
return hash((self.module, self.offset, self.address. self.enabled))
284+
return hash((self.module, self.offset, self.address, self.enabled))
285285

286286
def __setattr__(self, name, value):
287287
try:
@@ -290,7 +290,8 @@ def __setattr__(self, name, value):
290290
raise AttributeError(f"attribute '{name}' is read only")
291291

292292
def __repr__(self):
293-
return f"<DebugBreakpoint: {self.module}:{self.offset:#x}, {self.address:#x}>"
293+
status = "enabled" if self.enabled else "disabled"
294+
return f"<DebugBreakpoint: {self.module}:{self.offset:#x}, {self.address:#x}, {status}>"
294295

295296

296297
class ModuleNameAndOffset:
@@ -1958,6 +1959,38 @@ def has_breakpoint(self, address) -> bool:
19581959
else:
19591960
raise NotImplementedError
19601961

1962+
def enable_breakpoint(self, address):
1963+
"""
1964+
Enable a breakpoint
1965+
1966+
The input can be either an absolute address, or a ModuleNameAndOffset, which specifies a relative address to the
1967+
start of a module. The latter is useful for ASLR.
1968+
1969+
:param address: the address of breakpoint to enable
1970+
"""
1971+
if isinstance(address, int):
1972+
dbgcore.BNDebuggerEnableAbsoluteBreakpoint(self.handle, address)
1973+
elif isinstance(address, ModuleNameAndOffset):
1974+
dbgcore.BNDebuggerEnableRelativeBreakpoint(self.handle, address.module, address.offset)
1975+
else:
1976+
raise NotImplementedError
1977+
1978+
def disable_breakpoint(self, address):
1979+
"""
1980+
Disable a breakpoint
1981+
1982+
The input can be either an absolute address, or a ModuleNameAndOffset, which specifies a relative address to the
1983+
start of a module. The latter is useful for ASLR.
1984+
1985+
:param address: the address of breakpoint to disable
1986+
"""
1987+
if isinstance(address, int):
1988+
dbgcore.BNDebuggerDisableAbsoluteBreakpoint(self.handle, address)
1989+
elif isinstance(address, ModuleNameAndOffset):
1990+
dbgcore.BNDebuggerDisableRelativeBreakpoint(self.handle, address.module, address.offset)
1991+
else:
1992+
raise NotImplementedError
1993+
19611994
@property
19621995
def ip(self) -> int:
19631996
"""

core/debugadapter.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,14 @@ namespace BinaryNinjaDebugger {
264264

265265
virtual bool RemoveBreakpoint(const ModuleNameAndOffset& address) { return false; }
266266

267+
virtual bool EnableBreakpoint(const std::uintptr_t address) { return false; }
268+
269+
virtual bool EnableBreakpoint(const ModuleNameAndOffset& address) { return false; }
270+
271+
virtual bool DisableBreakpoint(const std::uintptr_t address) { return false; }
272+
273+
virtual bool DisableBreakpoint(const ModuleNameAndOffset& address) { return false; }
274+
267275
virtual std::vector<DebugBreakpoint> GetBreakpointList() const = 0;
268276

269277
virtual std::unordered_map<std::string, DebugRegister> ReadAllRegisters() = 0;

core/debuggercontroller.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,46 @@ void DebuggerController::DeleteBreakpoint(const ModuleNameAndOffset& address)
100100
}
101101

102102

103+
void DebuggerController::EnableBreakpoint(uint64_t address)
104+
{
105+
m_state->EnableBreakpoint(address);
106+
DebuggerEvent event;
107+
event.type = AbsoluteBreakpointEnabledEvent;
108+
event.data.absoluteAddress = address;
109+
PostDebuggerEvent(event);
110+
}
111+
112+
113+
void DebuggerController::EnableBreakpoint(const ModuleNameAndOffset& address)
114+
{
115+
m_state->EnableBreakpoint(address);
116+
DebuggerEvent event;
117+
event.type = RelativeBreakpointEnabledEvent;
118+
event.data.relativeAddress = address;
119+
PostDebuggerEvent(event);
120+
}
121+
122+
123+
void DebuggerController::DisableBreakpoint(uint64_t address)
124+
{
125+
m_state->DisableBreakpoint(address);
126+
DebuggerEvent event;
127+
event.type = AbsoluteBreakpointDisabledEvent;
128+
event.data.absoluteAddress = address;
129+
PostDebuggerEvent(event);
130+
}
131+
132+
133+
void DebuggerController::DisableBreakpoint(const ModuleNameAndOffset& address)
134+
{
135+
m_state->DisableBreakpoint(address);
136+
DebuggerEvent event;
137+
event.type = RelativeBreakpointDisabledEvent;
138+
event.data.relativeAddress = address;
139+
PostDebuggerEvent(event);
140+
}
141+
142+
103143
bool DebuggerController::SetIP(uint64_t address)
104144
{
105145
std::string ipRegisterName;

core/debuggercontroller.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,10 @@ namespace BinaryNinjaDebugger {
227227
void AddBreakpoint(const ModuleNameAndOffset& address);
228228
void DeleteBreakpoint(uint64_t address);
229229
void DeleteBreakpoint(const ModuleNameAndOffset& address);
230+
void EnableBreakpoint(uint64_t address);
231+
void EnableBreakpoint(const ModuleNameAndOffset& address);
232+
void DisableBreakpoint(uint64_t address);
233+
void DisableBreakpoint(const ModuleNameAndOffset& address);
230234
DebugBreakpoint GetAllBreakpoints();
231235

232236
// registers

core/debuggerstate.cpp

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,7 @@ bool DebuggerBreakpoints::AddAbsolute(uint64_t remoteAddress)
534534
{
535535
ModuleNameAndOffset info = m_state->GetModules()->AbsoluteAddressToRelative(remoteAddress);
536536
m_breakpoints.push_back(info);
537+
m_enabledState[info] = true; // Enable by default
537538
SerializeMetadata();
538539
}
539540

@@ -546,6 +547,7 @@ bool DebuggerBreakpoints::AddOffset(const ModuleNameAndOffset& address)
546547
if (!ContainsOffset(address))
547548
{
548549
m_breakpoints.push_back(address);
550+
m_enabledState[address] = true; // Enable by default
549551
SerializeMetadata();
550552

551553
// If the adapter is already created, we ask it to add the breakpoint.
@@ -574,6 +576,7 @@ bool DebuggerBreakpoints::RemoveAbsolute(uint64_t remoteAddress)
574576
{
575577
m_breakpoints.erase(iter);
576578
}
579+
m_enabledState.erase(info); // Remove enabled state
577580
SerializeMetadata();
578581
m_state->GetAdapter()->RemoveBreakpoint(remoteAddress);
579582
return true;
@@ -589,6 +592,7 @@ bool DebuggerBreakpoints::RemoveOffset(const ModuleNameAndOffset& address)
589592
if (auto iter = std::find(m_breakpoints.begin(), m_breakpoints.end(), address); iter != m_breakpoints.end())
590593
m_breakpoints.erase(iter);
591594

595+
m_enabledState.erase(address); // Remove enabled state
592596
SerializeMetadata();
593597

594598
if (m_state->GetAdapter() && m_state->IsConnected())
@@ -603,6 +607,76 @@ bool DebuggerBreakpoints::RemoveOffset(const ModuleNameAndOffset& address)
603607
}
604608

605609

610+
bool DebuggerBreakpoints::EnableAbsolute(uint64_t remoteAddress)
611+
{
612+
ModuleNameAndOffset info = m_state->GetModules()->AbsoluteAddressToRelative(remoteAddress);
613+
return EnableOffset(info);
614+
}
615+
616+
617+
bool DebuggerBreakpoints::EnableOffset(const ModuleNameAndOffset& address)
618+
{
619+
if (!ContainsOffset(address))
620+
return false;
621+
622+
m_enabledState[address] = true;
623+
SerializeMetadata();
624+
625+
// If connected, make sure the breakpoint is active in the target
626+
if (m_state->GetAdapter() && m_state->IsConnected())
627+
{
628+
uint64_t remoteAddress = m_state->GetModules()->RelativeAddressToAbsolute(address);
629+
m_state->GetAdapter()->AddBreakpoint(remoteAddress);
630+
return true;
631+
}
632+
return true;
633+
}
634+
635+
636+
bool DebuggerBreakpoints::DisableAbsolute(uint64_t remoteAddress)
637+
{
638+
ModuleNameAndOffset info = m_state->GetModules()->AbsoluteAddressToRelative(remoteAddress);
639+
return DisableOffset(info);
640+
}
641+
642+
643+
bool DebuggerBreakpoints::DisableOffset(const ModuleNameAndOffset& address)
644+
{
645+
if (!ContainsOffset(address))
646+
return false;
647+
648+
m_enabledState[address] = false;
649+
SerializeMetadata();
650+
651+
// If connected, remove the breakpoint from the target but keep it in our list
652+
if (m_state->GetAdapter() && m_state->IsConnected())
653+
{
654+
uint64_t remoteAddress = m_state->GetModules()->RelativeAddressToAbsolute(address);
655+
m_state->GetAdapter()->RemoveBreakpoint(remoteAddress);
656+
return true;
657+
}
658+
return true;
659+
}
660+
661+
662+
bool DebuggerBreakpoints::IsEnabledAbsolute(uint64_t address)
663+
{
664+
ModuleNameAndOffset info = m_state->GetModules()->AbsoluteAddressToRelative(address);
665+
return IsEnabledOffset(info);
666+
}
667+
668+
669+
bool DebuggerBreakpoints::IsEnabledOffset(const ModuleNameAndOffset& address)
670+
{
671+
auto iter = m_enabledState.find(address);
672+
if (iter != m_enabledState.end())
673+
return iter->second;
674+
675+
// Default to enabled if not explicitly set
676+
return true;
677+
}
678+
679+
606680
bool DebuggerBreakpoints::ContainsOffset(const ModuleNameAndOffset& address)
607681
{
608682
// If there is no backend, then only check if the breakpoint is in the list
@@ -980,6 +1054,30 @@ void DebuggerState::DeleteBreakpoint(const ModuleNameAndOffset& address)
9801054
}
9811055

9821056

1057+
void DebuggerState::EnableBreakpoint(uint64_t address)
1058+
{
1059+
m_breakpoints->EnableAbsolute(address);
1060+
}
1061+
1062+
1063+
void DebuggerState::EnableBreakpoint(const ModuleNameAndOffset& address)
1064+
{
1065+
m_breakpoints->EnableOffset(address);
1066+
}
1067+
1068+
1069+
void DebuggerState::DisableBreakpoint(uint64_t address)
1070+
{
1071+
m_breakpoints->DisableAbsolute(address);
1072+
}
1073+
1074+
1075+
void DebuggerState::DisableBreakpoint(const ModuleNameAndOffset& address)
1076+
{
1077+
m_breakpoints->DisableOffset(address);
1078+
}
1079+
1080+
9831081
uint64_t DebuggerState::IP()
9841082
{
9851083
if (!IsConnected())

core/debuggerstate.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ limitations under the License.
1616

1717
#pragma once
1818

19+
#include <unordered_map>
1920
#include "binaryninjaapi.h"
2021
#include "ui/uitypes.h"
2122
#include "debugadaptertype.h"
@@ -82,15 +83,22 @@ namespace BinaryNinjaDebugger {
8283
private:
8384
DebuggerState* m_state;
8485
std::vector<ModuleNameAndOffset> m_breakpoints;
86+
std::map<ModuleNameAndOffset, bool> m_enabledState;
8587

8688
public:
8789
DebuggerBreakpoints(DebuggerState* state, std::vector<ModuleNameAndOffset> initial = {});
8890
bool AddAbsolute(uint64_t remoteAddress);
8991
bool AddOffset(const ModuleNameAndOffset& address);
9092
bool RemoveAbsolute(uint64_t remoteAddress);
9193
bool RemoveOffset(const ModuleNameAndOffset& address);
94+
bool EnableAbsolute(uint64_t remoteAddress);
95+
bool EnableOffset(const ModuleNameAndOffset& address);
96+
bool DisableAbsolute(uint64_t remoteAddress);
97+
bool DisableOffset(const ModuleNameAndOffset& address);
9298
bool ContainsAbsolute(uint64_t address);
9399
bool ContainsOffset(const ModuleNameAndOffset& address);
100+
bool IsEnabledAbsolute(uint64_t address);
101+
bool IsEnabledOffset(const ModuleNameAndOffset& address);
94102
void Apply();
95103
void SerializeMetadata();
96104
void UnserializedMetadata();
@@ -237,6 +245,10 @@ namespace BinaryNinjaDebugger {
237245
void AddBreakpoint(const ModuleNameAndOffset& address);
238246
void DeleteBreakpoint(uint64_t address);
239247
void DeleteBreakpoint(const ModuleNameAndOffset& address);
248+
void EnableBreakpoint(uint64_t address);
249+
void EnableBreakpoint(const ModuleNameAndOffset& address);
250+
void DisableBreakpoint(uint64_t address);
251+
void DisableBreakpoint(const ModuleNameAndOffset& address);
240252

241253
uint64_t IP();
242254
uint64_t StackPointer();

0 commit comments

Comments
 (0)