From 7710eada65da99572cb872220269f59d6feb1f52 Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Sun, 20 Jul 2025 20:38:21 -0400 Subject: [PATCH 1/6] [WARP] Consult segments unconditionally when constructing relocatable regions This removes more barriers for those working with firmware to use WARP, previously if you had defined any sections (such as a vector interrupt table) then backed segments would end up being marked non-relocatable. This, combined with the use of ELF intermediate object files created inconsistent GUID's, using the segments backing the real functions fixes this. --- plugins/warp/src/lib.rs | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/plugins/warp/src/lib.rs b/plugins/warp/src/lib.rs index bea85cdddd..0a4a758cb0 100644 --- a/plugins/warp/src/lib.rs +++ b/plugins/warp/src/lib.rs @@ -400,30 +400,27 @@ pub fn is_address_relocatable(relocatable_regions: &[Range], address: u64) // TODO: This might need to be configurable, in that case we better remove this function. /// Get the relocatable regions of the view. /// -/// Currently, this is all the sections, however, this might be refined later. +/// Currently, segments are used by default, however, if the only segment is based at 0, then we fall +/// back to using sections. pub fn relocatable_regions(view: &BinaryView) -> Vec> { - // NOTE: We cannot use segments here as there will be a zero-based segment. - let mut ranges: Vec<_> = view - .sections() + // NOTE: We used to use sections because the image base for some object files would start + // at zero, masking non-relocatable instructions, since then we have started adjusting the + // image base to 0x10000 or higher so we can use segments directly, which improves the accuracy + // of function GUIDs for binaries which have no or bad section definitions, common of firmware. + let mut ranges = view.segments() .iter() - .map(|s| Range { - start: s.start(), - end: s.end(), - }) - .collect(); + .filter(|s| s.address_range().start != 0) + .map(|s| s.address_range()) + .collect::>(); - // If the only section available is the synthetic one, fallback to using the segments. - // NOTE: This should only happen for firmware, and it should be _fine_ considering that we - // do not use segments for the case where we are based at some zero offset. The user should have - // based the image somewhere reasonably. - // TODO: Restrict this to only when image base is above some value? - if ranges.len() <= 1 { - let segment_ranges = view - .segments() + if ranges.is_empty() { + // Realistically only happens if the only defined segment was based at 0, in which case + // we hope the user has set up correct sections. If not we are going to be masking off too many + // or too little instructions. + ranges = view.sections() .iter() .map(|s| s.address_range()) .collect::>(); - ranges.extend(segment_ranges); } ranges From f271341027d97de4f65571c22239e1fda461ecc1 Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Sun, 20 Jul 2025 20:41:01 -0400 Subject: [PATCH 2/6] [WARP] Fix garbage jumps from being included in the variant instruction masking We emit jumps at the end of some blocks that the architecture is not aware of, they are not actually apart of the functions instructions rather they are used in our analysis... somehow, I have not figured out what they are supposed to be used for but regardless, they should be ignored by WARP. This was just a case where the filtering was including jumps in non start instructions, where we actually wanted the opposite. --- plugins/warp/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/warp/src/lib.rs b/plugins/warp/src/lib.rs index 0a4a758cb0..fd935702f4 100644 --- a/plugins/warp/src/lib.rs +++ b/plugins/warp/src/lib.rs @@ -248,7 +248,7 @@ pub fn filtered_instructions_at( LowLevelILInstructionKind::NoRet(_) | LowLevelILInstructionKind::Ret(_) => false, // Stop collecting instruction if we are probably the end function jump in lifted IL. This // is emitted at the end of the function and will mess with our GUID. - LowLevelILInstructionKind::Jump(_) => *i != 0, + LowLevelILInstructionKind::Jump(_) => *i == 0, _ => true, }) .map(|(_, instr)| instr) From a988cfb175d624e1cff5ae119633ae08ba5b8167 Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Sun, 20 Jul 2025 20:42:01 -0400 Subject: [PATCH 3/6] [WARP] Add warning if trying to run matcher on database with no function guids generated Just a sanity check so we can alert the user, they can just reanalyze their functions and the guid will be constructed --- plugins/warp/src/plugin/workflow.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/plugins/warp/src/plugin/workflow.rs b/plugins/warp/src/plugin/workflow.rs index 04e14a160c..878f8e9479 100644 --- a/plugins/warp/src/plugin/workflow.rs +++ b/plugins/warp/src/plugin/workflow.rs @@ -105,6 +105,15 @@ pub fn run_matcher(view: &BinaryView) { }) .into_group_map(); + if functions_by_target_and_guid.is_empty() && !view.functions().is_empty() { + // The user is likely trying to run the matcher on a database before guids were automatically + // generated, we should alert them and ask if they would like to reanalyze. + // TODO: Call reanalyze for them? + log::error!("Trying to match with an older database, please reanalyze the database."); + background_task.finish(); + return; + } + // TODO: Par iter this? Using dashmap let guids_by_target: HashMap> = functions_by_target_and_guid .keys() From e4d45a956c595160847017c7610d763a0f31a6a1 Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Sun, 20 Jul 2025 23:16:25 -0400 Subject: [PATCH 4/6] [WARP] Fix UI hanging when matched function sidebar updates with >50k matched functions One day I will get the QT incantations correct --- plugins/warp/ui/matched.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/plugins/warp/ui/matched.cpp b/plugins/warp/ui/matched.cpp index 475e0cdd8d..618c1b4c0c 100644 --- a/plugins/warp/ui/matched.cpp +++ b/plugins/warp/ui/matched.cpp @@ -70,6 +70,10 @@ void WarpMatchedWidget::Update() { m_tableWidget->GetTableView()->setSortingEnabled(false); m_tableWidget->GetTableView()->setEnabled(false); + m_tableWidget->GetProxyModel()->setDynamicSortFilter(false); + m_tableWidget->GetTableView()->setUpdatesEnabled(false); + m_tableWidget->GetTableView()->setModel(nullptr); + m_tableWidget->GetProxyModel()->setSourceModel(nullptr); for (const auto &analysisFunction: m_current->GetAnalysisFunctionList()) { if (const auto &matchedFunction = Warp::Function::GetMatched(*analysisFunction)) @@ -78,6 +82,10 @@ void WarpMatchedWidget::Update() m_tableWidget->InsertFunction(startAddress, new WarpFunctionItem(matchedFunction, analysisFunction)); } } + m_tableWidget->GetTableView()->setModel(m_tableWidget->GetProxyModel()); + m_tableWidget->GetProxyModel()->setSourceModel(m_tableWidget->GetModel()); + m_tableWidget->GetProxyModel()->setDynamicSortFilter(true); m_tableWidget->GetTableView()->setEnabled(true); m_tableWidget->GetTableView()->setSortingEnabled(true); + m_tableWidget->GetTableView()->setUpdatesEnabled(true); } From 68998103ab0f7f6ebb3c1f0289f11d64f77fb94d Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Sun, 20 Jul 2025 23:17:20 -0400 Subject: [PATCH 5/6] [WARP] Fix sidebar run matcher icon not resetting to idle Fixes https://github.com/Vector35/binaryninja-api/issues/7116 --- plugins/warp/ui/plugin.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/warp/ui/plugin.cpp b/plugins/warp/ui/plugin.cpp index 923a79b364..dccea1f1ba 100644 --- a/plugins/warp/ui/plugin.cpp +++ b/plugins/warp/ui/plugin.cpp @@ -134,8 +134,7 @@ void WarpSidebarWidget::focus() void WarpSidebarWidget::Update() { m_matchedWidget->Update(); - if (!GetMatcherTask()) - setMatcherActionIcon(false); + setMatcherActionIcon(false); } void WarpSidebarWidget::setMatcherActionIcon(bool running) From dfa7a29905568709f236d711f05d9640560fd615 Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Sun, 20 Jul 2025 23:43:00 -0400 Subject: [PATCH 6/6] [WARP] Misc formatting fixes --- plugins/warp/src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/warp/src/lib.rs b/plugins/warp/src/lib.rs index fd935702f4..be6f8bc398 100644 --- a/plugins/warp/src/lib.rs +++ b/plugins/warp/src/lib.rs @@ -407,7 +407,8 @@ pub fn relocatable_regions(view: &BinaryView) -> Vec> { // at zero, masking non-relocatable instructions, since then we have started adjusting the // image base to 0x10000 or higher so we can use segments directly, which improves the accuracy // of function GUIDs for binaries which have no or bad section definitions, common of firmware. - let mut ranges = view.segments() + let mut ranges = view + .segments() .iter() .filter(|s| s.address_range().start != 0) .map(|s| s.address_range()) @@ -417,7 +418,8 @@ pub fn relocatable_regions(view: &BinaryView) -> Vec> { // Realistically only happens if the only defined segment was based at 0, in which case // we hope the user has set up correct sections. If not we are going to be masking off too many // or too little instructions. - ranges = view.sections() + ranges = view + .sections() .iter() .map(|s| s.address_range()) .collect::>();