diff options
| author | Mason Reed <mason@vector35.com> | 2024-11-07 17:03:39 -0500 |
|---|---|---|
| committer | Mason Reed <mason@vector35.com> | 2024-11-08 03:13:53 -0500 |
| commit | d24a2aab98477025f6d5311053490d9fa14deef4 (patch) | |
| tree | a95dde0f229df2abeb3f591908c37211dba42da6 /plugins/warp/src/matcher.rs | |
| parent | e348a79c132b1003b5bc5021f64ba205462248fd (diff) | |
WARP: Add manual file loading and initial user settings
Also just some general cleanup in the matcher
Diffstat (limited to 'plugins/warp/src/matcher.rs')
| -rw-r--r-- | plugins/warp/src/matcher.rs | 128 |
1 files changed, 72 insertions, 56 deletions
diff --git a/plugins/warp/src/matcher.rs b/plugins/warp/src/matcher.rs index 91405381..72751c4c 100644 --- a/plugins/warp/src/matcher.rs +++ b/plugins/warp/src/matcher.rs @@ -22,9 +22,6 @@ use crate::cache::{cached_call_site_constraints, cached_function_match, try_cach use crate::convert::to_bn_type; use crate::plugin::on_matched_function; -// TODO: Make this configurable. -pub const TRIVIAL_FUNCTION_DELTA_THRESHOLD: u64 = 20; - pub static PLAT_MATCHER_CACHE: OnceLock<DashMap<PlatformID, Matcher>> = OnceLock::new(); pub fn cached_function_matcher(function: &BNFunction) { @@ -47,7 +44,11 @@ pub fn invalidate_function_matcher_cache() { matcher_cache.clear(); } +#[derive(Debug, Default, Clone)] pub struct Matcher { + // TODO: Storing the settings here means that they are effectively global. + // TODO: If we want scoped or view settings they must be moved out. + pub settings: MatcherSettings, pub functions: DashMap<FunctionGUID, Vec<Function>>, pub types: DashMap<TypeGUID, Type>, pub named_types: DashMap<String, Type>, @@ -57,11 +58,6 @@ impl Matcher { /// Create a matcher from the platforms signature subdirectory. pub fn from_platform(platform: BNRef<Platform>) -> Self { let platform_name = platform.name().to_string(); - let task = BackgroundTask::new( - format!("Getting platform matcher data... {}", platform_name), - false, - ) - .unwrap(); // Get core signatures for the given platform let install_dir = binaryninja::install_directory().unwrap(); let core_dir = install_dir.parent().unwrap(); @@ -80,68 +76,44 @@ impl Matcher { let user_data = get_data_from_dir(&plat_user_sig_dir); data.extend(user_data); + let merged_data = Data::merge(&data.values().cloned().collect::<Vec<_>>()); + log::debug!("Loaded signatures: {:?}", data.keys()); + Matcher::from_data(merged_data) + } - // TODO: If a user signature has the same name as a core signature, remove the core signature. - - task.set_progress_text("Gathering matcher functions..."); - - // Get functions for comprehensive matching. + pub fn from_data(data: Data) -> Self { let functions = data - .iter() - .flat_map(|(_, data)| { - data.functions.iter().fold(DashMap::new(), |map, func| { - #[allow(clippy::unwrap_or_default)] - map.entry(func.guid) - .or_insert_with(Vec::new) - .push(func.clone()); - map - }) - }) - .map(|(guid, mut funcs)| { - funcs.sort_by_key(|f| f.symbol.name.to_owned()); - funcs.dedup_by_key(|f| f.symbol.name.to_owned()); - (guid, funcs) - }) - .collect(); - - task.set_progress_text("Gathering matcher types..."); - + .functions + .into_iter() + .fold(DashMap::new(), |map, func| { + map.entry(func.guid).or_insert_with(Vec::new).push(func); + map + }); let types = data + .types .iter() - .flat_map(|(_, data)| { - data.types.iter().fold(DashMap::new(), |map, comp_ty| { - map.insert(comp_ty.guid, comp_ty.ty.clone()); - map - }) - }) + .map(|ty| (ty.guid, ty.ty.clone())) .collect(); - - task.set_progress_text("Gathering matcher named types..."); - - // TODO: We store a duplicate lookup for named references. let named_types = data - .iter() - .flat_map(|(_, data)| { - data.types.iter().fold(DashMap::new(), |map, comp_ty| { - if let Some(ty_name) = &comp_ty.ty.name { - map.insert(ty_name.to_owned(), comp_ty.ty.clone()); - } - map - }) - }) + .types + .into_iter() + .filter_map(|ty| ty.ty.name.to_owned().map(|name| (name, ty.ty))) .collect(); - task.finish(); - - log::debug!("Loaded signatures: {:?}", data.keys()); - Self { + settings: Default::default(), functions, types, named_types, } } + pub fn extend_with_matcher(&mut self, matcher: Matcher) { + self.functions.extend(matcher.functions.into_iter()); + self.types.extend(matcher.types.into_iter()); + self.named_types.extend(matcher.named_types.into_iter()); + } + pub fn add_type_to_view<A: BNArchitecture>(&self, view: &BinaryView, arch: &A, ty: &Type) { fn inner_add_type_to_view<A: BNArchitecture>( matcher: &Matcher, @@ -246,7 +218,7 @@ impl Matcher { // 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 is_function_trivial = { function_delta < self.settings.trivial_function_len }; 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 => { @@ -371,6 +343,50 @@ fn get_data_from_dir(dir: &PathBuf) -> HashMap<PathBuf, Data> { .collect() } +#[derive(Debug, Clone)] +pub struct MatcherSettings { + /// Any function under this length will be required to constrain. + /// + /// This is set to [MatcherSettings::DEFAULT_TRIVIAL_FUNCTION_LEN] by default. + pub trivial_function_len: u64, +} + +impl MatcherSettings { + pub const TRIVIAL_FUNCTION_LEN_DEFAULT: u64 = 20; + pub const TRIVIAL_FUNCTION_LEN_SETTING: &'static str = "analysis.warp.trivial_function_len"; + + /// Populates the [MatcherSettings] to the current Binary Ninja settings instance. + /// + /// Call this once when you initialize so that the settings exist. + pub fn write_settings(&self) { + let bn_settings = binaryninja::settings::Settings::new(""); + bn_settings.set_integer( + Self::TRIVIAL_FUNCTION_LEN_SETTING, + self.trivial_function_len, + None, + None, + ); + } + + pub fn from_view(view: &BinaryView) -> Self { + let mut settings = MatcherSettings::default(); + let bn_settings = binaryninja::settings::Settings::new(""); + if bn_settings.contains(Self::TRIVIAL_FUNCTION_LEN_SETTING) { + settings.trivial_function_len = + bn_settings.get_integer(Self::TRIVIAL_FUNCTION_LEN_SETTING, None, None); + } + settings + } +} + +impl Default for MatcherSettings { + fn default() -> Self { + Self { + trivial_function_len: MatcherSettings::TRIVIAL_FUNCTION_LEN_DEFAULT, + } + } +} + /// A unique platform ID, used for caching. #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct PlatformID(u64); |
