Skip to content

Commit 2f70cda

Browse files
committed
[WARP] Automatically regenerate function GUIDs when rebasing or adding sections/segments
This should help with stale function GUIDs when opening a raw firmware with no setup image base and empty sections. One issue is this causes us to spin up a background task to enumerate all functions and generate the LLIL this is quite an involved process and as such can take a bit, unsure if there is really any way to improve this though.
1 parent 5dbe30f commit 2f70cda

File tree

2 files changed

+79
-0
lines changed

2 files changed

+79
-0
lines changed

plugins/warp/src/plugin.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::cache::container::add_cached_container;
55
use crate::container::disk::DiskContainer;
66
use crate::container::network::{NetworkClient, NetworkContainer};
77
use crate::matcher::MatcherSettings;
8+
use crate::plugin::listener::register_view_listeners;
89
use crate::plugin::render_layer::HighlightRenderLayer;
910
use crate::plugin::settings::PluginSettings;
1011
use crate::{core_signature_dir, user_signature_dir};
@@ -127,6 +128,9 @@ fn plugin_init() -> bool {
127128
// Make sure caches are flushed when the views get destructed.
128129
register_cache_destructor();
129130

131+
// Make sure we will regenerate guids on certain binary view events.
132+
register_view_listeners();
133+
130134
// Register our highlight render layer.
131135
HighlightRenderLayer::register();
132136

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use crate::cache::{cached_function_guid, try_cached_function_guid};
2+
use binaryninja::background_task::BackgroundTask;
3+
use binaryninja::binary_view::{register_binary_view_event, BinaryView, BinaryViewExt};
4+
use binaryninja::data_notification::DataNotificationClosure;
5+
use binaryninja::section::Section;
6+
use binaryninja::segment::Segment;
7+
use binaryninja::worker_thread::execute_on_worker_thread;
8+
use binaryninjacore_sys::BNBinaryViewEventType::BinaryViewFinalizationEvent;
9+
use rayon::iter::ParallelIterator;
10+
use std::time::Instant;
11+
12+
pub fn register_view_listeners() {
13+
// When the view is finalized (constructed) we need to register some listeners so we can automatically
14+
// regenerate the function GUIDs when necessary.
15+
register_binary_view_event(BinaryViewFinalizationEvent, |view: &BinaryView| {
16+
let _handle = DataNotificationClosure::default()
17+
.rebased(|_old_view: &BinaryView, new_view: &BinaryView| {
18+
spawn_regenerate_guids(&new_view)
19+
})
20+
.section_added(|view: &BinaryView, _section: &Section| spawn_regenerate_guids(&view))
21+
.section_removed(|view: &BinaryView, _section: &Section| spawn_regenerate_guids(&view))
22+
.segment_added(|view: &BinaryView, _segment: &Segment| spawn_regenerate_guids(&view))
23+
.segment_removed(|view: &BinaryView, _segment: &Segment| spawn_regenerate_guids(&view))
24+
.register_persisted(&view);
25+
});
26+
}
27+
28+
fn regenerate_guids(view: &BinaryView) {
29+
if !view.has_initial_analysis() {
30+
tracing::debug!(
31+
"Skipping regenerating GUIDs for view {} (not analyzed)",
32+
view.file()
33+
);
34+
return;
35+
}
36+
tracing::info!("Regenerating GUIDs for view {}", view.file());
37+
for running_task in &BackgroundTask::running_tasks() {
38+
if running_task
39+
.progress_text()
40+
.contains("Regenerating WARP Function GUIDs...")
41+
{
42+
tracing::debug!("Cancelling previous GUID regeneration task...");
43+
running_task.cancel();
44+
}
45+
}
46+
47+
let task = BackgroundTask::new("Regenerating WARP Function GUIDs...", false).enter();
48+
let time = Instant::now();
49+
view.functions().par_iter().all(|function| {
50+
let before = try_cached_function_guid(&function);
51+
if before.is_some() {
52+
function.remove_metadata("warp_function_guid");
53+
cached_function_guid(&function, || function.lifted_il().ok());
54+
}
55+
// Short circuit if the task was terminated.
56+
!task.is_cancelled()
57+
});
58+
tracing::debug!(
59+
"Regenerated GUIDs for view {} in {}ms",
60+
view.file(),
61+
time.elapsed().as_millis()
62+
);
63+
}
64+
65+
/// Spawn a background task to regenerate the GUIDs for the given view.
66+
fn spawn_regenerate_guids(view: &BinaryView) {
67+
// These events may be sent to us _before_ the view has finished analysis, we populate the guids
68+
// as a part of the initial function analysis already (see 'analysis.warp.guid') so we can skip.
69+
if !view.has_initial_analysis() {
70+
return;
71+
}
72+
let view_owned = view.to_owned();
73+
// Run in the background, we don't want to block the UI.
74+
execute_on_worker_thread("WARP: Regenerate", move || regenerate_guids(&view_owned));
75+
}

0 commit comments

Comments
 (0)