diff options
| author | KyleMiles <krm504@nyu.edu> | 2021-05-03 18:45:19 -0400 |
|---|---|---|
| committer | KyleMiles <krm504@nyu.edu> | 2021-05-11 16:49:24 -0400 |
| commit | eb7a52bf4bd3b19c22f2eadda444ab045c674fe3 (patch) | |
| tree | 2395d577d410908b5d0291682660dac2017c7e97 | |
| parent | 5731e612900bed0d542421e00e64324dbac9367e (diff) | |
Rust API : Better headless support; Introducing `binaryninja::open_view` and `binaryninja::open_view_with_options`, updated init/shutdown, script example, more setting support, and misc fixes
| -rw-r--r-- | rust/examples/basic_script/Cargo.toml | 8 | ||||
| -rw-r--r-- | rust/examples/basic_script/src/main.rs | 41 | ||||
| -rw-r--r-- | rust/src/basicblock.rs | 2 | ||||
| -rw-r--r-- | rust/src/binaryview.rs | 43 | ||||
| -rw-r--r-- | rust/src/callingconvention.rs | 8 | ||||
| -rw-r--r-- | rust/src/command.rs | 40 | ||||
| -rw-r--r-- | rust/src/custombinaryview.rs | 40 | ||||
| -rw-r--r-- | rust/src/filemetadata.rs | 45 | ||||
| -rw-r--r-- | rust/src/function.rs | 12 | ||||
| -rw-r--r-- | rust/src/headless.rs | 32 | ||||
| -rw-r--r-- | rust/src/lib.rs | 278 | ||||
| -rw-r--r-- | rust/src/rc.rs | 9 | ||||
| -rw-r--r-- | rust/src/settings.rs | 80 |
13 files changed, 555 insertions, 83 deletions
diff --git a/rust/examples/basic_script/Cargo.toml b/rust/examples/basic_script/Cargo.toml new file mode 100644 index 00000000..035528d4 --- /dev/null +++ b/rust/examples/basic_script/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "basic_script" +version = "0.1.0" +authors = ["KyleMiles <kyle@vector35.com>"] +edition = "2018" + +[dependencies] +binaryninja = {path="../../"} diff --git a/rust/examples/basic_script/src/main.rs b/rust/examples/basic_script/src/main.rs new file mode 100644 index 00000000..94e89df4 --- /dev/null +++ b/rust/examples/basic_script/src/main.rs @@ -0,0 +1,41 @@ +use binaryninja::architecture::Architecture; +use binaryninja::binaryview::{BinaryViewBase, BinaryViewExt}; + +fn main() { + println!("Loading plugins..."); // This loads all the core architecture, platform, etc plugins + binaryninja::headless::init(); + + println!("Loading binary..."); + let bv = binaryninja::open_view("/bin/cat").expect("Couldn't open `/bin/cat`"); + + println!("Filename: `{}`", bv.metadata().filename()); + println!("File size: `{:#x}`", bv.len()); + println!("Function count: {}", bv.functions().len()); + + for func in &bv.functions() { + println!(" `{}`:", func.symbol().full_name()); + for basic_block in &func.basic_blocks() { + // TODO : This is intended to be refactored to be more nice to work with soon(TM) + for addr in basic_block.as_ref() { + print!(" {} ", addr); + match func.arch().instruction_text( + bv.read_buffer(addr, func.arch().max_instr_len()) + .unwrap() + .get_data(), + addr, + ) { + Some((_, tokens)) => { + tokens + .iter() + .for_each(|token| print!("{}", token.text().to_str().unwrap())); + println!("") + } + _ => (), + } + } + } + } + + // Important! You need to call shutdown or your script will hang forever + binaryninja::headless::shutdown(); +} diff --git a/rust/src/basicblock.rs b/rust/src/basicblock.rs index 6b493803..3b9b7e5c 100644 --- a/rust/src/basicblock.rs +++ b/rust/src/basicblock.rs @@ -130,7 +130,7 @@ impl<C: BlockContext> BasicBlock<C> { pub fn function(&self) -> Ref<Function> { unsafe { let func = BNGetBasicBlockFunction(self.handle); - Ref::new(Function::from_raw(func)) + Function::from_raw(func) } } diff --git a/rust/src/binaryview.rs b/rust/src/binaryview.rs index aa5fb3f3..98d7c7b4 100644 --- a/rust/src/binaryview.rs +++ b/rust/src/binaryview.rs @@ -31,6 +31,7 @@ use crate::function::{Function, NativeBlock}; use crate::platform::Platform; use crate::section::{Section, SectionBuilder}; use crate::segment::{Segment, SegmentBuilder}; +use crate::settings::Settings; use crate::symbol::{Symbol, SymbolType}; use crate::types::{QualifiedName, Type}; use crate::Endianness; @@ -138,7 +139,7 @@ pub trait BinaryViewExt: BinaryViewBase { return Err(()); } - unsafe { Ok(Ref::new(BinaryView { handle })) } + unsafe { Ok(BinaryView::from_raw(handle)) } } /// Reads up to `len` bytes from address `offset` @@ -256,7 +257,7 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn get_instruction_len<A: Architecture>(&self, arch: &A, addr: u64) -> Option<usize> { + fn instruction_len<A: Architecture>(&self, arch: &A, addr: u64) -> Option<usize> { unsafe { let size = BNGetInstructionLength(self.as_ref().handle, arch.as_ref().0, addr); @@ -533,7 +534,7 @@ pub trait BinaryViewExt: BinaryViewBase { return Err(()); } - Ok(Ref::new(Function::from_raw(func))) + Ok(Function::from_raw(func)) } } @@ -565,7 +566,7 @@ pub trait BinaryViewExt: BinaryViewBase { return Err(()); } - Ok(Ref::new(Function::from_raw(handle))) + Ok(Function::from_raw(handle)) } } @@ -616,6 +617,34 @@ pub trait BinaryViewExt: BinaryViewBase { ); } } + + fn load_settings<S: BnStrCompatible>(&self, view_type_name: S) -> Result<Ref<Settings>> { + let view_type_name = view_type_name.as_bytes_with_nul(); + let settings_handle = unsafe { + BNBinaryViewGetLoadSettings( + self.as_ref().handle, + view_type_name.as_ref().as_ptr() as *mut _, + ) + }; + + if settings_handle.is_null() { + Err(()) + } else { + Ok(unsafe { Settings::from_raw(settings_handle) }) + } + } + + fn set_load_settings<S: BnStrCompatible>(&mut self, view_type_name: S, settings: &Settings) { + let view_type_name = view_type_name.as_bytes_with_nul(); + + unsafe { + BNBinaryViewSetLoadSettings( + self.as_ref().handle, + view_type_name.as_ref().as_ptr() as *mut _, + settings.handle, + ) + }; + } } impl<T: BinaryViewBase> BinaryViewExt for T {} @@ -626,14 +655,14 @@ pub struct BinaryView { } impl BinaryView { - pub(crate) unsafe fn from_raw(handle: *mut BNBinaryView) -> Self { + pub(crate) unsafe fn from_raw(handle: *mut BNBinaryView) -> Ref<Self> { debug_assert!(!handle.is_null()); - Self { handle } + Ref::new(Self { handle }) } pub fn from_filename<S: BnStrCompatible>( - meta: &FileMetadata, + meta: &mut FileMetadata, filename: S, ) -> Result<Ref<Self>> { let file = filename.as_bytes_with_nul(); diff --git a/rust/src/callingconvention.rs b/rust/src/callingconvention.rs index dcb88970..8cc1b71c 100644 --- a/rust/src/callingconvention.rs +++ b/rust/src/callingconvention.rs @@ -130,7 +130,7 @@ where where C: CallingConventionBase, { - ffi_wrap!("CallingConvention::callee_saved_registers", unsafe { + ffi_wrap!("CallingConvention::_callee_saved_registers", unsafe { let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>); let regs = ctxt.cc.callee_saved_registers(); @@ -559,7 +559,7 @@ unsafe impl<A: Architecture> RefCountable for CallingConvention<A> { pub struct ConventionBuilder<A: Architecture> { caller_saved_registers: Vec<A::Register>, - callee_saved_registers: Vec<A::Register>, + _callee_saved_registers: Vec<A::Register>, int_arg_registers: Vec<A::Register>, float_arg_registers: Vec<A::Register>, @@ -626,7 +626,7 @@ impl<A: Architecture> ConventionBuilder<A> { pub fn new(arch: &A) -> Self { Self { caller_saved_registers: Vec::new(), - callee_saved_registers: Vec::new(), + _callee_saved_registers: Vec::new(), int_arg_registers: Vec::new(), float_arg_registers: Vec::new(), @@ -649,7 +649,7 @@ impl<A: Architecture> ConventionBuilder<A> { } reg_list!(caller_saved_registers); - reg_list!(callee_saved_registers); + reg_list!(_callee_saved_registers); reg_list!(int_arg_registers); reg_list!(float_arg_registers); diff --git a/rust/src/command.rs b/rust/src/command.rs index 7dbc7436..3251e6b0 100644 --- a/rust/src/command.rs +++ b/rust/src/command.rs @@ -53,7 +53,9 @@ where { ffi_wrap!("Command::action", unsafe { let cmd = &*(ctxt as *const C); - let view = BinaryView::from_raw(view); + + debug_assert!(!view.is_null()); + let view = BinaryView { handle: view }; cmd.action(&view); }) @@ -65,7 +67,9 @@ where { ffi_wrap!("Command::valid", unsafe { let cmd = &*(ctxt as *const C); - let view = BinaryView::from_raw(view); + + debug_assert!(!view.is_null()); + let view = BinaryView { handle: view }; cmd.valid(&view) }) @@ -119,7 +123,9 @@ where { ffi_wrap!("AddressCommand::action", unsafe { let cmd = &*(ctxt as *const C); - let view = BinaryView::from_raw(view); + + debug_assert!(!view.is_null()); + let view = BinaryView { handle: view }; cmd.action(&view, addr); }) @@ -131,7 +137,9 @@ where { ffi_wrap!("AddressCommand::valid", unsafe { let cmd = &*(ctxt as *const C); - let view = BinaryView::from_raw(view); + + debug_assert!(!view.is_null()); + let view = BinaryView { handle: view }; cmd.valid(&view, addr) }) @@ -185,7 +193,9 @@ where { ffi_wrap!("RangeCommand::action", unsafe { let cmd = &*(ctxt as *const C); - let view = BinaryView::from_raw(view); + + debug_assert!(!view.is_null()); + let view = BinaryView { handle: view }; cmd.action(&view, addr..addr.wrapping_add(len)); }) @@ -202,7 +212,9 @@ where { ffi_wrap!("RangeCommand::valid", unsafe { let cmd = &*(ctxt as *const C); - let view = BinaryView::from_raw(view); + + debug_assert!(!view.is_null()); + let view = BinaryView { handle: view }; cmd.valid(&view, addr..addr.wrapping_add(len)) }) @@ -256,8 +268,12 @@ where { ffi_wrap!("FunctionCommand::action", unsafe { let cmd = &*(ctxt as *const C); - let view = BinaryView::from_raw(view); - let func = Function::from_raw(func); + + debug_assert!(!view.is_null()); + let view = BinaryView { handle: view }; + + debug_assert!(!func.is_null()); + let func = Function { handle: func }; cmd.action(&view, &func); }) @@ -273,8 +289,12 @@ where { ffi_wrap!("FunctionCommand::valid", unsafe { let cmd = &*(ctxt as *const C); - let view = BinaryView::from_raw(view); - let func = Function::from_raw(func); + + debug_assert!(!view.is_null()); + let view = BinaryView { handle: view }; + + debug_assert!(!func.is_null()); + let func = Function { handle: func }; cmd.valid(&view, &func) }) diff --git a/rust/src/custombinaryview.rs b/rust/src/custombinaryview.rs index 81272b0b..b1fda73a 100644 --- a/rust/src/custombinaryview.rs +++ b/rust/src/custombinaryview.rs @@ -96,7 +96,10 @@ where let view_type = &*(ctxt as *mut T); let data = BinaryView::from_raw(data); - Ref::into_raw(view_type.load_settings_for_data(&data)).handle + match view_type.load_settings_for_data(&data) { + Ok(settings) => Ref::into_raw(settings).handle, + _ => ptr::null_mut() as *mut _, + } }) } @@ -138,11 +141,14 @@ where pub trait BinaryViewTypeBase: AsRef<BinaryViewType> { fn is_valid_for(&self, data: &BinaryView) -> bool; - fn load_settings_for_data(&self, data: &BinaryView) -> Ref<Settings> { - unsafe { - Ref::new(Settings::from_raw( - BNGetBinaryViewDefaultLoadSettingsForData(self.as_ref().0, data.handle), - )) + fn load_settings_for_data(&self, data: &BinaryView) -> Result<Ref<Settings>> { + let settings_handle = + unsafe { BNGetBinaryViewDefaultLoadSettingsForData(self.as_ref().0, data.handle) }; + + if settings_handle.is_null() { + Err(()) + } else { + unsafe { Ok(Settings::from_raw(settings_handle)) } } } } @@ -181,7 +187,7 @@ pub trait BinaryViewTypeExt: BinaryViewTypeBase { return Err(()); } - unsafe { Ok(Ref::new(BinaryView::from_raw(handle))) } + unsafe { Ok(BinaryView::from_raw(handle)) } } } @@ -227,12 +233,14 @@ impl BinaryViewTypeBase for BinaryViewType { unsafe { BNIsBinaryViewTypeValidForData(self.0, data.handle) } } - fn load_settings_for_data(&self, data: &BinaryView) -> Ref<Settings> { - unsafe { - Ref::new(Settings::from_raw(BNGetBinaryViewLoadSettingsForData( - self.0, - data.handle, - ))) + fn load_settings_for_data(&self, data: &BinaryView) -> Result<Ref<Settings>> { + let settings_handle = + unsafe { BNGetBinaryViewDefaultLoadSettingsForData(self.as_ref().0, data.handle) }; + + if settings_handle.is_null() { + Err(()) + } else { + unsafe { Ok(Settings::from_raw(settings_handle)) } } } } @@ -280,7 +288,7 @@ pub struct CustomViewBuilder<'a, T: CustomBinaryViewType + ?Sized> { pub unsafe trait CustomBinaryView: 'static + BinaryViewBase + Sync + Sized { type Args: Send; - fn new(handle: BinaryView, args: &Self::Args) -> Result<Self>; + fn new(handle: &BinaryView, args: &Self::Args) -> Result<Self>; fn init(&self, args: Self::Args) -> Result<()>; } @@ -377,7 +385,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> { let context = &mut *(ctxt as *mut CustomViewContext<V>); let handle = BinaryView::from_raw(context.raw_handle); - if let Ok(v) = V::new(handle, &context.args) { + if let Ok(v) = V::new(handle.as_ref(), &context.args) { ptr::write(&mut context.view, v); context.initialized = true; @@ -730,7 +738,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> { (*ctxt).raw_handle = res; Ok(CustomView { - handle: Ref::new(BinaryView::from_raw(res)), + handle: BinaryView::from_raw(res), _builder: PhantomData, }) } diff --git a/rust/src/filemetadata.rs b/rust/src/filemetadata.rs index fa7d65e1..809abea4 100644 --- a/rust/src/filemetadata.rs +++ b/rust/src/filemetadata.rs @@ -31,6 +31,8 @@ use binaryninjacore_sys::{ BNMarkFileSaved, BNNavigate, BNNewFileReference, + BNOpenDatabaseForConfiguration, + BNOpenExistingDatabase, BNRedo, BNSetFilename, BNUndo, @@ -168,10 +170,49 @@ impl FileMetadata { if res.is_null() { Err(()) } else { - Ok(Ref::new(BinaryView::from_raw(res))) + Ok(BinaryView::from_raw(res)) } } } + + pub fn open_database_for_configuration<S: BnStrCompatible>( + &mut self, + filename: S, + ) -> Result<Ref<BinaryView>, ()> { + let filename = filename.as_bytes_with_nul(); + unsafe { + let bv = + BNOpenDatabaseForConfiguration(self.handle, filename.as_ref().as_ptr() as *const _); + + if bv.is_null() { + Err(()) + } else { + Ok(BinaryView::from_raw(bv)) + } + } + } + + pub fn open_database<S: BnStrCompatible>( + &mut self, + filename: S, + ) -> Result<Ref<BinaryView>, ()> { + let filename = filename.as_bytes_with_nul(); + let filename_ptr = filename.as_ref().as_ptr() as *mut _; + + let view = unsafe { BNOpenExistingDatabase(self.handle, filename_ptr) }; + + // TODO : add optional progress function + // let view = match progress_func { + // None => BNOpenExistingDatabase(self.handle, filename_ptr), + // _ => BNOpenExistingDatabaseWithProgress(self.handle, str(filename), None, ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)(lambda ctxt, cur, total: progress_func(cur, total))) + // }; + + if view.is_null() { + Err(()) + } else { + Ok(unsafe { BinaryView::from_raw(view) }) + } + } } impl ToOwned for FileMetadata { @@ -197,6 +238,4 @@ unsafe impl RefCountable for FileMetadata { /* BNCreateDatabase, BNCreateDatabaseWithProgress, -BNOpenExistingDatabase, -BNOpenExistingDatabaseWithProgress, */ diff --git a/rust/src/function.rs b/rust/src/function.rs index 41c3a973..4230900f 100644 --- a/rust/src/function.rs +++ b/rust/src/function.rs @@ -68,7 +68,7 @@ impl Iterator for NativeBlockIter { None } else { self.bv - .get_instruction_len(&self.arch, res) + .instruction_len(&self.arch, res) .map(|x| { self.cur += x as u64; res @@ -112,15 +112,15 @@ impl BlockContext for NativeBlock { #[derive(PartialEq, Eq, Hash)] pub struct Function { - handle: *mut BNFunction, + pub(crate) handle: *mut BNFunction, } unsafe impl Send for Function {} unsafe impl Sync for Function {} impl Function { - pub(crate) unsafe fn from_raw(handle: *mut BNFunction) -> Self { - Self { handle } + pub(crate) unsafe fn from_raw(handle: *mut BNFunction) -> Ref<Self> { + Ref::new(Self { handle }) } pub fn arch(&self) -> CoreArchitecture { @@ -140,7 +140,7 @@ impl Function { pub fn view(&self) -> Ref<BinaryView> { unsafe { let view = BNGetFunctionData(self.handle); - Ref::new(BinaryView::from_raw(view)) + BinaryView::from_raw(view) } } @@ -282,6 +282,6 @@ unsafe impl<'a> CoreOwnedArrayWrapper<'a> for Function { type Wrapped = Guard<'a, Function>; unsafe fn wrap_raw(raw: &'a *mut BNFunction, context: &'a ()) -> Guard<'a, Function> { - Guard::new(Function::from_raw(*raw), context) + Guard::new(Function { handle: *raw }, context) } } diff --git a/rust/src/headless.rs b/rust/src/headless.rs index f7b9f16d..39711f94 100644 --- a/rust/src/headless.rs +++ b/rust/src/headless.rs @@ -63,9 +63,7 @@ fn binja_path() -> PathBuf { PathBuf::from(env::var("PROGRAMFILES").unwrap()).join("Vector35\\BinaryNinja\\") } -use binaryninjacore_sys::{ - BNInitCorePlugins, BNInitRepoPlugins, BNInitUserPlugins, BNSetBundledPluginDirectory, -}; +use binaryninjacore_sys::{BNInitPlugins, BNInitRepoPlugins, BNSetBundledPluginDirectory}; pub fn init() { unsafe { @@ -73,8 +71,32 @@ pub fn init() { let path = CString::new(path.into_string().unwrap()).unwrap(); BNSetBundledPluginDirectory(path.as_ptr()); - BNInitCorePlugins(); - BNInitUserPlugins(); + BNInitPlugins(true); BNInitRepoPlugins(); } } + +pub fn shutdown() { + //! Unloads plugins, stops all worker threads, and closes open logs + //! Important! : Must be called at the end of scripts + + unsafe { binaryninjacore_sys::BNShutdown() }; +} + +pub fn script_helper(func: fn()) { + //! Prelued-postlued helper function: + //! ``` + //! fn main() { + //! binaryninja::headless::script_helper(|| { + //! binaryninja::open_view("/bin/cat") + //! .expect("Couldn't open `/bin/cat`") + //! .iter() + //! .for_each(|func| println!(" `{}`", func.symbol().full_name())); + //! }); + //! } + //! ``` + + init(); + func(); + shutdown(); +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 94e6b966..b8919fb1 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -17,9 +17,7 @@ #[macro_use] extern crate log; -pub extern crate binaryninjacore_sys; // For testing functions and stuff -#[cfg(feature = "ui")] -pub extern crate binaryninjaui_sys; // For testing functions and stuff +pub extern crate binaryninjacore_sys; extern crate libc; #[cfg(feature = "rayon")] extern crate rayon; @@ -62,6 +60,11 @@ pub mod string; pub mod symbol; pub mod types; +use std::collections::HashMap; +use std::fs::File; +use std::io::Read; +use std::path::Path; + pub use binaryninjacore_sys::BNBranchType as BranchType; pub use binaryninjacore_sys::BNEndianness as Endianness; @@ -213,6 +216,251 @@ pub mod logger { } } +pub fn open_view<F: AsRef<Path>>(filename: F) -> Result<rc::Ref<binaryview::BinaryView>, String> { + use crate::binaryview::BinaryViewExt; + use crate::custombinaryview::BinaryViewTypeExt; + + let filename = filename.as_ref(); + + let mut metadata = filemetadata::FileMetadata::with_filename(filename.to_str().unwrap()); + + let mut is_bndb = false; + let view = match match filename.ends_with(".bndb") { + true => { + match File::open(filename) { + Ok(mut file) => { + let mut buf = [0; 15]; + match file.read_exact(&mut buf) { + Ok(_) => { + let sqlite_string = "SQLite format 3"; + if buf != sqlite_string.as_bytes() { + return Err("Not a valid BNDB (invalid magic)".to_string()); + } + } + _ => return Err("Not a valid BNDB (too small)".to_string()), + } + } + _ => return Err("Could not open file".to_string()), + } + is_bndb = true; + metadata.open_database(filename.to_str().unwrap()) + } + false => binaryview::BinaryView::from_filename(&mut metadata, filename.to_str().unwrap()), + } { + Ok(view) => view, + _ => return Err("Unable to open file".to_string()), + }; + + let mut bv = None; + for available_view in custombinaryview::BinaryViewType::list_valid_types_for(&view).iter() { + // TODO : These weird comparison arguments is probably symptomatic of something we should fix (fix other instance too) + if bv.is_none() && **available_view.name() != *"Raw" { + if is_bndb { + bv = Some( + view.metadata() + .get_view_of_type(available_view.name()) + .unwrap(), + ); + } else { + // TODO : add log prints + // println!("Opening view of type: `{}`", available_view.name()); + bv = Some(available_view.open(&view).unwrap()); + } + break; + } + } + + let bv = match bv { + None => { + if is_bndb { + match view.metadata().get_view_of_type("Raw") { + Ok(view) => view, + _ => return Err("Could not get raw view from bndb".to_string()), + } + } else { + match custombinaryview::BinaryViewType::by_name("Raw") + .unwrap() + .open(&view) + { + Ok(view) => view, + _ => return Err("Could not open raw view".to_string()), + } + } + } + Some(bv) => bv, + }; + + bv.update_analysis_and_wait(); + Ok(bv) +} + +pub fn open_view_with_options<F: AsRef<Path>>( + filename: F, + update_analysis_and_wait: bool, + options: Option<HashMap<&str, &str>>, +) -> Result<rc::Ref<binaryview::BinaryView>, String> { + //! This is incomplete, but should work in most cases: + //! ``` + //! let settings = [("analysis.linearSweep.autorun", "false")] + //! .iter() + //! .cloned() + //! .collect(); + //! + //! let bv = binaryninja::open_view_with_options("/bin/cat", true, Some(settings)) + //! .expect("Couldn't open `/bin/cat`"); + //! ``` + + use crate::binaryview::BinaryViewExt; + use crate::custombinaryview::{BinaryViewTypeBase, BinaryViewTypeExt}; + + let filename = filename.as_ref(); + + let mut metadata = filemetadata::FileMetadata::with_filename(filename.to_str().unwrap()); + + let mut is_bndb = false; + let mut view = match match filename.ends_with(".bndb") { + true => { + match File::open(filename) { + Ok(mut file) => { + let mut buf = [0; 15]; + match file.read_exact(&mut buf) { + Ok(_) => { + let sqlite_string = "SQLite format 3"; + if buf != sqlite_string.as_bytes() { + return Err("Not a valid BNDB (invalid magic)".to_string()); + } + } + _ => return Err("Not a valid BNDB (too small)".to_string()), + } + } + _ => return Err("Could not open file".to_string()), + } + is_bndb = true; + metadata.open_database_for_configuration(filename.to_str().unwrap()) + } + false => binaryview::BinaryView::from_filename(&mut metadata, filename.to_str().unwrap()), + } { + Ok(view) => view, + _ => return Err("Unable to open file".to_string()), + }; + + let mut universal_view_type = None; + let mut view_type = None; + for available_view in custombinaryview::BinaryViewType::list_valid_types_for(&view).iter() { + if available_view.name().as_ref() == "Universal".as_bytes() { + universal_view_type = Some(available_view); + } else if view_type.is_none() && **available_view.name() != *"Raw" { + view_type = Some(available_view); + } + } + + let view_type = match view_type { + None => custombinaryview::BinaryViewType::by_name("Mapped").unwrap(), + Some(view_type) => view_type, + }; + + // I have no idea why this is needed + let setting_id = format!("{}{}", view_type.name(), "_settings"); + let mut default_settings = settings::Settings::new(setting_id); + default_settings.deserialize_schema(settings::Settings::new("").serialize_schema()); + default_settings.set_resource_id(view_type.name()); + + let mut load_settings = match (is_bndb, view.load_settings(view_type.name())) { + (true, Ok(settings)) => Some(settings), + _ => None, + }; + + if load_settings.is_none() { + // TODO : The Python version has a "fixme" here but I have no idea why + if universal_view_type.is_some() + && options.is_some() + && options + .as_ref() + .unwrap() + .contains_key("files.universal.architecturePreference") + { + load_settings = match universal_view_type + .unwrap() + .load_settings_for_data(view.as_ref()) + { + Ok(settings) => Some(settings), + _ => return Err("Could not load settings for universal view data".to_string()), + } + + // TODO : finish converting this to Rust + // arch_list = json.loads(load_settings.get_string('loader.universal.architectures')) + // arch_entry = [entry for entry in arch_list if entry['architecture'] == options['files.universal.architecturePreference'][0]] + // if not arch_entry: + // log.log_error(f"Could not load {options['files.universal.architecturePreference'][0]} from Universal image. Entry not found!") + // return None + + // let tmp_load_settings = settings::Settings::new(BNGetUniqueIdentifierString()); + // tmp_load_settings.deserialize_schema(arch_entry[0]['loadSchema']); + // load_settings = Some(tmp_load_settings); + } else { + load_settings = match view_type.load_settings_for_data(view.as_ref()) { + Ok(settings) => Some(settings), + _ => None, + } + } + } + if load_settings.is_none() { + // log.log_error(f"Could not get load settings for binary view of type `{bvt.name}`") + return Ok(view); + } + let mut load_settings = load_settings.unwrap(); + load_settings.set_resource_id(view_type.name()); + view.set_load_settings(view_type.name(), load_settings.as_ref()); + + match options { + Some(options) => { + for (setting, value) in options { + if load_settings.contains(setting) { + if !load_settings.set_json(setting, value, Some(view.as_ref()), None) { + return Err(format!("Setting: {} set operation failed!", setting)); + } + } else if default_settings.contains(setting) { + if !default_settings.set_json(setting, value, Some(view.as_ref()), None) { + return Err(format!("Setting: {} set operation failed!", setting)); + } + } else { + return Err(format!("Setting: {} not available!", setting)); + } + } + } + None => (), + } + + if is_bndb { + let view = view + .metadata() + .open_database(filename.to_str().unwrap()) + .expect("Couldn't open database"); + let view_type_name = view_type.name(); + // TODO : handle the case where the view doesn't exist in the bndb + let bv = unsafe { + binaryview::BinaryView::from_raw(binaryninjacore_sys::BNGetFileViewOfType( + view.metadata().handle, + view_type_name.as_ref().as_ptr() as *mut _, + )) + }; + if update_analysis_and_wait { + bv.update_analysis_and_wait(); + } + Ok(bv) + } else { + match view_type.open(&view) { + Ok(bv) => { + if update_analysis_and_wait { + bv.update_analysis_and_wait(); + } + Ok(bv) + } + _ => Ok(view), + } + } +} + pub fn version() -> string::BnString { unsafe { string::BnString::from_raw(binaryninjacore_sys::BNGetVersionString()) } } @@ -225,18 +473,6 @@ pub fn plugin_abi_minimum_version() -> u32 { binaryninjacore_sys::BN_MINIMUM_CORE_ABI_VERSION } -// TODO : We need to get this from uitypes.h::BN_CURRENT_UI_ABI_VERSION -pub fn plugin_ui_abi_version() -> u32 { - //xxxx::BN_CURRENT_UI_ABI_VERSION - 2 -} - -// TODO : We need to get this from uitypes.h::BN_MINIMUM_UI_ABI_VERSION -pub fn plugin_ui_abi_minimum_version() -> u32 { - //xxxx::BN_MINIMUM_UI_ABI_VERSION - 2 -} - pub fn core_abi_version() -> u32 { unsafe { binaryninjacore_sys::BNGetCurrentCoreABIVersion() } } @@ -245,21 +481,21 @@ pub fn core_abi_minimum_version() -> u32 { unsafe { binaryninjacore_sys::BNGetMinimumCoreABIVersion() } } -// TODO : We need to get this from uitypes.h::UIPluginABIVersion() -pub fn ui_plugin_abi_version() -> u32 { - //unsafe { xxxx::UIPluginABIVersion() } - 2 +pub fn plugin_ui_abi_version() -> u32 { + binaryninjacore_sys::BN_CURRENT_UI_ABI_VERSION +} + +pub fn plugin_ui_abi_minimum_version() -> u32 { + binaryninjacore_sys::BN_MINIMUM_UI_ABI_VERSION } // Provide ABI version automatically so that the core can verify binary compatibility -#[cfg(any(windows, not(feature = "headless")))] #[no_mangle] #[allow(non_snake_case)] pub extern "C" fn CorePluginABIVersion() -> u32 { plugin_abi_version() } -#[cfg(any(windows, not(feature = "headless")))] #[no_mangle] pub extern "C" fn UIPluginABIVersion() -> u32 { plugin_ui_abi_version() diff --git a/rust/src/rc.rs b/rust/src/rc.rs index 9264e3bd..7c5d610f 100644 --- a/rust/src/rc.rs +++ b/rust/src/rc.rs @@ -163,10 +163,7 @@ where unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped; } -pub struct Array<P> -where - P: CoreOwnedArrayProvider, -{ +pub struct Array<P: CoreOwnedArrayProvider> { contents: *mut P::Raw, count: usize, context: P::Context, @@ -189,8 +186,8 @@ impl<P: CoreOwnedArrayProvider> Array<P> { pub(crate) unsafe fn new(raw: *mut P::Raw, count: usize, context: P::Context) -> Self { Self { contents: raw, - count: count, - context: context, + count, + context, } } diff --git a/rust/src/settings.rs b/rust/src/settings.rs index 09fa14a6..a555cb4c 100644 --- a/rust/src/settings.rs +++ b/rust/src/settings.rs @@ -12,11 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -use binaryninjacore_sys::*; - pub use binaryninjacore_sys::BNSettingsScope as SettingsScope; +use binaryninjacore_sys::*; +use crate::binaryview::BinaryView; use crate::rc::*; +use crate::string::{BnStrCompatible, BnString}; + +use std::ptr; #[derive(PartialEq, Eq, Hash)] pub struct Settings { @@ -27,10 +30,79 @@ unsafe impl Send for Settings {} unsafe impl Sync for Settings {} impl Settings { - pub(crate) unsafe fn from_raw(handle: *mut BNSettings) -> Self { + pub(crate) unsafe fn from_raw(handle: *mut BNSettings) -> Ref<Self> { debug_assert!(!handle.is_null()); - Self { handle } + Ref::new(Self { handle }) + } + + pub fn new<S: BnStrCompatible>(instance_id: S) -> Ref<Self> { + let instance_id = instance_id.as_bytes_with_nul(); + unsafe { + let handle = BNCreateSettings(instance_id.as_ref().as_ptr() as *mut _); + + debug_assert!(!handle.is_null()); + + Ref::new(Self { handle }) + } + } + + pub fn set_resource_id<S: BnStrCompatible>(&mut self, resource_id: S) { + let resource_id = resource_id.as_bytes_with_nul(); + unsafe { BNSettingsSetResourceId(self.handle, resource_id.as_ref().as_ptr() as *mut _) }; + } + + pub fn serialize_schema(&self) -> BnString { + unsafe { BnString::from_raw(BNSettingsSerializeSchema(self.handle)) } + } + + pub fn deserialize_schema<S: BnStrCompatible>(&self, schema: S) -> bool { + let schema = schema.as_bytes_with_nul(); + unsafe { + BNSettingsDeserializeSchema( + self.handle, + schema.as_ref().as_ptr() as *mut _, + BNSettingsScope::SettingsAutoScope, + true, + ) + } + } + + pub fn contains<S: BnStrCompatible>(&self, key: S) -> bool { + let key = key.as_bytes_with_nul(); + + unsafe { BNSettingsContains(self.handle, key.as_ref().as_ptr() as *mut _) } + } + + pub fn set_json<S1: BnStrCompatible, S2: BnStrCompatible>( + &mut self, + key: S1, + value: S2, + view: Option<&BinaryView>, + scope: Option<SettingsScope>, + ) -> bool { + let key = key.as_bytes_with_nul(); + let value = value.as_bytes_with_nul(); + + let view_handle = match view { + Some(view) => view.handle, + None => ptr::null_mut() as *mut _, + }; + + let scope = match scope { + Some(scope) => scope, + None => SettingsScope::SettingsAutoScope, + }; + + unsafe { + BNSettingsSetJson( + self.handle, + view_handle, + scope, + key.as_ref().as_ptr() as *mut _, + value.as_ref().as_ptr() as *mut _, + ) + } } } |
