Skip to content

Commit 6c85b99

Browse files
authored
Support "Run back to here" in TTD debugging (#781)
1 parent fb9a81e commit 6c85b99

File tree

10 files changed

+184
-0
lines changed

10 files changed

+184
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ core/adapters/LLDB.framework
5858
/test/binaries/Darwin-arm64-signed/
5959
/test/binaries/Darwin-x86_64-signed/
6060
test/__pycache__
61+
api/python/__pycache__
6162

6263
/build
6364
/artifacts

api/debuggerapi.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,8 @@ namespace BinaryNinjaDebuggerAPI {
596596

597597
bool RunTo(uint64_t remoteAddresses);
598598
bool RunTo(const std::vector<uint64_t>& remoteAddresses);
599+
bool RunToReverse(uint64_t remoteAddresses);
600+
bool RunToReverse(const std::vector<uint64_t>& remoteAddresses);
599601
void Pause();
600602

601603
DebugStopReason GoAndWait();
@@ -608,6 +610,8 @@ namespace BinaryNinjaDebuggerAPI {
608610
DebugStopReason StepReturnReverseAndWait();
609611
DebugStopReason RunToAndWait(uint64_t remoteAddresses);
610612
DebugStopReason RunToAndWait(const std::vector<uint64_t>& remoteAddresses);
613+
DebugStopReason RunToReverseAndWait(uint64_t remoteAddresses);
614+
DebugStopReason RunToReverseAndWait(const std::vector<uint64_t>& remoteAddresses);
611615
DebugStopReason PauseAndWait();
612616
DebugStopReason RestartAndWait();
613617

api/debuggercontroller.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,18 @@ bool DebuggerController::RunTo(const std::vector<uint64_t>& remoteAddresses)
467467
}
468468

469469

470+
bool DebuggerController::RunToReverse(uint64_t remoteAddresses)
471+
{
472+
return RunToReverse(std::vector<uint64_t> {remoteAddresses});
473+
}
474+
475+
476+
bool DebuggerController::RunToReverse(const std::vector<uint64_t>& remoteAddresses)
477+
{
478+
return BNDebuggerRunToReverse(m_object, remoteAddresses.data(), remoteAddresses.size());
479+
}
480+
481+
470482
DebugStopReason DebuggerController::StepIntoAndWait(BNFunctionGraphType il)
471483
{
472484
return BNDebuggerStepIntoAndWait(m_object, il);
@@ -514,6 +526,18 @@ DebugStopReason DebuggerController::RunToAndWait(const std::vector<uint64_t>& re
514526
}
515527

516528

529+
DebugStopReason DebuggerController::RunToReverseAndWait(uint64_t remoteAddresses)
530+
{
531+
return RunToReverseAndWait(std::vector<uint64_t> {remoteAddresses});
532+
}
533+
534+
535+
DebugStopReason DebuggerController::RunToReverseAndWait(const std::vector<uint64_t>& remoteAddresses)
536+
{
537+
return BNDebuggerRunToReverseAndWait(m_object, remoteAddresses.data(), remoteAddresses.size());
538+
}
539+
540+
517541
DebugStopReason DebuggerController::PauseAndWait()
518542
{
519543
return BNDebuggerPauseAndWait(m_object);

api/ffi.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,8 @@ extern "C"
461461
DEBUGGER_FFI_API bool BNDebuggerStepReturnReverse(BNDebuggerController* controller);
462462
DEBUGGER_FFI_API bool BNDebuggerRunTo(
463463
BNDebuggerController* controller, const uint64_t* remoteAddresses, size_t count);
464+
DEBUGGER_FFI_API bool BNDebuggerRunToReverse(
465+
BNDebuggerController* controller, const uint64_t* remoteAddresses, size_t count);
464466
DEBUGGER_FFI_API void BNDebuggerPause(BNDebuggerController* controller);
465467

466468
DEBUGGER_FFI_API BNDebugStopReason BNDebuggerGoAndWait(BNDebuggerController* controller);
@@ -478,6 +480,8 @@ extern "C"
478480
DEBUGGER_FFI_API BNDebugStopReason BNDebuggerStepReturnReverseAndWait(BNDebuggerController* controller);
479481
DEBUGGER_FFI_API BNDebugStopReason BNDebuggerRunToAndWait(
480482
BNDebuggerController* controller, const uint64_t* remoteAddresses, size_t count);
483+
DEBUGGER_FFI_API BNDebugStopReason BNDebuggerRunToReverseAndWait(
484+
BNDebuggerController* controller, const uint64_t* remoteAddresses, size_t count);
481485
DEBUGGER_FFI_API BNDebugStopReason BNDebuggerPauseAndWait(BNDebuggerController* controller);
482486
DEBUGGER_FFI_API BNDebugStopReason BNDebuggerRestartAndWait(BNDebuggerController* controller);
483487

api/python/debuggercontroller.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,6 +1047,31 @@ def run_to(self, address) -> bool:
10471047

10481048
return dbgcore.BNDebuggerRunTo(self.handle, addr_list, len(address))
10491049

1050+
def run_to_reverse(self, address) -> bool:
1051+
"""
1052+
Resume the target in reverse, and wait for it to break at the given address(es).
1053+
1054+
The address parameter can be either an integer, or a list of integers.
1055+
1056+
Internally, the debugger places breakpoints on these addresses, resumes the target in reverse, and waits for the target
1057+
to break. Then the debugger removes the added breakpoints.
1058+
1059+
The call is asynchronous and returns before the target stops.
1060+
1061+
:return: whether the operation is successfully requested
1062+
"""
1063+
if isinstance(address, int):
1064+
address = [address]
1065+
1066+
if not isinstance(address, list):
1067+
raise NotImplementedError
1068+
1069+
addr_list = (ctypes.c_uint64 * len(address))()
1070+
for i in range(len(address)):
1071+
addr_list[i] = address[i]
1072+
1073+
return dbgcore.BNDebuggerRunToReverse(self.handle, addr_list, len(address))
1074+
10501075
def go_and_wait(self) -> DebugStopReason:
10511076
"""
10521077
Resume the target.
@@ -1200,6 +1225,31 @@ def run_to_and_wait(self, address) -> DebugStopReason:
12001225

12011226
return DebugStopReason(dbgcore.BNDebuggerRunToAndWait(self.handle, addr_list, len(address)))
12021227

1228+
def run_to_reverse_and_wait(self, address) -> DebugStopReason:
1229+
"""
1230+
Resume the target in reverse, and wait for it to break at the given address(es).
1231+
1232+
The address parameter can be either an integer, or a list of integers.
1233+
1234+
Internally, the debugger places breakpoints on these addresses, resumes the target in reverse, and waits for the target
1235+
to break. Then the debugger removes the added breakpoints.
1236+
1237+
The call is blocking and only returns when the target stops.
1238+
1239+
:return: the reason for the stop
1240+
"""
1241+
if isinstance(address, int):
1242+
address = [address]
1243+
1244+
if not isinstance(address, list):
1245+
raise NotImplementedError
1246+
1247+
addr_list = (ctypes.c_uint64 * len(address))()
1248+
for i in range(len(address)):
1249+
addr_list[i] = address[i]
1250+
1251+
return DebugStopReason(dbgcore.BNDebuggerRunToReverseAndWait(self.handle, addr_list, len(address)))
1252+
12031253
def pause_and_wait(self) -> None:
12041254
"""
12051255
Pause a running target.

core/debuggercontroller.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,6 +1108,33 @@ DebugStopReason DebuggerController::RunToAndWaitInternal(const std::vector<uint6
11081108
}
11091109

11101110

1111+
DebugStopReason DebuggerController::RunToReverseAndWaitInternal(const std::vector<uint64_t>& remoteAddresses)
1112+
{
1113+
m_userRequestedBreak = false;
1114+
1115+
for (uint64_t remoteAddress : remoteAddresses)
1116+
{
1117+
if (!m_state->GetBreakpoints()->ContainsAbsolute(remoteAddress))
1118+
{
1119+
m_adapter->AddBreakpoint(remoteAddress);
1120+
}
1121+
}
1122+
1123+
auto reason = GoReverseAndWaitInternal();
1124+
1125+
for (uint64_t remoteAddress : remoteAddresses)
1126+
{
1127+
if (!m_state->GetBreakpoints()->ContainsAbsolute(remoteAddress))
1128+
{
1129+
m_adapter->RemoveBreakpoint(remoteAddress);
1130+
}
1131+
}
1132+
1133+
NotifyStopped(reason);
1134+
return reason;
1135+
}
1136+
1137+
11111138
bool DebuggerController::RunTo(const std::vector<uint64_t>& remoteAddresses)
11121139
{
11131140
// This is an API function of the debugger. We only do these checks at the API level.
@@ -1120,6 +1147,18 @@ bool DebuggerController::RunTo(const std::vector<uint64_t>& remoteAddresses)
11201147
}
11211148

11221149

1150+
bool DebuggerController::RunToReverse(const std::vector<uint64_t>& remoteAddresses)
1151+
{
1152+
// This is an API function of the debugger. We only do these checks at the API level.
1153+
if (!CanResumeTarget())
1154+
return false;
1155+
1156+
std::thread([&, remoteAddresses]() { RunToReverseAndWait(remoteAddresses); }).detach();
1157+
1158+
return true;
1159+
}
1160+
1161+
11231162
DebugStopReason DebuggerController::RunToAndWait(const std::vector<uint64_t>& remoteAddresses)
11241163
{
11251164
// This is an API function of the debugger. We only do these checks at the API level.
@@ -1138,6 +1177,24 @@ DebugStopReason DebuggerController::RunToAndWait(const std::vector<uint64_t>& re
11381177
}
11391178

11401179

1180+
DebugStopReason DebuggerController::RunToReverseAndWait(const std::vector<uint64_t>& remoteAddresses)
1181+
{
1182+
// This is an API function of the debugger. We only do these checks at the API level.
1183+
if (!CanResumeTarget())
1184+
return InvalidStatusOrOperation;
1185+
1186+
if (!m_targetControlMutex.try_lock())
1187+
return InternalError;
1188+
1189+
auto reason = RunToReverseAndWaitInternal(remoteAddresses);
1190+
if (!m_userRequestedBreak && (reason != ProcessExited) && (reason != InternalError))
1191+
NotifyStopped(reason);
1192+
1193+
m_targetControlMutex.unlock();
1194+
return reason;
1195+
}
1196+
1197+
11411198
bool DebuggerController::CreateDebuggerBinaryView()
11421199
{
11431200
BinaryViewRef data = GetData();

core/debuggercontroller.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ namespace BinaryNinjaDebugger {
156156
DebugStopReason StepReturnAndWaitInternal();
157157
DebugStopReason StepReturnReverseAndWaitInternal();
158158
DebugStopReason RunToAndWaitInternal(const std::vector<uint64_t> &remoteAddresses);
159+
DebugStopReason RunToReverseAndWaitInternal(const std::vector<uint64_t> &remoteAddresses);
159160

160161
// Whether we can start debugging, e.g., launch/attach/connec to a target
161162
bool CanStartDebgging();
@@ -305,6 +306,7 @@ namespace BinaryNinjaDebugger {
305306
bool StepReturn();
306307
bool StepReturnReverse();
307308
bool RunTo(const std::vector<uint64_t>& remoteAddresses);
309+
bool RunToReverse(const std::vector<uint64_t>& remoteAddresses);
308310
bool Pause();
309311

310312
DebugStopReason ExecuteAdapterAndWait(const DebugAdapterOperation operation);
@@ -323,6 +325,7 @@ namespace BinaryNinjaDebugger {
323325
DebugStopReason StepReturnAndWait();
324326
DebugStopReason StepReturnReverseAndWait();
325327
DebugStopReason RunToAndWait(const std::vector<uint64_t>& remoteAddresses);
328+
DebugStopReason RunToReverseAndWait(const std::vector<uint64_t>& remoteAddresses);
326329
DebugStopReason PauseAndWait();
327330
void DetachAndWait();
328331
void QuitAndWait();

core/ffi.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,18 @@ bool BNDebuggerRunTo(BNDebuggerController* controller, const uint64_t* remoteAdd
530530
}
531531

532532

533+
bool BNDebuggerRunToReverse(BNDebuggerController* controller, const uint64_t* remoteAddresses, size_t count)
534+
{
535+
std::vector<uint64_t> addresses;
536+
addresses.reserve(count);
537+
for (size_t i = 0; i < count; i++)
538+
{
539+
addresses.push_back(remoteAddresses[i]);
540+
}
541+
return controller->object->RunToReverse(addresses);
542+
}
543+
544+
533545
BNDebugStopReason BNDebuggerGoAndWait(BNDebuggerController* controller)
534546
{
535547
return controller->object->GoAndWait();
@@ -590,6 +602,19 @@ BNDebugStopReason BNDebuggerRunToAndWait(
590602
}
591603

592604

605+
BNDebugStopReason BNDebuggerRunToReverseAndWait(
606+
BNDebuggerController* controller, const uint64_t* remoteAddresses, size_t count)
607+
{
608+
std::vector<uint64_t> addresses;
609+
addresses.reserve(count);
610+
for (size_t i = 0; i < count; i++)
611+
{
612+
addresses.push_back(remoteAddresses[i]);
613+
}
614+
return controller->object->RunToReverseAndWait(addresses);
615+
}
616+
617+
593618
DebugStopReason BNDebuggerPauseAndWait(BNDebuggerController* controller)
594619
{
595620
return controller->object->PauseAndWait();

ui/ui.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,21 @@ void GlobalDebuggerUI::SetupMenu(UIContext* context)
670670
connectedAndStopped));
671671
debuggerMenu->addAction("Run To Here", "Control");
672672

673+
UIAction::registerAction("Run Back To Here", QKeySequence(Qt::ShiftModifier | Qt::Key_F4));
674+
context->globalActions()->bindAction("Run Back To Here",
675+
UIAction(
676+
[this](const UIActionContext& ctxt) {
677+
if (!ctxt.binaryView)
678+
return;
679+
auto controller = DebuggerController::GetController(ctxt.binaryView);
680+
if (!controller)
681+
return;
682+
683+
controller->RunToReverse(ctxt.address);
684+
m_context->refreshCurrentViewContents();
685+
},
686+
connectedAndStoppedWithTTD));
687+
673688
UIAction::registerAction("Detach");
674689
context->globalActions()->bindAction("Detach",
675690
UIAction(

ui/uinotification.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ void NotificationListener::OnContextMenuCreated(UIContext *context, View* view,
184184
menu.addAction("Debugger", "Step Over", "Control");
185185
menu.addAction("Debugger", "Step Return", "Control");
186186
menu.addAction("Debugger", "Run To Here", "Control");
187+
menu.addAction("Debugger", "Run Back To Here", "Control");
187188
menu.addAction("Debugger", "Create Stack View", "Misc");
188189
menu.addAction("Debugger", "Override IP", "Misc");
189190
#ifdef WIN32

0 commit comments

Comments
 (0)