Skip to content

Commit b72e83b

Browse files
3rditxusheng6claude
authored
Manual rebasing setting and rebase to remote base action (#969)
* Add optional manual rebasing with debugger.autoRebase setting and Rebase to Remote Base action * Add user-provided base address support for manual rebasing with auto-detected default * fix inconsistency * Fix Optional not imported * Use ParseExpression API for rebase address input Replace ad-hoc hex parsing with BinaryView::ParseExpression to match debugger conventions and support expressions like symbols and arithmetic. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Xusheng <xusheng@vector35.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent ecacb01 commit b72e83b

10 files changed

Lines changed: 250 additions & 52 deletions

api/debuggerapi.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -769,6 +769,11 @@ namespace BinaryNinjaDebuggerAPI {
769769
uint64_t RelativeAddressToAbsolute(const ModuleNameAndOffset& address);
770770
ModuleNameAndOffset AbsoluteAddressToRelative(uint64_t address);
771771

772+
// rebasing
773+
bool RebaseToRemoteBase();
774+
bool RebaseToAddress(uint64_t address);
775+
bool GetRemoteBase(uint64_t& address);
776+
772777
size_t RegisterEventCallback(
773778
std::function<void(const DebuggerEvent& event)> callback, const std::string& name = "");
774779
void RecordTrace();

api/debuggercontroller.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,24 @@ ModuleNameAndOffset DebuggerController::AbsoluteAddressToRelative(uint64_t addre
900900
}
901901

902902

903+
bool DebuggerController::RebaseToRemoteBase()
904+
{
905+
return BNDebuggerRebaseToRemoteBase(m_object);
906+
}
907+
908+
909+
bool DebuggerController::RebaseToAddress(uint64_t address)
910+
{
911+
return BNDebuggerRebaseToAddress(m_object, address);
912+
}
913+
914+
915+
bool DebuggerController::GetRemoteBase(uint64_t& address)
916+
{
917+
return BNDebuggerGetRemoteBase(m_object, &address);
918+
}
919+
920+
903921
uint64_t DebuggerController::IP()
904922
{
905923
return BNDebuggerGetIP(m_object);

api/ffi.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,10 @@ extern "C"
643643
DEBUGGER_FFI_API BNModuleNameAndOffset BNDebuggerAbsoluteAddressToRelative(
644644
BNDebuggerController* controller, uint64_t address);
645645

646+
DEBUGGER_FFI_API bool BNDebuggerRebaseToRemoteBase(BNDebuggerController* controller);
647+
DEBUGGER_FFI_API bool BNDebuggerRebaseToAddress(BNDebuggerController* controller, uint64_t address);
648+
DEBUGGER_FFI_API bool BNDebuggerGetRemoteBase(BNDebuggerController* controller, uint64_t* address);
649+
646650
DEBUGGER_FFI_API uint32_t BNDebuggerGetExitCode(BNDebuggerController* controller);
647651

648652
DEBUGGER_FFI_API void BNDebuggerWriteStdin(BNDebuggerController* controller, const char* data, size_t len);

api/python/debuggercontroller.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
# import debugger
2222
from . import _debuggercore as dbgcore
2323
from .debugger_enums import *
24-
from typing import Callable, List, Union
24+
from typing import Callable, List, Optional, Union
2525

2626

2727
# TTD (Time Travel Debugging) Memory Access Type parsing
@@ -1356,6 +1356,47 @@ def modules(self) -> DebugModules:
13561356
dbgcore.BNDebuggerFreeModules(modules, count.value)
13571357
return DebugModules(result)
13581358

1359+
def rebase_to_remote_base(self) -> bool:
1360+
"""
1361+
Rebase the input binary view to match the remote base address.
1362+
1363+
This is useful when auto-rebase is disabled (via the ``debugger.autoRebase`` setting)
1364+
and you want to manually trigger a rebase after the target has been launched.
1365+
1366+
Note: In UI mode, this returns True immediately and the rebase completes asynchronously.
1367+
1368+
:return: True if the rebase was initiated successfully, False otherwise
1369+
"""
1370+
return dbgcore.BNDebuggerRebaseToRemoteBase(self.handle)
1371+
1372+
def rebase_to_address(self, address: int) -> bool:
1373+
"""
1374+
Rebase the input binary view to the specified base address.
1375+
1376+
This allows manual rebasing to a user-specified address, which is useful when the
1377+
auto-detected remote base is incorrect.
1378+
1379+
Note: In UI mode, this returns True immediately and the rebase completes asynchronously.
1380+
1381+
:param address: The new base address for the binary view
1382+
:return: True if the rebase was initiated successfully, False otherwise
1383+
"""
1384+
return dbgcore.BNDebuggerRebaseToAddress(self.handle, address)
1385+
1386+
def get_remote_base(self) -> Optional[int]:
1387+
"""
1388+
Get the detected remote base address of the target module.
1389+
1390+
This returns the base address that the debugger detected for the input binary
1391+
in the remote process. Returns None if not connected or detection failed.
1392+
1393+
:return: The remote base address, or None if unavailable
1394+
"""
1395+
address = ctypes.c_uint64()
1396+
if not dbgcore.BNDebuggerGetRemoteBase(self.handle, ctypes.byref(address)):
1397+
return None
1398+
return address.value
1399+
13591400
@property
13601401
def regs(self) -> DebugRegisters:
13611402
"""

core/debugger.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,15 @@ static void RegisterSettings()
189189
"description" : "When enabled, module name comparisons are case-insensitive. This is useful when debug adapters report module names with different casing than the actual binary file names.",
190190
"ignore" : ["SettingsProjectScope", "SettingsResourceScope"]
191191
})");
192+
193+
settings->RegisterSetting("debugger.autoRebase",
194+
R"({
195+
"title" : "Auto-Rebase on Module Load",
196+
"type" : "boolean",
197+
"default" : true,
198+
"description" : "When enabled, automatically rebase the input binary view to match the remote base address when the module is loaded. Disable this to keep the original base address and use the 'Rebase to Remote Base' action in the Debugger menu to manually trigger rebasing.",
199+
"ignore" : ["SettingsProjectScope", "SettingsResourceScope"]
200+
})");
192201
}
193202

194203
extern "C"

core/debuggercontroller.cpp

Lines changed: 96 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1392,60 +1392,17 @@ void DebuggerController::DetectLoadedModule()
13921392

13931393
m_inputFileLoaded = true;
13941394
auto oldBase = GetViewFileSegmentsStart();
1395+
13951396
if (remoteBase == oldBase)
13961397
return;
13971398

1398-
m_ranges.clear();
1399-
m_oldViewBase = oldBase;
1400-
m_newViewBase = remoteBase;
1401-
auto data = GetData();
1402-
for (const auto& func: data->GetAnalysisFunctionList())
1403-
{
1404-
for (const auto& range: func->GetAddressRanges())
1405-
m_ranges.emplace_back(range);
1406-
}
1407-
1408-
if (BinaryNinja::IsUIEnabled())
1409-
{
1410-
// When the UI is enabled, let the debugger UI do the work. It can show a progress bar if the operation takes
1411-
// a while.
1412-
if (m_uiCallbacks)
1413-
m_uiCallbacks->NotifyRebaseBinaryView(remoteBase);
1414-
}
1415-
else
1416-
{
1417-
// Halt analysis before rebasing. Otherwise, the old view may continue analysis which leads to various issues
1418-
data->AbortAnalysis();
1419-
data->UpdateAnalysisAndWait();
1420-
1421-
RemoveDebuggerMemoryRegion();
1422-
1423-
auto shouldHoldAnalysis = Settings::Instance()->Get<bool>("debugger.holdAnalysis");
1424-
if (shouldHoldAnalysis)
1425-
data->SetAnalysisHold(false);
1399+
bool autoRebase = Settings::Instance()->Get<bool>("debugger.autoRebase");
14261400

1427-
// remote base is different from the local base, first need a rebase
1428-
auto viewType = data->GetTypeName();
1429-
if (!m_file->Rebase(data, remoteBase, [&](size_t cur, size_t total) { return true; }))
1430-
{
1431-
LogWarn("rebase failed");
1432-
}
1433-
auto rebasedView = m_file->GetViewOfType(viewType);
1434-
if (!rebasedView)
1435-
return;
1436-
1437-
if (shouldHoldAnalysis)
1438-
{
1439-
static auto completionEvent = rebasedView->AddAnalysisCompletionEvent([=](){
1440-
rebasedView->SetAnalysisHold(true);
1441-
});
1442-
rebasedView->UpdateAnalysis();
1443-
}
1444-
1445-
ReAddDebuggerMemoryRegion();
1446-
}
1401+
if (!autoRebase)
1402+
return;
14471403

1448-
GetData()->UpdateAnalysis();
1404+
if (!RebaseToAddress(remoteBase))
1405+
LogWarn("Failed to rebase to remote base 0x%" PRIx64, remoteBase);
14491406
}
14501407

14511408

@@ -3406,7 +3363,6 @@ bool DebuggerController::ReAddDebuggerMemoryRegion()
34063363
}
34073364

34083365

3409-
34103366
// TODO: these 3 functions should be moved to the BinaryNinjaAPI namespace for wider audiences
34113367
static intx::uint512 MaskToSize(intx::uint512 value, size_t size)
34123368
{
@@ -4506,3 +4462,93 @@ bool DebuggerController::FunctionExistsInOldView(uint64_t address)
45064462
}
45074463
return false;
45084464
}
4465+
4466+
4467+
bool DebuggerController::RebaseToRemoteBase()
4468+
{
4469+
uint64_t remoteBase;
4470+
if (!GetRemoteBase(remoteBase))
4471+
return false;
4472+
4473+
return RebaseToAddress(remoteBase);
4474+
}
4475+
4476+
4477+
bool DebuggerController::GetRemoteBase(uint64_t& address)
4478+
{
4479+
if (!m_state->IsConnected())
4480+
return false;
4481+
4482+
return m_state->GetRemoteBase(address);
4483+
}
4484+
4485+
4486+
bool DebuggerController::RebaseToAddress(uint64_t newBase)
4487+
{
4488+
const auto data = GetData();
4489+
if (!data)
4490+
return false;
4491+
4492+
const uint64_t oldBase = GetViewFileSegmentsStart();
4493+
4494+
if (newBase == oldBase)
4495+
return true;
4496+
4497+
// Check UI callbacks early before modifying state
4498+
if (BinaryNinja::IsUIEnabled() && !m_uiCallbacks)
4499+
return false;
4500+
4501+
m_oldViewBase = oldBase;
4502+
m_newViewBase = newBase;
4503+
4504+
m_ranges.clear();
4505+
for (const auto& func: data->GetAnalysisFunctionList())
4506+
{
4507+
for (const auto& range: func->GetAddressRanges())
4508+
m_ranges.emplace_back(range);
4509+
}
4510+
4511+
if (BinaryNinja::IsUIEnabled())
4512+
{
4513+
m_uiCallbacks->NotifyRebaseBinaryView(newBase);
4514+
return true; // Rebase completes asynchronously via UI callback
4515+
}
4516+
4517+
data->AbortAnalysis();
4518+
data->UpdateAnalysisAndWait();
4519+
4520+
RemoveDebuggerMemoryRegion();
4521+
4522+
const auto shouldHoldAnalysis = Settings::Instance()->Get<bool>("debugger.holdAnalysis");
4523+
if (shouldHoldAnalysis)
4524+
data->SetAnalysisHold(false);
4525+
4526+
const auto viewType = data->GetTypeName();
4527+
if (!m_file->Rebase(data, newBase, [&](size_t, size_t) { return true; }))
4528+
{
4529+
LogWarn("Failed to rebase to remote base 0x%" PRIx64, newBase);
4530+
ReAddDebuggerMemoryRegion();
4531+
return false;
4532+
}
4533+
4534+
const auto rebasedView = m_file->GetViewOfType(viewType);
4535+
if (!rebasedView)
4536+
{
4537+
ReAddDebuggerMemoryRegion();
4538+
return false;
4539+
}
4540+
4541+
if (shouldHoldAnalysis)
4542+
{
4543+
// Store in member variable to keep alive until callback fires
4544+
m_rebaseCompletionEvent = rebasedView->AddAnalysisCompletionEvent([=]() {
4545+
rebasedView->SetAnalysisHold(true);
4546+
});
4547+
rebasedView->UpdateAnalysis();
4548+
}
4549+
4550+
ReAddDebuggerMemoryRegion();
4551+
GetData()->UpdateAnalysis();
4552+
4553+
return true;
4554+
}

core/debuggercontroller.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ namespace BinaryNinjaDebugger {
204204

205205
uint64_t m_oldViewBase, m_newViewBase;
206206
std::vector<BNAddressRange> m_ranges;
207+
BinaryNinja::Ref<BinaryNinja::AnalysisCompletionEvent> m_rebaseCompletionEvent;
207208

208209
// TTD Code Coverage Analysis
209210
std::unordered_set<uint64_t> m_executedInstructions;
@@ -278,6 +279,12 @@ namespace BinaryNinjaDebugger {
278279
ModuleNameAndOffset AbsoluteAddressToRelative(uint64_t absoluteAddress);
279280
uint64_t RelativeAddressToAbsolute(const ModuleNameAndOffset& relativeAddress);
280281

282+
// rebasing
283+
// Note: Returns true immediately in UI mode (rebase completes asynchronously via UI callback)
284+
bool RebaseToRemoteBase();
285+
bool RebaseToAddress(uint64_t address);
286+
bool GetRemoteBase(uint64_t& address);
287+
281288
// arch
282289
ArchitectureRef GetRemoteArchitecture();
283290

core/ffi.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,24 @@ BNModuleNameAndOffset BNDebuggerAbsoluteAddressToRelative(BNDebuggerController*
10401040
}
10411041

10421042

1043+
bool BNDebuggerRebaseToRemoteBase(BNDebuggerController* controller)
1044+
{
1045+
return controller->object->RebaseToRemoteBase();
1046+
}
1047+
1048+
1049+
bool BNDebuggerRebaseToAddress(BNDebuggerController* controller, uint64_t address)
1050+
{
1051+
return controller->object->RebaseToAddress(address);
1052+
}
1053+
1054+
1055+
bool BNDebuggerGetRemoteBase(BNDebuggerController* controller, uint64_t* address)
1056+
{
1057+
return controller->object->GetRemoteBase(*address);
1058+
}
1059+
1060+
10431061
bool BNDebuggerIsSameBaseModule(const char* module1, const char* module2)
10441062
{
10451063
return DebugModule::IsSameBaseModule(module1, module2);

0 commit comments

Comments
 (0)