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

// rebasing
bool RebaseToRemoteBase();

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


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


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

DEBUGGER_FFI_API bool BNDebuggerRebaseToRemoteBase(BNDebuggerController* controller);

DEBUGGER_FFI_API uint32_t BNDebuggerGetExitCode(BNDebuggerController* controller);

DEBUGGER_FFI_API void BNDebuggerWriteStdin(BNDebuggerController* controller, const char* data, size_t len);
Expand Down
13 changes: 13 additions & 0 deletions api/python/debuggercontroller.py
Original file line number Diff line number Diff line change
Expand Up @@ -1356,6 +1356,19 @@ 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)

@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
136 changes: 86 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);

// 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();
}
bool autoRebase = Settings::Instance()->Get<bool>("debugger.autoRebase");

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,83 @@ bool DebuggerController::FunctionExistsInOldView(uint64_t address)
}
return false;
}


bool DebuggerController::RebaseToRemoteBase()
{
if (!m_state->IsConnected())
return false;

uint64_t remoteBase;
if (!m_state->GetRemoteBase(remoteBase))
return false;

return RebaseToAddress(remoteBase);
}


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

const auto rebasedView = m_file->GetViewOfType(viewType);
if (!rebasedView)
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;
}
6 changes: 6 additions & 0 deletions core/debuggercontroller.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ namespace BinaryNinjaDebugger {
bool m_shouldAnnotateStackVariable = false;

void EventHandler(const DebuggerEvent& event);
bool RebaseToAddress(uint64_t newBase);
void UpdateStackVariables();
void AddRegisterValuesToExpressionParser();
void AddModuleValuesToExpressionParser();
Expand Down Expand Up @@ -204,6 +205,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 +280,10 @@ 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();

// arch
ArchitectureRef GetRemoteArchitecture();

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


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


bool BNDebuggerIsSameBaseModule(const char* module1, const char* module2)
{
return DebugModule::IsSameBaseModule(module1, module2);
Expand Down
23 changes: 22 additions & 1 deletion ui/ui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,27 @@ void GlobalDebuggerUI::SetupMenu(UIContext* context)
Menu::setMainMenuOrder("Debugger", MENU_ORDER_LATE);
debuggerMenu->addAction("Debug Adapter Settings...", "Settings", MENU_ORDER_FIRST);

UIAction::registerAction("Rebase to Remote Base");
context->globalActions()->bindAction("Rebase to Remote Base",
UIAction(
[=](const UIActionContext& ctxt) {
if (!ctxt.binaryView)
return;
const auto controller = DebuggerController::GetController(ctxt.binaryView);
if (!controller || !controller->IsConnected())
return;
if (!controller->RebaseToRemoteBase())
LogWarn("Failed to rebase to remote base");
},
[=](const UIActionContext& ctxt) {
if (!ctxt.binaryView)
return false;
const auto controller = DebuggerController::GetController(ctxt.binaryView);
return controller && controller->IsConnected();
}));

debuggerMenu->addAction("Rebase to Remote Base", "Rebase");

UIAction::registerAction("Launch", QKeySequence(Qt::Key_F6));
context->globalActions()->bindAction("Launch",
UIAction(
Expand Down Expand Up @@ -1776,7 +1797,7 @@ void DebuggerUI::checkRebaseBinaryView(uint64_t remoteBase)

if (!result)
{
LogWarn("failed to rebase the input view");
LogWarn("Failed to rebase to remote base 0x%" PRIx64, remoteBase);
return;
}

Expand Down
1 change: 1 addition & 0 deletions ui/uinotification.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ void NotificationListener::OnContextMenuCreated(UIContext *context, View* view,
menu.addAction("Debugger", "Run Back To Here", "Control");
menu.addAction("Debugger", "Create Stack View", "Misc");
menu.addAction("Debugger", "Override IP", "Misc");
menu.addAction("Debugger", "Rebase to Remote Base", "Misc");
#ifdef WIN32
// TTD Memory Access context menu items
menu.addAction("Debugger", "Navigate to TTD Timestamp...", "TTD");
Expand Down