diff options
Diffstat (limited to 'plugins/warp/src/plugin')
| -rw-r--r-- | plugins/warp/src/plugin/commit.rs | 150 | ||||
| -rw-r--r-- | plugins/warp/src/plugin/ffi.rs | 12 | ||||
| -rw-r--r-- | plugins/warp/src/plugin/ffi/container.rs | 271 | ||||
| -rw-r--r-- | plugins/warp/src/plugin/ffi/file.rs | 38 | ||||
| -rw-r--r-- | plugins/warp/src/plugin/ffi/function.rs | 2 | ||||
| -rw-r--r-- | plugins/warp/src/plugin/load.rs | 11 | ||||
| -rw-r--r-- | plugins/warp/src/plugin/settings.rs | 76 | ||||
| -rw-r--r-- | plugins/warp/src/plugin/workflow.rs | 10 |
8 files changed, 551 insertions, 19 deletions
diff --git a/plugins/warp/src/plugin/commit.rs b/plugins/warp/src/plugin/commit.rs new file mode 100644 index 00000000..2af5fc3e --- /dev/null +++ b/plugins/warp/src/plugin/commit.rs @@ -0,0 +1,150 @@ +//! Commit file to a source. + +use crate::cache::container::cached_containers; +use crate::container::{SourceId, SourcePath}; +use crate::plugin::create::OpenFileField; +use binaryninja::binary_view::BinaryView; +use binaryninja::command::Command; +use binaryninja::interaction::{Form, FormInputField}; +use warp::chunk::ChunkKind; +use warp::WarpFile; + +pub struct SelectedSourceField { + sources: Vec<(SourceId, SourcePath)>, +} + +impl SelectedSourceField { + pub fn field(&self) -> FormInputField { + FormInputField::Choice { + prompt: "Selected Source".to_string(), + choices: self + .sources + .iter() + .map(|(id, path)| { + // For display purposes we only want to show the last path item. + let path_name = path + .to_string() + .rsplit_once('/') + .map_or(path.to_string(), |(_, last_path_item)| { + last_path_item.to_string() + }); + // TODO: Probably have a truncation limit here, this is just for display after all. + format!("{} ({})", path_name, id) + }) + .collect(), + default: None, + value: 0, + } + } + + pub fn from_form(&self, form: &Form) -> Option<SourceId> { + let field = form.get_field_with_name("Selected Source")?; + let field_value = field.try_value_index()?; + self.sources.get(field_value).map(|(id, _)| *id) + } +} + +pub struct CommitFile; + +impl CommitFile { + pub fn selected_source_field() -> SelectedSourceField { + let mut writable_sources = Vec::new(); + for container in cached_containers() { + if let Ok(container) = container.read() { + for source in container.sources().unwrap_or_default() { + if let Ok(true) = container.is_source_writable(&source) { + if let Ok(source_path) = container.source_path(&source) { + writable_sources.push((source, source_path)); + } + } + } + } + } + SelectedSourceField { + sources: writable_sources, + } + } + + pub fn execute() -> Option<()> { + let mut form = Form::new("Commit File"); + + // Users are going to get confused between this and adding functions to a source then commiting. + // So we should make it clear with a label, and also probably deprecate this command and replace it with "add functions to source" and "commit source". + form.add_field(FormInputField::Label { + prompt: "Commits a WARP file to an existing source, this is primarily used for committing to network containers".to_string() + }); + + form.add_field(OpenFileField::field()); + let source_field = Self::selected_source_field(); + form.add_field(source_field.field()); + + if !form.prompt() { + return None; + } + + let open_file_path = OpenFileField::from_form(&form)?; + let source_id = source_field.from_form(&form)?; + log::info!("Committing file to source: {}", source_id); + + let bytes = std::fs::read(open_file_path).ok()?; + let Some(warp_file) = WarpFile::from_bytes(&bytes) else { + log::error!("Failed to parse warp file!"); + return None; + }; + + for container in cached_containers() { + let Ok(mut container) = container.write() else { + continue; + }; + + if let Ok(true) = container.is_source_writable(&source_id) { + // TODO: We need to find a sane way to do this procedure through the FFI. + for chunk in &warp_file.chunks { + match &chunk.kind { + ChunkKind::Signature(sc) => { + let functions: Vec<_> = sc.functions().collect(); + log::info!( + "Adding {} functions to source: {}", + functions.len(), + source_id + ); + if let Err(e) = container.add_functions( + &chunk.header.target, + &source_id, + &functions, + ) { + log::error!("Failed to add functions to source: {}", e); + } + } + ChunkKind::Type(sc) => { + let types: Vec<_> = sc.types().collect(); + log::info!("Adding {} types to source: {}", types.len(), source_id); + if let Err(e) = container.add_computed_types(&source_id, &types) { + log::error!("Failed to add types to source: {}", e); + } + } + } + } + if let Err(e) = container.commit_source(&source_id) { + log::error!("Failed to commit source: {}", e); + } + log::info!("Committed file to source: {}", source_id); + return Some(()); + } + } + + Some(()) + } +} + +impl Command for CommitFile { + fn action(&self, _view: &BinaryView) { + std::thread::spawn(move || { + Self::execute(); + }); + } + + fn valid(&self, _view: &BinaryView) -> bool { + true + } +} diff --git a/plugins/warp/src/plugin/ffi.rs b/plugins/warp/src/plugin/ffi.rs index 2826ed34..c1f5acb8 100644 --- a/plugins/warp/src/plugin/ffi.rs +++ b/plugins/warp/src/plugin/ffi.rs @@ -1,4 +1,5 @@ mod container; +mod file; mod function; use binaryninjacore_sys::{ @@ -88,6 +89,17 @@ pub unsafe extern "C" fn BNWARPUUIDGetString(uuid: *const Uuid) -> *mut c_char { } #[no_mangle] +pub unsafe extern "C" fn BNWARPUUIDFromString(uuid_str: *mut c_char, uuid: *mut Uuid) -> bool { + if let Ok(uuid_str) = std::ffi::CStr::from_ptr(uuid_str).to_str() { + if let Some(parsed_uuid) = Uuid::parse_str(uuid_str).ok() { + *uuid = parsed_uuid; + return true; + } + } + false +} + +#[no_mangle] pub unsafe extern "C" fn BNWARPUUIDEqual(a: *const Uuid, b: *const Uuid) -> bool { (*a) == (*b) } diff --git a/plugins/warp/src/plugin/ffi/container.rs b/plugins/warp/src/plugin/ffi/container.rs index 4de6bab3..72c85366 100644 --- a/plugins/warp/src/plugin/ffi/container.rs +++ b/plugins/warp/src/plugin/ffi/container.rs @@ -1,5 +1,7 @@ use crate::cache::container::cached_containers; -use crate::container::SourcePath; +use crate::container::{ + ContainerSearchItem, ContainerSearchItemKind, ContainerSearchQuery, SourcePath, SourceTag, +}; use crate::convert::{from_bn_type, to_bn_type}; use crate::plugin::ffi::{ BNWARPContainer, BNWARPFunction, BNWARPFunctionGUID, BNWARPSource, BNWARPTarget, BNWARPTypeGUID, @@ -12,7 +14,163 @@ use binaryninja::types::Type; use binaryninjacore_sys::{BNArchitecture, BNBinaryView, BNType}; use std::ffi::{c_char, CStr}; use std::mem::ManuallyDrop; +use std::ops::Deref; use std::sync::Arc; +use warp::r#type::guid::TypeGUID; + +pub type BNWARPContainerSearchQuery = ContainerSearchQuery; +pub type BNWARPContainerSearchItem = ContainerSearchItem; + +#[repr(C)] +pub enum BNWARPContainerSearchItemKind { + Source = 0, + Function = 1, + Type = 2, + Symbol = 3, +} + +#[repr(C)] +pub struct BNWARPContainerSearchResponse { + pub count: usize, + pub items: *mut *mut BNWARPContainerSearchItem, + pub offset: usize, + pub total: usize, +} + +#[no_mangle] +pub unsafe extern "C" fn BNWARPNewContainerSearchQuery( + query: *mut c_char, + offset: *const usize, + limit: *const usize, + source: *const BNWARPSource, + source_tags: *mut *mut c_char, + source_tags_count: usize, +) -> *mut BNWARPContainerSearchQuery { + let query_cstr = unsafe { CStr::from_ptr(query) }; + let Ok(query) = query_cstr.to_str() else { + return std::ptr::null_mut(); + }; + let mut search_query = ContainerSearchQuery::new(query.to_string()); + if !offset.is_null() { + search_query.offset = Some(*offset); + } + if !limit.is_null() { + search_query.limit = Some(*limit); + } + if !source.is_null() { + search_query.source = Some(*source); + } + if !source_tags.is_null() { + let source_tags_raw = unsafe { std::slice::from_raw_parts(source_tags, source_tags_count) }; + let source_tags: Vec<SourceTag> = source_tags_raw + .iter() + .filter_map(|&ptr| CStr::from_ptr(ptr).to_str().ok()) + .map(|s| s.into()) + .collect(); + search_query.tags = source_tags; + } + Box::into_raw(Box::new(search_query)) +} + +#[no_mangle] +pub unsafe extern "C" fn BNWARPContainerSearchItemGetKind( + item: *mut BNWARPContainerSearchItem, +) -> BNWARPContainerSearchItemKind { + let item = ManuallyDrop::new(Arc::from_raw(item)); + match &item.kind { + ContainerSearchItemKind::Source { .. } => BNWARPContainerSearchItemKind::Source, + ContainerSearchItemKind::Function(_) => BNWARPContainerSearchItemKind::Function, + ContainerSearchItemKind::Type(_) => BNWARPContainerSearchItemKind::Type, + ContainerSearchItemKind::Symbol(_) => BNWARPContainerSearchItemKind::Symbol, + } +} + +#[no_mangle] +pub unsafe extern "C" fn BNWARPContainerSearchItemGetSource( + item: *mut BNWARPContainerSearchItem, +) -> BNWARPSource { + let item = ManuallyDrop::new(Arc::from_raw(item)); + item.source +} + +#[no_mangle] +pub unsafe extern "C" fn BNWARPContainerSearchItemGetType( + arch: *mut BNArchitecture, + item: *mut BNWARPContainerSearchItem, +) -> *mut BNType { + // NOTE: to convert the type, we must have an architecture. + let arch = match !arch.is_null() { + true => Some(CoreArchitecture::from_raw(arch)), + false => None, + }; + + let item = ManuallyDrop::new(Arc::from_raw(item)); + match &item.kind { + ContainerSearchItemKind::Source { .. } => std::ptr::null_mut(), + ContainerSearchItemKind::Function(func) => { + match &func.ty { + None => std::ptr::null_mut(), + Some(ty) => { + let bn_ty = to_bn_type(arch, &ty); + // NOTE: The type ref has been pre-incremented for the caller. + unsafe { Ref::into_raw(bn_ty) }.handle + } + } + } + ContainerSearchItemKind::Type(ty) => { + let bn_ty = to_bn_type(arch, &ty); + // NOTE: The type ref has been pre-incremented for the caller. + unsafe { Ref::into_raw(bn_ty) }.handle + } + ContainerSearchItemKind::Symbol(_) => std::ptr::null_mut(), + } +} + +// NOTE: In the future we should allow for the possibility of this returning a null pointer. +#[no_mangle] +pub unsafe extern "C" fn BNWARPContainerSearchItemGetName( + item: *mut BNWARPContainerSearchItem, +) -> *mut c_char { + let item = ManuallyDrop::new(Arc::from_raw(item)); + match &item.kind { + ContainerSearchItemKind::Source { path, .. } => { + let bn_name = BnString::new(path.to_string()); + BnString::into_raw(bn_name) + } + ContainerSearchItemKind::Function(func) => { + let bn_name = BnString::new(func.symbol.name.clone()); + BnString::into_raw(bn_name) + } + ContainerSearchItemKind::Type(ty) => { + // TODO: Maybe un-named types should return std::ptr::null_mut()? + let ty_name = ty + .name + .clone() + .unwrap_or_else(|| TypeGUID::from(ty).to_string()); + let bn_name = BnString::new(ty_name); + BnString::into_raw(bn_name) + } + ContainerSearchItemKind::Symbol(sym) => { + let bn_name = BnString::new(sym.name.clone()); + BnString::into_raw(bn_name) + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn BNWARPContainerSearchItemGetFunction( + item: *mut BNWARPContainerSearchItem, +) -> *mut BNWARPFunction { + let item = ManuallyDrop::new(Arc::from_raw(item)); + match &item.kind { + ContainerSearchItemKind::Source { .. } => std::ptr::null_mut(), + ContainerSearchItemKind::Function(func) => { + Arc::into_raw(Arc::new(func.clone())) as *mut BNWARPFunction + } + ContainerSearchItemKind::Type(_) => std::ptr::null_mut(), + ContainerSearchItemKind::Symbol(_) => std::ptr::null_mut(), + } +} #[no_mangle] pub unsafe extern "C" fn BNWARPGetContainers(count: *mut usize) -> *mut *mut BNWARPContainer { @@ -39,6 +197,8 @@ pub unsafe extern "C" fn BNWARPContainerGetName(container: *mut BNWARPContainer) pub unsafe extern "C" fn BNWARPContainerFetchFunctions( container: *mut BNWARPContainer, target: *mut BNWARPTarget, + source_tags: *mut *mut c_char, + source_tags_count: usize, guids: *const BNWARPFunctionGUID, count: usize, ) { @@ -49,9 +209,16 @@ pub unsafe extern "C" fn BNWARPContainerFetchFunctions( let target = unsafe { ManuallyDrop::new(Arc::from_raw(target)) }; + let source_tags_raw = unsafe { std::slice::from_raw_parts(source_tags, source_tags_count) }; + let source_tags: Vec<SourceTag> = source_tags_raw + .iter() + .filter_map(|&ptr| CStr::from_ptr(ptr).to_str().ok()) + .map(|s| s.into()) + .collect(); + let guids = unsafe { std::slice::from_raw_parts(guids, count) }; - if let Err(e) = container.fetch_functions(&target, guids) { + if let Err(e) = container.fetch_functions(&target, &source_tags, guids) { log::error!("Failed to fetch functions: {}", e); } } @@ -373,7 +540,7 @@ pub unsafe extern "C" fn BNWARPContainerGetTypeWithGUID( let Some(ty) = container.type_with_guid(&source, &guid).unwrap_or_default() else { return std::ptr::null_mut(); }; - let function_type = to_bn_type(&arch, &ty); + let function_type = to_bn_type(Some(arch), &ty); // NOTE: The type ref has been pre-incremented for the caller. unsafe { Ref::into_raw(function_type) }.handle } @@ -405,6 +572,45 @@ pub unsafe extern "C" fn BNWARPContainerGetTypeGUIDsWithName( } #[no_mangle] +pub unsafe extern "C" fn BNWARPContainerSearch( + container: *mut BNWARPContainer, + query: *mut BNWARPContainerSearchQuery, +) -> *mut BNWARPContainerSearchResponse { + let arc_container = ManuallyDrop::new(Arc::from_raw(container)); + let Ok(container) = arc_container.read() else { + return std::ptr::null_mut(); + }; + + let query = unsafe { ManuallyDrop::new(Arc::from_raw(query)) }; + + let result = match container.search(&query) { + Ok(result) => result, + Err(err) => { + log::error!("Failed to search container {:?}: {}", query.deref(), err); + return std::ptr::null_mut(); + } + }; + + let boxed_raw_items: Box<[_]> = result + .items + .into_iter() + .map(Arc::new) + .map(Arc::into_raw) + .collect(); + let count = boxed_raw_items.len(); + // NOTE: Leak the functions to be freed by BNWARPFreeContainerSearchItemList + let leaked_raw_items = Box::into_raw(boxed_raw_items) as *mut *mut BNWARPContainerSearchItem; + let raw_result = BNWARPContainerSearchResponse { + count, + items: leaked_raw_items, + total: result.total, + offset: result.offset, + }; + // NOTE: Leak the result to be freed by BNWARPFreeContainerSearchResult + Box::into_raw(Box::new(raw_result)) +} + +#[no_mangle] pub unsafe extern "C" fn BNWARPNewContainerReference( container: *mut BNWARPContainer, ) -> *mut BNWARPContainer { @@ -431,3 +637,62 @@ pub unsafe extern "C" fn BNWARPFreeContainerList( BNWARPFreeContainerReference(container); } } + +#[no_mangle] +pub unsafe extern "C" fn BNWARPNewContainerSearchQueryReference( + query: *mut BNWARPContainerSearchQuery, +) -> *mut BNWARPContainerSearchQuery { + Arc::increment_strong_count(query); + query +} + +#[no_mangle] +pub unsafe extern "C" fn BNWARPFreeContainerSearchQueryReference( + query: *mut BNWARPContainerSearchQuery, +) { + if query.is_null() { + return; + } + Arc::decrement_strong_count(query); +} + +#[no_mangle] +pub unsafe extern "C" fn BNWARPNewContainerSearchItemReference( + item: *mut BNWARPContainerSearchItem, +) -> *mut BNWARPContainerSearchItem { + Arc::increment_strong_count(item); + item +} + +#[no_mangle] +pub unsafe extern "C" fn BNWARPFreeContainerSearchItemReference( + item: *mut BNWARPContainerSearchItem, +) { + if item.is_null() { + return; + } + Arc::decrement_strong_count(item); +} + +#[no_mangle] +pub unsafe extern "C" fn BNWARPFreeContainerSearchItemList( + items: *mut *mut BNWARPContainerSearchItem, + count: usize, +) { + let items_ptr = std::ptr::slice_from_raw_parts_mut(items, count); + let items = unsafe { Box::from_raw(items_ptr) }; + for item in items { + BNWARPFreeContainerSearchItemReference(item); + } +} + +#[no_mangle] +pub unsafe extern "C" fn BNWARPFreeContainerSearchResponse( + response: *mut BNWARPContainerSearchResponse, +) { + if response.is_null() { + return; + } + let response = unsafe { Box::from_raw(response) }; + BNWARPFreeContainerSearchItemList(response.items, response.count); +} diff --git a/plugins/warp/src/plugin/ffi/file.rs b/plugins/warp/src/plugin/ffi/file.rs new file mode 100644 index 00000000..951b5eb2 --- /dev/null +++ b/plugins/warp/src/plugin/ffi/file.rs @@ -0,0 +1,38 @@ +use std::ffi::c_char; +use std::sync::Arc; +use warp::WarpFile; + +pub type BNWARPFile = WarpFile<'static>; + +// TODO: At some point we may want to expose chunks directly. For now we will just enumerate all of them. +// pub type BNWARPChunk = warp::chunk::Chunk<'static>; + +// TODO: From bytes as well. +#[no_mangle] +pub unsafe extern "C" fn BNWARPNewFileFromPath(path: *mut c_char) -> *mut BNWARPFile { + let path_cstr = unsafe { std::ffi::CStr::from_ptr(path) }; + let Ok(path) = path_cstr.to_str() else { + return std::ptr::null_mut(); + }; + let Ok(bytes) = std::fs::read(path) else { + return std::ptr::null_mut(); + }; + let Some(file) = WarpFile::from_owned_bytes(bytes) else { + return std::ptr::null_mut(); + }; + Arc::into_raw(Arc::new(file)) as *mut BNWARPFile +} + +#[no_mangle] +pub unsafe extern "C" fn BNWARPNewFileReference(file: *mut BNWARPFile) -> *mut BNWARPFile { + Arc::increment_strong_count(file); + file +} + +#[no_mangle] +pub unsafe extern "C" fn BNWARPFreeFileReference(file: *mut BNWARPFile) { + if file.is_null() { + return; + } + Arc::decrement_strong_count(file); +} diff --git a/plugins/warp/src/plugin/ffi/function.rs b/plugins/warp/src/plugin/ffi/function.rs index b37d613b..e3f6a079 100644 --- a/plugins/warp/src/plugin/ffi/function.rs +++ b/plugins/warp/src/plugin/ffi/function.rs @@ -124,7 +124,7 @@ pub unsafe extern "C" fn BNWARPFunctionGetType( match &function.ty { Some(func_ty) => { let arch = analysis_function.arch(); - let function_type = to_bn_type(&arch, func_ty); + let function_type = to_bn_type(Some(arch), func_ty); // NOTE: The type ref has been pre-incremented for the caller. unsafe { Ref::into_raw(function_type) }.handle } diff --git a/plugins/warp/src/plugin/load.rs b/plugins/warp/src/plugin/load.rs index cef6ea11..6f8b562e 100644 --- a/plugins/warp/src/plugin/load.rs +++ b/plugins/warp/src/plugin/load.rs @@ -40,16 +40,15 @@ pub struct RunMatcherField; impl RunMatcherField { pub fn field() -> FormInputField { - FormInputField::Choice { - prompt: "Rerun Initial Matcher".to_string(), - choices: vec!["No".to_string(), "Yes".to_string()], - default: Some(1), - value: 0, + FormInputField::Checkbox { + prompt: "Rerun Matcher".to_string(), + default: Some(true), + value: false, } } pub fn from_form(form: &Form) -> Option<bool> { - let field = form.get_field_with_name("Rerun Initial Matcher")?; + let field = form.get_field_with_name("Rerun Matcher")?; let field_value = field.try_value_index()?; match field_value { 1 => Some(true), diff --git a/plugins/warp/src/plugin/settings.rs b/plugins/warp/src/plugin/settings.rs index 489deb23..39092177 100644 --- a/plugins/warp/src/plugin/settings.rs +++ b/plugins/warp/src/plugin/settings.rs @@ -1,4 +1,4 @@ -use binaryninja::settings::Settings as BNSettings; +use binaryninja::settings::{QueryOptions, Settings as BNSettings}; use serde_json::json; use std::string::ToString; @@ -20,6 +20,12 @@ pub struct PluginSettings { /// /// This is set to [PluginSettings::SERVER_API_KEY_DEFAULT] by default. pub server_api_key: Option<String>, + pub second_server_url: Option<String>, + pub second_server_api_key: Option<String>, + /// A source must have at least one of these tags to be considered a valid source. + /// + /// This is set to [PluginSettings::SOURCE_TAGS_DEFAULT] by default. + pub whitelisted_source_tags: Vec<String>, /// Whether to allow networked WARP requests. Turning this off will not disable local WARP functionality. /// /// This is set to [PluginSettings::ENABLE_SERVER_DEFAULT] by default. @@ -27,6 +33,8 @@ pub struct PluginSettings { } impl PluginSettings { + pub const WHITELISTED_SOURCE_TAGS_DEFAULT: Vec<String> = vec![]; + pub const WHITELISTED_SOURCE_TAGS_SETTING: &'static str = "analysis.warp.whitelistedSourceTags"; pub const LOAD_BUNDLED_FILES_DEFAULT: bool = true; pub const LOAD_BUNDLED_FILES_SETTING: &'static str = "analysis.warp.loadBundledFiles"; pub const LOAD_USER_FILES_DEFAULT: bool = true; @@ -35,10 +43,25 @@ impl PluginSettings { pub const SERVER_URL_SETTING: &'static str = "analysis.warp.serverUrl"; pub const SERVER_API_KEY_DEFAULT: Option<String> = None; pub const SERVER_API_KEY_SETTING: &'static str = "analysis.warp.serverApiKey"; + pub const SECONDARY_SERVER_URL_DEFAULT: Option<String> = None; + pub const SECONDARY_SERVER_URL_SETTING: &'static str = "analysis.warp.secondServerUrl"; + pub const SECONDARY_SERVER_API_KEY_DEFAULT: Option<String> = None; + pub const SECONDARY_SERVER_API_KEY_SETTING: &'static str = "analysis.warp.secondServerApiKey"; pub const ENABLE_SERVER_DEFAULT: bool = false; pub const ENABLE_SERVER_SETTING: &'static str = "network.enableWARP"; pub fn register(bn_settings: &mut BNSettings) { + let whitelisted_source_tags_prop = json!({ + "title" : "Blacklisted Sources", + "type" : "array", + "default" : Self::WHITELISTED_SOURCE_TAGS_DEFAULT, + "description" : "Add a sources UUID to this list to blacklist it from being considered a valid source. This is useful for sources that are known to be false positives.", + "ignore" : [], + }); + bn_settings.register_setting_json( + Self::WHITELISTED_SOURCE_TAGS_SETTING, + &whitelisted_source_tags_prop.to_string(), + ); let load_bundled_files_prop = json!({ "title" : "Load Bundled Files", "type" : "boolean", @@ -85,6 +108,31 @@ impl PluginSettings { Self::SERVER_API_KEY_SETTING, &server_api_key_prop.to_string(), ); + let second_server_url_prop = json!({ + "title" : "Secondary Server URL", + "type" : "string", + "default" : Self::SECONDARY_SERVER_URL_DEFAULT, + "description" : "", + "ignore" : ["SettingsProjectScope", "SettingsResourceScope"], + "requiresRestart" : true + }); + bn_settings.register_setting_json( + Self::SECONDARY_SERVER_URL_SETTING, + &second_server_url_prop.to_string(), + ); + let second_server_api_key_prop = json!({ + "title" : "Secondary Server API Key", + "type" : "string", + "default" : Self::SECONDARY_SERVER_API_KEY_DEFAULT, + "description" : "", + "ignore" : ["SettingsProjectScope", "SettingsResourceScope"], + "hidden": true, + "requiresRestart" : true + }); + bn_settings.register_setting_json( + Self::SECONDARY_SERVER_API_KEY_SETTING, + &second_server_api_key_prop.to_string(), + ); let server_enabled_prop = json!({ "title" : "Enable WARP", "type" : "boolean", @@ -100,7 +148,7 @@ impl PluginSettings { } /// Retrieve plugin settings from [`BNSettings`]. - pub fn from_settings(bn_settings: &BNSettings) -> Self { + pub fn from_settings(bn_settings: &BNSettings, query_opts: &mut QueryOptions) -> Self { let mut settings = PluginSettings::default(); if bn_settings.contains(Self::LOAD_BUNDLED_FILES_SETTING) { settings.load_bundled_files = bn_settings.get_bool(Self::LOAD_BUNDLED_FILES_SETTING); @@ -117,9 +165,30 @@ impl PluginSettings { settings.server_api_key = Some(server_api_key_str); } } + if bn_settings.contains(Self::SECONDARY_SERVER_URL_SETTING) { + let server_api_key_str = bn_settings.get_string(Self::SECONDARY_SERVER_URL_SETTING); + if !server_api_key_str.is_empty() { + settings.second_server_url = Some(server_api_key_str); + } + } + if bn_settings.contains(Self::SECONDARY_SERVER_API_KEY_SETTING) { + let server_api_key_str = bn_settings.get_string(Self::SECONDARY_SERVER_API_KEY_SETTING); + if !server_api_key_str.is_empty() { + settings.second_server_api_key = Some(server_api_key_str); + } + } if bn_settings.contains(Self::ENABLE_SERVER_SETTING) { settings.enable_server = bn_settings.get_bool(Self::ENABLE_SERVER_SETTING); } + + if bn_settings.contains(Self::WHITELISTED_SOURCE_TAGS_SETTING) { + let whitelisted_source_tags_str = bn_settings + .get_string_list_with_opts(Self::WHITELISTED_SOURCE_TAGS_SETTING, query_opts); + settings.whitelisted_source_tags = whitelisted_source_tags_str + .iter() + .map(|s| s.to_string()) + .collect(); + } settings } } @@ -127,10 +196,13 @@ impl PluginSettings { impl Default for PluginSettings { fn default() -> Self { Self { + whitelisted_source_tags: PluginSettings::WHITELISTED_SOURCE_TAGS_DEFAULT, load_bundled_files: PluginSettings::LOAD_BUNDLED_FILES_DEFAULT, load_user_files: PluginSettings::LOAD_USER_FILES_DEFAULT, server_url: PluginSettings::SERVER_URL_DEFAULT.to_string(), server_api_key: PluginSettings::SERVER_API_KEY_DEFAULT, + second_server_url: PluginSettings::SECONDARY_SERVER_URL_DEFAULT, + second_server_api_key: PluginSettings::SECONDARY_SERVER_API_KEY_DEFAULT, enable_server: PluginSettings::ENABLE_SERVER_DEFAULT, } } diff --git a/plugins/warp/src/plugin/workflow.rs b/plugins/warp/src/plugin/workflow.rs index 9871f5d2..08d07c52 100644 --- a/plugins/warp/src/plugin/workflow.rs +++ b/plugins/warp/src/plugin/workflow.rs @@ -162,11 +162,7 @@ pub fn run_matcher(view: &BinaryView) { log::info!("Matcher was cancelled by user, you may run it again by running the 'Run Matcher' command."); } - // It is noisy to show this every time, so we only show it in cases where a user can reasonably perceive. - let elapsed = start.elapsed(); - if elapsed > std::time::Duration::from_secs(1) { - log::info!("Function matching took {:?}", elapsed); - } + log::info!("Function matching took {:?}", start.elapsed()); background_task.finish(); // Now we want to trigger re-analysis. @@ -185,7 +181,7 @@ pub fn insert_workflow() -> Result<(), ()> { // otherwise we will wipe over user type info. if !function.has_user_type() { if let Some(func_ty) = &matched_function.ty { - function.set_auto_type(&to_bn_type(&function.arch(), func_ty)); + function.set_auto_type(&to_bn_type(Some(function.arch()), func_ty)); } } if let Some(mlil) = ctx.mlil_function() { @@ -206,7 +202,7 @@ pub fn insert_workflow() -> Result<(), ()> { continue; } let decl_ty = match variable.ty { - Some(decl_ty) => to_bn_type(&function.arch(), &decl_ty), + Some(decl_ty) => to_bn_type(Some(function.arch()), &decl_ty), None => { let Some(existing_var) = function.variable_type(&decl_var) else { continue; |
