summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authorMason Reed <mason@vector35.com>2024-10-28 21:20:03 -0400
committerMason Reed <mason@vector35.com>2024-10-28 21:21:55 -0400
commita38d13f32326b72e59503a8280f610c52e018366 (patch)
tree44ea154e651489cc3d3e5a5fb5d17f894ace784d /plugins
parent08f1c49f0cb7f74d0f50dcf60289e974ae9d5c3a (diff)
Refactor WARP to use a module workflow for matching
Also flush caches on view destruction and improve performance
Diffstat (limited to 'plugins')
-rw-r--r--plugins/warp/src/cache.rs181
-rw-r--r--plugins/warp/src/matcher.rs123
-rw-r--r--plugins/warp/src/plugin.rs27
-rw-r--r--plugins/warp/src/plugin/create.rs8
-rw-r--r--plugins/warp/src/plugin/types.rs2
-rw-r--r--plugins/warp/src/plugin/workflow.rs66
6 files changed, 251 insertions, 156 deletions
diff --git a/plugins/warp/src/cache.rs b/plugins/warp/src/cache.rs
index bafc4ede..54ce339f 100644
--- a/plugins/warp/src/cache.rs
+++ b/plugins/warp/src/cache.rs
@@ -1,10 +1,14 @@
+use crate::convert::from_bn_symbol;
+use crate::{build_function, function_guid};
use binaryninja::architecture::Architecture;
use binaryninja::binaryview::{BinaryView, BinaryViewBase, BinaryViewExt};
use binaryninja::function::Function as BNFunction;
-use binaryninja::llil;
use binaryninja::llil::{FunctionMutability, NonSSA, NonSSAVariant};
use binaryninja::rc::Guard;
use binaryninja::rc::Ref as BNRef;
+use binaryninja::symbol::Symbol as BNSymbol;
+use binaryninja::{llil, ObjectDestructor};
+use dashmap::mapref::one::Ref;
use dashmap::try_result::TryResult;
use dashmap::DashMap;
use std::collections::HashSet;
@@ -13,12 +17,47 @@ use std::sync::OnceLock;
use warp::signature::function::constraints::FunctionConstraint;
use warp::signature::function::{Function, FunctionGUID};
-use crate::convert::from_bn_symbol;
-use crate::{build_function, function_guid};
-
+pub static MATCHED_FUNCTION_CACHE: OnceLock<DashMap<ViewID, MatchedFunctionCache>> =
+ OnceLock::new();
pub static FUNCTION_CACHE: OnceLock<DashMap<ViewID, FunctionCache>> = OnceLock::new();
pub static GUID_CACHE: OnceLock<DashMap<ViewID, GUIDCache>> = OnceLock::new();
+pub fn register_cache_destructor() {
+ pub static mut CACHE_DESTRUCTOR: CacheDestructor = CacheDestructor;
+ unsafe { CACHE_DESTRUCTOR.register() };
+}
+
+pub fn cached_function_match<F>(function: &BNFunction, f: F) -> Option<Function>
+where
+ F: Fn() -> Option<Function>,
+{
+ let view = function.view();
+ let view_id = ViewID::from(view.as_ref());
+ let function_id = FunctionID::from(function);
+ let function_cache = MATCHED_FUNCTION_CACHE.get_or_init(Default::default);
+ match function_cache.get(&view_id) {
+ Some(cache) => cache.get_or_insert(&function_id, f).to_owned(),
+ None => {
+ let cache = MatchedFunctionCache::default();
+ let matched = cache.get_or_insert(&function_id, f).to_owned();
+ function_cache.insert(view_id, cache);
+ matched
+ }
+ }
+}
+
+pub fn try_cached_function_match(function: &BNFunction) -> Option<Function> {
+ let view = function.view();
+ let view_id = ViewID::from(view);
+ let function_id = FunctionID::from(function);
+ let function_cache = MATCHED_FUNCTION_CACHE.get_or_init(Default::default);
+ function_cache
+ .get(&view_id)?
+ .get(&function_id)?
+ .value()
+ .to_owned()
+}
+
pub fn cached_function<A: Architecture, M: FunctionMutability, V: NonSSAVariant>(
function: &BNFunction,
llil: &llil::Function<A, M, NonSSA<V>>,
@@ -85,6 +124,38 @@ pub fn cached_function_guid<A: Architecture, M: FunctionMutability, V: NonSSAVar
}
}
+pub fn try_cached_function_guid(function: &BNFunction) -> Option<FunctionGUID> {
+ let view = function.view();
+ let view_id = ViewID::from(view);
+ let guid_cache = GUID_CACHE.get_or_init(Default::default);
+ guid_cache.get(&view_id)?.try_function_guid(function)
+}
+
+#[derive(Clone, Debug, Default)]
+pub struct MatchedFunctionCache {
+ pub cache: DashMap<FunctionID, Option<Function>>,
+}
+
+impl MatchedFunctionCache {
+ pub fn get_or_insert<F>(
+ &self,
+ function_id: &FunctionID,
+ f: F,
+ ) -> Ref<'_, FunctionID, Option<Function>>
+ where
+ F: FnOnce() -> Option<Function>,
+ {
+ self.cache.get(function_id).unwrap_or_else(|| {
+ self.cache.insert(*function_id, f());
+ self.cache.get(function_id).unwrap()
+ })
+ }
+
+ pub fn get(&self, function_id: &FunctionID) -> Option<Ref<'_, FunctionID, Option<Function>>> {
+ self.cache.get(function_id)
+ }
+}
+
#[derive(Clone, Debug, Default)]
pub struct FunctionCache {
pub cache: DashMap<FunctionID, Function>,
@@ -119,22 +190,33 @@ impl GUIDCache {
let view = function.view();
let func_id = FunctionID::from(function);
let func_start = function.start();
+ let func_platform = function.platform();
let mut constraints = HashSet::new();
for call_site in &function.call_sites() {
- for cs_ref in &view.get_code_refs(call_site.address) {
- let cs_ref_func = cs_ref.function();
- let cs_ref_func_id = FunctionID::from(cs_ref_func);
- if cs_ref_func_id != func_id {
- let call_site_offset: i64 = func_start as i64 - call_site.address as i64;
- let function_constraint = match cs_ref_func.low_level_il_if_available() {
- Some(cs_ref_func_llil) => self.function_constraint_with_guid(
- cs_ref_func,
- &cs_ref_func_llil,
- call_site_offset,
- ),
- None => self.function_constraint(cs_ref_func, call_site_offset),
- };
- constraints.insert(function_constraint);
+ for cs_ref_addr in view.get_code_refs_from(call_site.address, Some(function)) {
+ match view.function_at(&func_platform, cs_ref_addr) {
+ Ok(cs_ref_func) => {
+ // Call site is a function, constrain on it.
+ let cs_ref_func_id = FunctionID::from(cs_ref_func.as_ref());
+ if cs_ref_func_id != func_id {
+ let call_site_offset: i64 =
+ func_start as i64 - call_site.address as i64;
+ constraints
+ .insert(self.function_constraint(&cs_ref_func, call_site_offset));
+ }
+ }
+ Err(_) => {
+ // We could be dealing with an extern symbol, get the symbol as a constraint.
+ let call_site_offset: i64 = func_start as i64 - call_site.address as i64;
+ if let Ok(call_site_sym) = view.symbol_by_address(cs_ref_addr) {
+ constraints.insert(
+ self.function_constraint_from_symbol(
+ &call_site_sym,
+ call_site_offset,
+ ),
+ );
+ }
+ }
}
}
}
@@ -152,19 +234,10 @@ impl GUIDCache {
for curr_func in &view.functions_at(func_start_addr) {
let curr_func_id = FunctionID::from(curr_func.as_ref());
if curr_func_id != func_id {
- // NOTE: We have to get the llil here for the function which is problematic for running
- // NOTE: within a workflow (before analysis has finished)
+ // NOTE: For this to work the GUID has to have already been cached. If not it will just be the symbol.
// Function adjacent to another function, constrain on the pattern.
let curr_addr_offset = (func_start_addr as i64) - func_start as i64;
- let function_constraint = match curr_func.low_level_il_if_available() {
- Some(curr_func_llil) => self.function_constraint_with_guid(
- &curr_func,
- &curr_func_llil,
- curr_addr_offset,
- ),
- None => self.function_constraint(&curr_func, curr_addr_offset),
- };
- constraints.insert(function_constraint);
+ constraints.insert(self.function_constraint(&curr_func, curr_addr_offset));
}
}
};
@@ -186,29 +259,24 @@ impl GUIDCache {
/// Construct a function constraint, must pass the offset at which it is located.
pub fn function_constraint(&self, function: &BNFunction, offset: i64) -> FunctionConstraint {
+ let guid = self.try_function_guid(function);
let symbol = from_bn_symbol(&function.symbol());
FunctionConstraint {
- guid: None,
+ guid,
symbol: Some(symbol),
offset,
}
}
- /// Construct a function constraint, must pass the offset at which it is located.
- pub fn function_constraint_with_guid<
- A: Architecture,
- M: FunctionMutability,
- V: NonSSAVariant,
- >(
+ /// Construct a function constraint from a symbol, typically used for extern function call sites, must pass the offset at which it is located.
+ pub fn function_constraint_from_symbol(
&self,
- function: &BNFunction,
- llil: &llil::Function<A, M, NonSSA<V>>,
+ symbol: &BNSymbol,
offset: i64,
) -> FunctionConstraint {
- let guid = self.function_guid(function, llil);
- let symbol = from_bn_symbol(&function.symbol());
+ let symbol = from_bn_symbol(symbol);
FunctionConstraint {
- guid: Some(guid),
+ guid: None,
symbol: Some(symbol),
offset,
}
@@ -227,9 +295,19 @@ impl GUIDCache {
self.cache.insert(function_id, function_guid);
function_guid
}
- TryResult::Locked => function_guid(function, llil),
+ TryResult::Locked => {
+ log::warn!("Failed to acquire function guid cache");
+ function_guid(function, llil)
+ }
}
}
+
+ pub fn try_function_guid(&self, function: &BNFunction) -> Option<FunctionGUID> {
+ let function_id = FunctionID::from(function);
+ self.cache
+ .get(&function_id)
+ .map(|function_guid| function_guid.value().to_owned())
+ }
}
/// A unique view ID, used for caching.
@@ -284,3 +362,22 @@ impl From<Guard<'_, BNFunction>> for FunctionID {
Self::from(value.as_ref())
}
}
+
+pub struct CacheDestructor;
+
+impl ObjectDestructor for CacheDestructor {
+ fn destruct_view(&self, view: &BinaryView) {
+ // Clear caches as the view is no longer alive.
+ let view_id = ViewID::from(view);
+ if let Some(cache) = MATCHED_FUNCTION_CACHE.get() {
+ cache.remove(&view_id);
+ }
+ if let Some(cache) = FUNCTION_CACHE.get() {
+ cache.remove(&view_id);
+ }
+ if let Some(cache) = GUID_CACHE.get() {
+ cache.remove(&view_id);
+ }
+ log::debug!("Removed WARP caches for {:?}", view);
+ }
+}
diff --git a/plugins/warp/src/matcher.rs b/plugins/warp/src/matcher.rs
index 46ddd88c..81755af9 100644
--- a/plugins/warp/src/matcher.rs
+++ b/plugins/warp/src/matcher.rs
@@ -1,14 +1,11 @@
-use binaryninja::architecture::{Architecture as BNArchitecture, Architecture};
+use binaryninja::architecture::Architecture as BNArchitecture;
use binaryninja::backgroundtask::BackgroundTask;
use binaryninja::binaryview::{BinaryView, BinaryViewExt};
use binaryninja::function::{Function as BNFunction, FunctionUpdateType};
-use binaryninja::llil;
-use binaryninja::llil::{FunctionMutability, NonSSA, NonSSAVariant};
use binaryninja::platform::Platform;
use binaryninja::rc::Guard;
use binaryninja::rc::Ref as BNRef;
use dashmap::DashMap;
-use fastbloom::BloomFilter;
use std::cmp::Ordering;
use std::collections::{HashMap, HashSet};
use std::hash::{DefaultHasher, Hasher};
@@ -18,44 +15,41 @@ use walkdir::{DirEntry, WalkDir};
use warp::r#type::class::TypeClass;
use warp::r#type::guid::TypeGUID;
use warp::r#type::Type;
-use warp::signature::basic_block::BasicBlock;
use warp::signature::function::{Function, FunctionGUID};
use warp::signature::Data;
-use crate::cache::{cached_call_site_constraints, cached_function_guid, FunctionID};
+use crate::cache::{cached_call_site_constraints, cached_function_match, try_cached_function_guid};
use crate::convert::to_bn_type;
-use crate::entry_basic_block_guid;
use crate::plugin::on_matched_function;
-pub const TRIVIAL_LLIL_THRESHOLD: usize = 8;
+pub const TRIVIAL_FUNCTION_DELTA_THRESHOLD: u64 = 8;
pub static PLAT_MATCHER_CACHE: OnceLock<DashMap<PlatformID, Matcher>> = OnceLock::new();
-pub fn cached_function_match<A: Architecture, M: FunctionMutability, V: NonSSAVariant>(
- function: &BNFunction,
- llil: &llil::Function<A, M, NonSSA<V>>,
-) {
+pub fn cached_function_matcher(function: &BNFunction) {
let platform = function.platform();
let platform_id = PlatformID::from(platform.as_ref());
let matcher_cache = PLAT_MATCHER_CACHE.get_or_init(Default::default);
match matcher_cache.get(&platform_id) {
- Some(matcher) => matcher.match_function(function, llil),
+ Some(matcher) => matcher.match_function(function),
None => {
let matcher = Matcher::from_platform(platform);
- matcher.match_function(function, llil);
+ matcher.match_function(function);
matcher_cache.insert(platform_id, matcher);
}
}
}
+// TODO: Maybe just clear individual platforms? This works well enough either way.
+pub fn invalidate_function_matcher_cache() {
+ let matcher_cache = PLAT_MATCHER_CACHE.get_or_init(Default::default);
+ matcher_cache.clear();
+}
+
pub struct Matcher {
- pub matched_functions: DashMap<FunctionID, Function>,
pub functions: DashMap<FunctionGUID, Vec<Function>>,
pub types: DashMap<TypeGUID, Type>,
pub named_types: DashMap<String, Type>,
- /// This is used to fast-fail on functions not in the dataset.
- /// NOTE: This can only handle one basic block classification, right now that is the entry block.
- basic_block_filter: BloomFilter,
}
impl Matcher {
@@ -83,20 +77,8 @@ impl Matcher {
// TODO: If a user signature has the same name as a core signature, remove the core signature.
- task.set_progress_text("Gathering entry blocks for matcher filtering...");
-
- // Get entry_blocks for filtering.
- let entry_blocks = data
- .iter()
- .flat_map(|(_, data)| data.functions.iter().map(|function| &function.entry))
- .collect::<Vec<_>>();
- // TODO: We need to disable this if we get a None basic block, as it will then fail to match all cases.
- let basic_block_filter = BloomFilter::with_false_pos(0.1).items(entry_blocks);
-
task.set_progress_text("Gathering matcher functions...");
- // TODO: Merge like functions, right now we just hope and pray.
-
// Get functions for comprehensive matching.
let functions = data
.iter()
@@ -148,9 +130,7 @@ impl Matcher {
log::debug!("Loaded signatures: {:?}", data.keys());
Self {
- matched_functions: Default::default(),
functions,
- basic_block_filter,
types,
named_types,
}
@@ -239,18 +219,8 @@ impl Matcher {
inner_add_type_to_view(self, view, arch, &mut HashSet::new(), ty)
}
- pub fn match_function<A: Architecture, M: FunctionMutability, V: NonSSAVariant>(
- &self,
- function: &BNFunction,
- llil: &llil::Function<A, M, NonSSA<V>>,
- ) {
- let function_id = FunctionID::from(function);
- if let Some(matched_function) = self.matched_functions.get(&function_id) {
- // Skip computing the match for already matched function.
- // We do still need to apply the match data through analysis updates.
- return on_matched_function(function, &matched_function);
- }
-
+ pub fn match_function(&self, function: &BNFunction) {
+ // Call this the first time you matched on the function.
let on_new_match = |matched: &Function| {
// We also want to resolve the types here.
if let TypeClass::Function(c) = matched.ty.class.as_ref() {
@@ -263,51 +233,32 @@ impl Matcher {
for in_member in &c.in_members {
self.add_type_to_view(&view, &arch, &in_member.ty);
}
- } else {
- // This should never happen.
- log::error!(
- "Matched function is not of function type class... 0x{:x}",
- function.start()
- );
}
- on_matched_function(function, matched);
-
- // We matched on the function, great! Now make sure we don't do this again :3
- self.matched_functions
- .insert(function_id, matched.to_owned());
// Also mark this for updates.
// TODO: Does this do anything?
function.mark_updates_required(FunctionUpdateType::UserFunctionUpdate);
};
- // TODO: Expand this check to be less broad.
- let is_function_trivial = { llil.instruction_count() < TRIVIAL_LLIL_THRESHOLD };
-
- // Check to see if the functions entry block is even in the dataset
- let entry_block = entry_basic_block_guid(function, llil).map(BasicBlock::new);
- if self.basic_block_filter.contains(&entry_block) {
- // Build the full function guid now
- let warp_func_guid = cached_function_guid(function, llil);
- if let Some(matched) = self.functions.get(&warp_func_guid) {
- if matched.len() == 1 && !is_function_trivial {
+ if let Some(matched_function) = cached_function_match(function, || {
+ // We have yet to match on this function.
+ // TODO: Expand this check to be less broad.
+ let function_delta = function.highest_address() - function.lowest_address();
+ let is_function_trivial = { function_delta < TRIVIAL_FUNCTION_DELTA_THRESHOLD };
+ let warp_func_guid = try_cached_function_guid(function)?;
+ match self.functions.get(&warp_func_guid) {
+ Some(matched) if matched.len() == 1 && !is_function_trivial => {
on_new_match(&matched[0]);
- } else if let Some(matched_function) =
- self.match_function_from_constraints(function, &matched)
- {
- log::debug!(
- "Found best matching function `{}`... 0x{:x}",
- matched_function.symbol.name,
- function.start()
- );
- on_new_match(matched_function);
- } else {
- log::debug!(
- "Failed to find matching function `{}`... 0x{:x}",
- matched.len(),
- function.start()
- );
+ Some(matched[0].to_owned())
}
+ Some(matched) => {
+ let matched_on = self.match_function_from_constraints(function, &matched)?;
+ on_new_match(matched_on);
+ Some(matched_on.to_owned())
+ }
+ None => None,
}
+ }) {
+ on_matched_function(function, &matched_function);
}
}
@@ -387,7 +338,17 @@ impl Matcher {
match highest_guid_count.cmp(&highest_symbol_count) {
Ordering::Less => matched_symbol_func,
Ordering::Greater => matched_guid_func,
- Ordering::Equal => None,
+ Ordering::Equal => {
+ // If the two highest our the same we can use it.
+ let ty_is_same = matched_guid_func?.ty == matched_symbol_func?.ty;
+ let sym_is_same = matched_guid_func?.symbol == matched_symbol_func?.symbol;
+ if ty_is_same && sym_is_same {
+ matched_guid_func
+ } else {
+ // We matched equally on two different functions
+ None
+ }
+ }
}
}
}
diff --git a/plugins/warp/src/plugin.rs b/plugins/warp/src/plugin.rs
index 3373cafd..ad08e842 100644
--- a/plugins/warp/src/plugin.rs
+++ b/plugins/warp/src/plugin.rs
@@ -1,5 +1,11 @@
use log::LevelFilter;
+use crate::build_function;
+use crate::cache::{
+ register_cache_destructor, ViewID, FUNCTION_CACHE, GUID_CACHE, MATCHED_FUNCTION_CACHE,
+};
+use crate::convert::{to_bn_symbol_at_address, to_bn_type};
+use crate::matcher::{PlatformID, PLAT_MATCHER_CACHE};
use binaryninja::binaryview::{BinaryView, BinaryViewExt};
use binaryninja::command::{Command, FunctionCommand};
use binaryninja::function::Function;
@@ -7,11 +13,6 @@ use binaryninja::rc::Ref;
use binaryninja::tags::TagType;
use warp::signature::function::Function as WarpFunction;
-use crate::build_function;
-use crate::cache::{ViewID, FUNCTION_CACHE, GUID_CACHE};
-use crate::convert::{to_bn_symbol_at_address, to_bn_type};
-use crate::matcher::{PlatformID, PLAT_MATCHER_CACHE};
-
mod apply;
mod copy;
mod create;
@@ -67,12 +68,17 @@ struct DebugCache;
impl Command for DebugCache {
fn action(&self, view: &BinaryView) {
- let function_cache = FUNCTION_CACHE.get_or_init(Default::default);
let view_id = ViewID::from(view);
+ let function_cache = FUNCTION_CACHE.get_or_init(Default::default);
if let Some(cache) = function_cache.get(&view_id) {
log::info!("View functions: {}", cache.cache.len());
}
+ let matched_function_cache = MATCHED_FUNCTION_CACHE.get_or_init(Default::default);
+ if let Some(cache) = matched_function_cache.get(&view_id) {
+ log::info!("View matched functions: {}", cache.cache.len());
+ }
+
let function_guid_cache = GUID_CACHE.get_or_init(Default::default);
if let Some(cache) = function_guid_cache.get(&view_id) {
log::info!("View function guids: {}", cache.cache.len());
@@ -84,10 +90,6 @@ impl Command for DebugCache {
if let Some(cache) = plat_cache.get(&platform_id) {
log::info!("Platform functions: {}", cache.functions.len());
log::info!("Platform types: {}", cache.types.len());
- log::info!(
- "Platform matched functions: {}",
- cache.matched_functions.len()
- );
}
}
}
@@ -102,7 +104,10 @@ impl Command for DebugCache {
pub extern "C" fn CorePluginInit() -> bool {
binaryninja::logger::init(LevelFilter::Debug).unwrap();
- workflow::insert_matcher_workflow();
+ // Make sure caches are flushed when the views get destructed.
+ register_cache_destructor();
+
+ workflow::insert_workflow();
binaryninja::command::register(
"WARP\\Apply Signature File Types",
diff --git a/plugins/warp/src/plugin/create.rs b/plugins/warp/src/plugin/create.rs
index b404ae1c..966c69ca 100644
--- a/plugins/warp/src/plugin/create.rs
+++ b/plugins/warp/src/plugin/create.rs
@@ -1,5 +1,6 @@
use crate::cache::cached_function;
use crate::convert::from_bn_type;
+use crate::matcher::invalidate_function_matcher_cache;
use binaryninja::binaryview::{BinaryView, BinaryViewExt};
use binaryninja::command::Command;
use rayon::prelude::*;
@@ -15,7 +16,6 @@ pub struct CreateSignatureFile;
impl Command for CreateSignatureFile {
fn action(&self, view: &BinaryView) {
let mut signature_dir = binaryninja::user_directory().unwrap().join("signatures/");
- // TODO: This needs to split out each platform into its own bucket...
if let Some(default_plat) = view.default_platform() {
// If there is a default platform, put the signature in there.
signature_dir.push(default_plat.name().to_string());
@@ -53,7 +53,11 @@ impl Command for CreateSignatureFile {
// TODO: Should we overwrite? Prompt user.
if let Ok(mut file) = std::fs::File::create(&save_file) {
match file.write_all(&data.to_bytes()) {
- Ok(_) => log::info!("Signature file saved successfully."),
+ Ok(_) => {
+ log::info!("Signature file saved successfully.");
+ // Force rebuild platform matcher.
+ invalidate_function_matcher_cache();
+ }
Err(e) => log::error!("Failed to write data to signature file: {:?}", e),
}
} else {
diff --git a/plugins/warp/src/plugin/types.rs b/plugins/warp/src/plugin/types.rs
index ecb60428..5ddff403 100644
--- a/plugins/warp/src/plugin/types.rs
+++ b/plugins/warp/src/plugin/types.rs
@@ -23,7 +23,7 @@ impl Command for LoadTypesCommand {
log::error!("Could not get data from signature file: {:?}", file);
return;
};
-
+
let Some(arch) = view.default_arch() else {
log::error!("Could not get default architecture");
return;
diff --git a/plugins/warp/src/plugin/workflow.rs b/plugins/warp/src/plugin/workflow.rs
index da8473f0..bd47c117 100644
--- a/plugins/warp/src/plugin/workflow.rs
+++ b/plugins/warp/src/plugin/workflow.rs
@@ -1,38 +1,66 @@
-use crate::matcher::cached_function_match;
+use crate::cache::cached_function_guid;
+use crate::matcher::cached_function_matcher;
+use binaryninja::backgroundtask::BackgroundTask;
+use binaryninja::binaryview::BinaryViewExt;
use binaryninja::llil;
use binaryninja::workflow::{Activity, AnalysisContext, Workflow};
+use std::time::Instant;
-const MATCHER_ACTIVITY_NAME: &str = "analysis.plugins.WARPMatcher";
+const MATCHER_ACTIVITY_NAME: &str = "analysis.plugins.warp.matcher";
// NOTE: runOnce is off because previously matched functions need info applied.
const MATCHER_ACTIVITY_CONFIG: &str = r#"{
- "name": "analysis.plugins.WARPMatcher",
+ "name": "analysis.plugins.warp.matcher",
"title" : "WARP Matcher",
"description": "This analysis step applies WARP info to matched functions...",
"eligibility": {
- "auto": { "default": true },
+ "auto": {},
"runOnce": false
}
}"#;
-pub fn insert_matcher_workflow() {
+const GUID_ACTIVITY_NAME: &str = "analysis.plugins.warp.guid";
+const GUID_ACTIVITY_CONFIG: &str = r#"{
+ "name": "analysis.plugins.warp.guid",
+ "title" : "WARP GUID Generator",
+ "description": "This analysis step generates the GUID for all analyzed functions...",
+ "eligibility": {
+ "auto": {},
+ "runOnce": true
+ }
+}"#;
+
+pub fn insert_workflow() {
let matcher_activity = |ctx: &AnalysisContext| {
- let function = ctx.function();
- if function.has_user_annotations() {
- // User has touched the function, stop trying to match on it!
- return;
- }
+ let view = ctx.view();
+ let background_task = BackgroundTask::new("Matching on functions...", false).unwrap();
+ let start = Instant::now();
+ view.functions()
+ .iter()
+ .for_each(|function| cached_function_matcher(&function));
+ log::info!("Function matching took {:?}", start.elapsed());
+ background_task.finish();
+ };
+ let guid_activity = |ctx: &AnalysisContext| {
+ let function = ctx.function();
if let Some(llil) = unsafe { ctx.llil_function::<llil::NonSSA<llil::RegularNonSSA>>() } {
- cached_function_match(&function, &llil);
+ cached_function_guid(&function, &llil);
}
};
- let meta_workflow = Workflow::new_from_copy("core.function.metaAnalysis");
- let activity = Activity::new_with_action(MATCHER_ACTIVITY_CONFIG, matcher_activity);
- meta_workflow.register_activity(&activity).unwrap();
- meta_workflow.insert(
- "core.function.runFunctionRecognizers",
- [MATCHER_ACTIVITY_NAME],
- );
- meta_workflow.register().unwrap();
+ let function_meta_workflow = Workflow::new_from_copy("core.function.metaAnalysis");
+ let guid_activity = Activity::new_with_action(GUID_ACTIVITY_CONFIG, guid_activity);
+ function_meta_workflow
+ .register_activity(&guid_activity)
+ .unwrap();
+ function_meta_workflow.insert("core.function.runFunctionRecognizers", [GUID_ACTIVITY_NAME]);
+ function_meta_workflow.register().unwrap();
+
+ let module_meta_workflow = Workflow::new_from_copy("core.module.metaAnalysis");
+ let matcher_activity = Activity::new_with_action(MATCHER_ACTIVITY_CONFIG, matcher_activity);
+ module_meta_workflow
+ .register_activity(&matcher_activity)
+ .unwrap();
+ module_meta_workflow.insert("core.module.notifyCompletion", [MATCHER_ACTIVITY_NAME]);
+ module_meta_workflow.register().unwrap();
}