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
5 changes: 5 additions & 0 deletions api/debuggerapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,11 @@ namespace BinaryNinjaDebuggerAPI {
uint64_t RelativeAddressToAbsolute(const ModuleNameAndOffset& address);
ModuleNameAndOffset AbsoluteAddressToRelative(uint64_t address);

// rebasing
bool RebaseToRemoteBase();
bool RebaseToAddress(uint64_t address);
bool GetRemoteBase(uint64_t& address);

size_t RegisterEventCallback(
std::function<void(const DebuggerEvent& event)> callback, const std::string& name = "");
void RecordTrace();
Expand Down
18 changes: 18 additions & 0 deletions api/debuggercontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -900,6 +900,24 @@ ModuleNameAndOffset DebuggerController::AbsoluteAddressToRelative(uint64_t addre
}


bool DebuggerController::RebaseToRemoteBase()
{
return BNDebuggerRebaseToRemoteBase(m_object);
}


bool DebuggerController::RebaseToAddress(uint64_t address)
{
return BNDebuggerRebaseToAddress(m_object, address);
}


bool DebuggerController::GetRemoteBase(uint64_t& address)
{
return BNDebuggerGetRemoteBase(m_object, &address);
}


uint64_t DebuggerController::IP()
{
return BNDebuggerGetIP(m_object);
Expand Down
4 changes: 4 additions & 0 deletions api/ffi.h
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,10 @@ extern "C"
DEBUGGER_FFI_API BNModuleNameAndOffset BNDebuggerAbsoluteAddressToRelative(
BNDebuggerController* controller, uint64_t address);

DEBUGGER_FFI_API bool BNDebuggerRebaseToRemoteBase(BNDebuggerController* controller);
DEBUGGER_FFI_API bool BNDebuggerRebaseToAddress(BNDebuggerController* controller, uint64_t address);
DEBUGGER_FFI_API bool BNDebuggerGetRemoteBase(BNDebuggerController* controller, uint64_t* address);

DEBUGGER_FFI_API uint32_t BNDebuggerGetExitCode(BNDebuggerController* controller);

DEBUGGER_FFI_API void BNDebuggerWriteStdin(BNDebuggerController* controller, const char* data, size_t len);
Expand Down
43 changes: 42 additions & 1 deletion api/python/debuggercontroller.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
# import debugger
from . import _debuggercore as dbgcore
from .debugger_enums import *
from typing import Callable, List, Union
from typing import Callable, List, Optional, Union


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

def rebase_to_remote_base(self) -> bool:
"""
Rebase the input binary view to match the remote base address.

This is useful when auto-rebase is disabled (via the ``debugger.autoRebase`` setting)
and you want to manually trigger a rebase after the target has been launched.

Note: In UI mode, this returns True immediately and the rebase completes asynchronously.

:return: True if the rebase was initiated successfully, False otherwise
"""
return dbgcore.BNDebuggerRebaseToRemoteBase(self.handle)

def rebase_to_address(self, address: int) -> bool:
"""
Rebase the input binary view to the specified base address.

This allows manual rebasing to a user-specified address, which is useful when the
auto-detected remote base is incorrect.

Note: In UI mode, this returns True immediately and the rebase completes asynchronously.

:param address: The new base address for the binary view
:return: True if the rebase was initiated successfully, False otherwise
"""
return dbgcore.BNDebuggerRebaseToAddress(self.handle, address)

def get_remote_base(self) -> Optional[int]:
"""
Get the detected remote base address of the target module.

This returns the base address that the debugger detected for the input binary
in the remote process. Returns None if not connected or detection failed.

:return: The remote base address, or None if unavailable
"""
address = ctypes.c_uint64()
if not dbgcore.BNDebuggerGetRemoteBase(self.handle, ctypes.byref(address)):
return None
return address.value

@property
def regs(self) -> DebugRegisters:
"""
Expand Down
9 changes: 9 additions & 0 deletions core/debugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,15 @@ static void RegisterSettings()
"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.",
"ignore" : ["SettingsProjectScope", "SettingsResourceScope"]
})");

settings->RegisterSetting("debugger.autoRebase",
R"({
"title" : "Auto-Rebase on Module Load",
"type" : "boolean",
"default" : true,
"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.",
"ignore" : ["SettingsProjectScope", "SettingsResourceScope"]
})");
}

extern "C"
Expand Down
146 changes: 96 additions & 50 deletions core/debuggercontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1392,60 +1392,17 @@ void DebuggerController::DetectLoadedModule()

m_inputFileLoaded = true;
auto oldBase = GetViewFileSegmentsStart();

if (remoteBase == oldBase)
return;

m_ranges.clear();
m_oldViewBase = oldBase;
m_newViewBase = remoteBase;
auto data = GetData();
for (const auto& func: data->GetAnalysisFunctionList())
{
for (const auto& range: func->GetAddressRanges())
m_ranges.emplace_back(range);
}

if (BinaryNinja::IsUIEnabled())
{
// When the UI is enabled, let the debugger UI do the work. It can show a progress bar if the operation takes
// a while.
if (m_uiCallbacks)
m_uiCallbacks->NotifyRebaseBinaryView(remoteBase);
}
else
{
// Halt analysis before rebasing. Otherwise, the old view may continue analysis which leads to various issues
data->AbortAnalysis();
data->UpdateAnalysisAndWait();

RemoveDebuggerMemoryRegion();

auto shouldHoldAnalysis = Settings::Instance()->Get<bool>("debugger.holdAnalysis");
if (shouldHoldAnalysis)
data->SetAnalysisHold(false);
bool autoRebase = Settings::Instance()->Get<bool>("debugger.autoRebase");

// remote base is different from the local base, first need a rebase
auto viewType = data->GetTypeName();
if (!m_file->Rebase(data, remoteBase, [&](size_t cur, size_t total) { return true; }))
{
LogWarn("rebase failed");
}
auto rebasedView = m_file->GetViewOfType(viewType);
if (!rebasedView)
return;

if (shouldHoldAnalysis)
{
static auto completionEvent = rebasedView->AddAnalysisCompletionEvent([=](){
rebasedView->SetAnalysisHold(true);
});
rebasedView->UpdateAnalysis();
}

ReAddDebuggerMemoryRegion();
}
if (!autoRebase)
return;

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


Expand Down Expand Up @@ -3406,7 +3363,6 @@ bool DebuggerController::ReAddDebuggerMemoryRegion()
}



// TODO: these 3 functions should be moved to the BinaryNinjaAPI namespace for wider audiences
static intx::uint512 MaskToSize(intx::uint512 value, size_t size)
{
Expand Down Expand Up @@ -4506,3 +4462,93 @@ bool DebuggerController::FunctionExistsInOldView(uint64_t address)
}
return false;
}


bool DebuggerController::RebaseToRemoteBase()
{
uint64_t remoteBase;
if (!GetRemoteBase(remoteBase))
return false;

return RebaseToAddress(remoteBase);
}


bool DebuggerController::GetRemoteBase(uint64_t& address)
{
if (!m_state->IsConnected())
return false;

return m_state->GetRemoteBase(address);
}


bool DebuggerController::RebaseToAddress(uint64_t newBase)
{
const auto data = GetData();
if (!data)
return false;

const uint64_t oldBase = GetViewFileSegmentsStart();

if (newBase == oldBase)
return true;

// Check UI callbacks early before modifying state
if (BinaryNinja::IsUIEnabled() && !m_uiCallbacks)
return false;

m_oldViewBase = oldBase;
m_newViewBase = newBase;

m_ranges.clear();
for (const auto& func: data->GetAnalysisFunctionList())
{
for (const auto& range: func->GetAddressRanges())
m_ranges.emplace_back(range);
}

if (BinaryNinja::IsUIEnabled())
{
m_uiCallbacks->NotifyRebaseBinaryView(newBase);
return true; // Rebase completes asynchronously via UI callback
}

data->AbortAnalysis();
data->UpdateAnalysisAndWait();

RemoveDebuggerMemoryRegion();

const auto shouldHoldAnalysis = Settings::Instance()->Get<bool>("debugger.holdAnalysis");
if (shouldHoldAnalysis)
data->SetAnalysisHold(false);

const auto viewType = data->GetTypeName();
if (!m_file->Rebase(data, newBase, [&](size_t, size_t) { return true; }))
{
LogWarn("Failed to rebase to remote base 0x%" PRIx64, newBase);
ReAddDebuggerMemoryRegion();
return false;
}

const auto rebasedView = m_file->GetViewOfType(viewType);
if (!rebasedView)
{
ReAddDebuggerMemoryRegion();
return false;
}

if (shouldHoldAnalysis)
{
// Store in member variable to keep alive until callback fires
m_rebaseCompletionEvent = rebasedView->AddAnalysisCompletionEvent([=]() {
rebasedView->SetAnalysisHold(true);
});
rebasedView->UpdateAnalysis();
}

ReAddDebuggerMemoryRegion();
GetData()->UpdateAnalysis();

return true;
}
7 changes: 7 additions & 0 deletions core/debuggercontroller.h
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ namespace BinaryNinjaDebugger {

uint64_t m_oldViewBase, m_newViewBase;
std::vector<BNAddressRange> m_ranges;
BinaryNinja::Ref<BinaryNinja::AnalysisCompletionEvent> m_rebaseCompletionEvent;

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

// rebasing
// Note: Returns true immediately in UI mode (rebase completes asynchronously via UI callback)
bool RebaseToRemoteBase();
bool RebaseToAddress(uint64_t address);
bool GetRemoteBase(uint64_t& address);

// arch
ArchitectureRef GetRemoteArchitecture();

Expand Down
18 changes: 18 additions & 0 deletions core/ffi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1040,6 +1040,24 @@ BNModuleNameAndOffset BNDebuggerAbsoluteAddressToRelative(BNDebuggerController*
}


bool BNDebuggerRebaseToRemoteBase(BNDebuggerController* controller)
{
return controller->object->RebaseToRemoteBase();
}


bool BNDebuggerRebaseToAddress(BNDebuggerController* controller, uint64_t address)
{
return controller->object->RebaseToAddress(address);
}


bool BNDebuggerGetRemoteBase(BNDebuggerController* controller, uint64_t* address)
{
return controller->object->GetRemoteBase(*address);
}


bool BNDebuggerIsSameBaseModule(const char* module1, const char* module2)
{
return DebugModule::IsSameBaseModule(module1, module2);
Expand Down
Loading