diff options
| author | Josh Ferrell <josh@vector35.com> | 2024-08-21 10:30:46 -0400 |
|---|---|---|
| committer | Josh Ferrell <josh@vector35.com> | 2024-08-21 10:30:46 -0400 |
| commit | 7a0ab6ffa99533bc241c88b8761fb82d795dc832 (patch) | |
| tree | 32d3cc8339f681f9a1755fb36a2c69580af42042 | |
| parent | 3d088d6dae28ab851b584a11678c9993eb18da10 (diff) | |
Add progress context to load APIs
| -rw-r--r-- | binaryninjacore.h | 10 | ||||
| -rw-r--r-- | binaryview.cpp | 8 | ||||
| -rw-r--r-- | python/binaryview.py | 14 | ||||
| -rw-r--r-- | rust/src/lib.rs | 180 | ||||
| -rw-r--r-- | rust/src/update.rs | 14 |
5 files changed, 197 insertions, 29 deletions
diff --git a/binaryninjacore.h b/binaryninjacore.h index 8b33af27..05f77741 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -37,14 +37,14 @@ // Current ABI version for linking to the core. This is incremented any time // there are changes to the API that affect linking, including new functions, // new types, or modifications to existing functions or types. -#define BN_CURRENT_CORE_ABI_VERSION 74 +#define BN_CURRENT_CORE_ABI_VERSION 75 // Minimum ABI version that is supported for loading of plugins. Plugins that // are linked to an ABI version less than this will not be able to load and // will require rebuilding. The minimum version is increased when there are // incompatible changes that break binary compatibility, such as changes to // existing types or functions. -#define BN_MINIMUM_CORE_ABI_VERSION 74 +#define BN_MINIMUM_CORE_ABI_VERSION 75 #ifdef __GNUC__ #ifdef BINARYNINJACORE_LIBRARY @@ -6180,9 +6180,9 @@ extern "C" BINARYNINJACOREAPI bool BNCheckForStringAnnotationType(BNBinaryView* view, uint64_t addr, char** value, BNStringType* strType, bool allowShortStrings, bool allowLargeStrings, size_t childWidth); - BINARYNINJACOREAPI BNBinaryView* BNLoadFilename(const char* const filename, const bool updateAnalysis, const char* options, bool (*progress)(size_t, size_t)); - BINARYNINJACOREAPI BNBinaryView* BNLoadProjectFile(BNProjectFile* projectFile, const bool updateAnalysis, const char* options, bool (*progress)(size_t, size_t)); - BINARYNINJACOREAPI BNBinaryView* BNLoadBinaryView(BNBinaryView* view, const bool updateAnalysis, const char* options, bool (*progress)(size_t, size_t)); + BINARYNINJACOREAPI BNBinaryView* BNLoadFilename(const char* const filename, const bool updateAnalysis, const char* options, BNProgressFunction progress, void* progressContext); + BINARYNINJACOREAPI BNBinaryView* BNLoadProjectFile(BNProjectFile* projectFile, const bool updateAnalysis, const char* options, BNProgressFunction progress, void* progressContext); + BINARYNINJACOREAPI BNBinaryView* BNLoadBinaryView(BNBinaryView* view, const bool updateAnalysis, const char* options, BNProgressFunction progress, void* progressContext); BINARYNINJACOREAPI BNExternalLibrary* BNBinaryViewAddExternalLibrary(BNBinaryView* view, const char* name, BNProjectFile* backingFile, bool isAuto); BINARYNINJACOREAPI void BNBinaryViewRemoveExternalLibrary(BNBinaryView* view, const char* name); diff --git a/binaryview.cpp b/binaryview.cpp index 2f24904d..9c1373c1 100644 --- a/binaryview.cpp +++ b/binaryview.cpp @@ -5427,7 +5427,9 @@ Ref<BinaryView> BinaryNinja::Load(Ref<BinaryView> view, bool updateAnalysis, Ref<BinaryView> BinaryNinja::Load(const std::string& filename, bool updateAnalysis, const std::string& options, std::function<bool(size_t, size_t)> progress) { - BNBinaryView* handle = BNLoadFilename(filename.c_str(), updateAnalysis, options.c_str(), (bool (*)(size_t, size_t))progress.target<bool (*)(size_t, size_t)>()); + ProgressContext cb; + cb.callback = progress; + BNBinaryView* handle = BNLoadFilename(filename.c_str(), updateAnalysis, options.c_str(), ProgressCallback, &cb); if (!handle) return nullptr; return new BinaryView(handle); @@ -5444,7 +5446,9 @@ Ref<BinaryView> BinaryNinja::Load(const DataBuffer& rawData, bool updateAnalysis Ref<BinaryView> BinaryNinja::Load(Ref<BinaryView> view, bool updateAnalysis, const std::string& options, std::function<bool(size_t, size_t)> progress) { - BNBinaryView* handle = BNLoadBinaryView(view->GetObject(), updateAnalysis, options.c_str(), (bool (*)(size_t, size_t))progress.target<bool (*)(size_t, size_t)>()); + ProgressContext cb; + cb.callback = progress; + BNBinaryView* handle = BNLoadBinaryView(view->GetObject(), updateAnalysis, options.c_str(), ProgressCallback, &cb); if (!handle) return nullptr; return new BinaryView(handle); diff --git a/python/binaryview.py b/python/binaryview.py index 960476d6..484e704f 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -2646,7 +2646,7 @@ class BinaryView: return BinaryView(file_metadata=file_metadata, handle=view) @staticmethod - def load(source: Union[str, bytes, bytearray, 'databuffer.DataBuffer', 'os.PathLike', 'BinaryView', 'project.ProjectFile'], update_analysis: Optional[bool] = True, + def load(source: Union[str, bytes, bytearray, 'databuffer.DataBuffer', 'os.PathLike', 'BinaryView', 'project.ProjectFile'], update_analysis: bool = True, progress_func: Optional[ProgressFuncType] = None, options: Mapping[str, Any] = {}) -> Optional['BinaryView']: """ ``load`` opens, generates default load options (which are overridable), and returns the first available \ @@ -2687,21 +2687,21 @@ class BinaryView: binaryninja._init_plugins() if progress_func is None: - progress_cfunc = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_ulonglong, ctypes.c_ulonglong)(lambda cur, total: True) + progress_cfunc = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)(lambda ctxt, cur, total: True) else: - progress_cfunc = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_ulonglong, ctypes.c_ulonglong)(lambda cur, total: progress_func(cur, total)) + progress_cfunc = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)(lambda ctxt, cur, total: progress_func(cur, total)) if isinstance(source, os.PathLike): source = str(source) if isinstance(source, BinaryView): - handle = core.BNLoadBinaryView(source.handle, update_analysis, json.dumps(options), progress_cfunc) + handle = core.BNLoadBinaryView(source.handle, update_analysis, json.dumps(options), progress_cfunc, None) elif isinstance(source, project.ProjectFile): - handle = core.BNLoadProjectFile(source._handle, update_analysis, json.dumps(options), progress_cfunc) + handle = core.BNLoadProjectFile(source._handle, update_analysis, json.dumps(options), progress_cfunc, None) elif isinstance(source, str): - handle = core.BNLoadFilename(source, update_analysis, json.dumps(options), progress_cfunc) + handle = core.BNLoadFilename(source, update_analysis, json.dumps(options), progress_cfunc, None) elif isinstance(source, bytes) or isinstance(source, bytearray) or isinstance(source, databuffer.DataBuffer): raw_view = BinaryView.new(source) - handle = core.BNLoadBinaryView(raw_view.handle, update_analysis, json.dumps(options), progress_cfunc) + handle = core.BNLoadBinaryView(raw_view.handle, update_analysis, json.dumps(options), progress_cfunc, None) else: raise NotImplementedError return BinaryView(handle=handle) if handle else None diff --git a/rust/src/lib.rs b/rust/src/lib.rs index bd46611d..d881c943 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -173,6 +173,7 @@ pub mod types; pub mod update; use std::path::PathBuf; +use std::ptr; pub use binaryninjacore_sys::BNBranchType as BranchType; pub use binaryninjacore_sys::BNEndianness as Endianness; @@ -202,17 +203,76 @@ use string::IntoJson; const BN_FULL_CONFIDENCE: u8 = 255; const BN_INVALID_EXPR: usize = usize::MAX; +unsafe extern "C" fn cb_progress_func<F: FnMut(usize, usize) -> bool>( + ctxt: *mut std::ffi::c_void, + progress: usize, + total: usize, +) -> bool { + if ctxt.is_null() { + return true; + } + let closure: &mut F = std::mem::transmute(ctxt); + closure(progress, total) +} + + +unsafe extern "C" fn cb_progress_nop( + _ctxt: *mut std::ffi::c_void, + _arg1: usize, + _arg2: usize +) -> bool { + true +} + + /// The main way to open and load files into Binary Ninja. Make sure you've properly initialized the core before calling this function. See [`crate::headless::init()`] -pub fn load<S: BnStrCompatible>(filename: S) -> Option<rc::Ref<binaryview::BinaryView>> { +pub fn load<S>( + filename: S, +) -> Option<rc::Ref<binaryview::BinaryView>> +where + S: BnStrCompatible, +{ + let filename = filename.into_bytes_with_nul(); + let options = "\x00"; + + + let handle = unsafe { + binaryninjacore_sys::BNLoadFilename( + filename.as_ref().as_ptr() as *mut _, + true, + options.as_ptr() as *mut core::ffi::c_char, + Some(cb_progress_nop), + ptr::null_mut(), + ) + }; + + if handle.is_null() { + None + } else { + Some(unsafe { BinaryView::from_raw(handle) }) + } +} + +pub fn load_with_progress<S, F>( + filename: S, + mut progress: F, +) -> Option<rc::Ref<binaryview::BinaryView>> +where + S: BnStrCompatible, + F: FnMut(usize, usize) -> bool, +{ let filename = filename.into_bytes_with_nul(); let options = "\x00"; + let progress_ctx = &mut progress as *mut F as *mut std::ffi::c_void; + let handle = unsafe { binaryninjacore_sys::BNLoadFilename( filename.as_ref().as_ptr() as *mut _, true, options.as_ptr() as *mut core::ffi::c_char, - None, + Some(cb_progress_func::<F>), + progress_ctx, ) }; @@ -240,11 +300,15 @@ pub fn load<S: BnStrCompatible>(filename: S) -> Option<rc::Ref<binaryview::Binar /// let bv = binaryninja::load_with_options("/bin/cat", true, Some(json!("analysis.linearSweep.autorun": false).to_string())) /// .expect("Couldn't open `/bin/cat`"); /// ``` -pub fn load_with_options<S: BnStrCompatible, O: IntoJson>( +pub fn load_with_options<S, O>( filename: S, update_analysis_and_wait: bool, options: Option<O>, -) -> Option<rc::Ref<binaryview::BinaryView>> { +) -> Option<rc::Ref<binaryview::BinaryView>> +where + S: BnStrCompatible, + O: IntoJson, +{ let filename = filename.into_bytes_with_nul(); let options_or_default = if let Some(opt) = options { @@ -266,7 +330,8 @@ pub fn load_with_options<S: BnStrCompatible, O: IntoJson>( filename.as_ref().as_ptr() as *mut _, update_analysis_and_wait, options_or_default.as_ptr() as *mut core::ffi::c_char, - None, + Some(cb_progress_nop), + core::ptr::null_mut(), ) }; @@ -277,11 +342,63 @@ pub fn load_with_options<S: BnStrCompatible, O: IntoJson>( } } -pub fn load_view<O: IntoJson>( +pub fn load_with_options_and_progress<S, O, F>( + filename: S, + update_analysis_and_wait: bool, + options: Option<O>, + progress: Option<F>, +) -> Option<rc::Ref<binaryview::BinaryView>> +where + S: BnStrCompatible, + O: IntoJson, + F: FnMut(usize, usize) -> bool, +{ + let filename = filename.into_bytes_with_nul(); + + let options_or_default = if let Some(opt) = options { + opt.get_json_string() + .ok()? + .into_bytes_with_nul() + .as_ref() + .to_vec() + } else { + Metadata::new_of_type(MetadataType::KeyValueDataType) + .get_json_string() + .ok()? + .as_ref() + .to_vec() + }; + + let progress_ctx = match progress { + Some(mut x) => &mut x as *mut F as *mut std::ffi::c_void, + None => core::ptr::null_mut() + }; + + let handle = unsafe { + binaryninjacore_sys::BNLoadFilename( + filename.as_ref().as_ptr() as *mut _, + update_analysis_and_wait, + options_or_default.as_ptr() as *mut core::ffi::c_char, + Some(cb_progress_func::<F>), + progress_ctx, + ) + }; + + if handle.is_null() { + None + } else { + Some(unsafe { BinaryView::from_raw(handle) }) + } +} + +pub fn load_view<O>( bv: &BinaryView, update_analysis_and_wait: bool, options: Option<O>, -) -> Option<rc::Ref<binaryview::BinaryView>> { +) -> Option<rc::Ref<binaryview::BinaryView>> +where + O: IntoJson, +{ let options_or_default = if let Some(opt) = options { opt.get_json_string() .ok()? @@ -301,7 +418,54 @@ pub fn load_view<O: IntoJson>( bv.handle as *mut _, update_analysis_and_wait, options_or_default.as_ptr() as *mut core::ffi::c_char, - None, + Some(cb_progress_nop), + core::ptr::null_mut(), + ) + }; + + if handle.is_null() { + None + } else { + Some(unsafe { BinaryView::from_raw(handle) }) + } +} + +pub fn load_view_with_progress<O, F>( + bv: &BinaryView, + update_analysis_and_wait: bool, + options: Option<O>, + progress: Option<F>, +) -> Option<rc::Ref<binaryview::BinaryView>> +where + O: IntoJson, + F: FnMut(usize, usize) -> bool, +{ + let options_or_default = if let Some(opt) = options { + opt.get_json_string() + .ok()? + .into_bytes_with_nul() + .as_ref() + .to_vec() + } else { + Metadata::new_of_type(MetadataType::KeyValueDataType) + .get_json_string() + .ok()? + .as_ref() + .to_vec() + }; + + let progress_ctx = match progress { + Some(mut x) => &mut x as *mut F as *mut std::ffi::c_void, + None => core::ptr::null_mut() + }; + + let handle = unsafe { + binaryninjacore_sys::BNLoadBinaryView( + bv.handle as *mut _, + update_analysis_and_wait, + options_or_default.as_ptr() as *mut core::ffi::c_char, + Some(cb_progress_func::<F>), + progress_ctx, ) }; diff --git a/rust/src/update.rs b/rust/src/update.rs index 026316f5..31e02cd8 100644 --- a/rust/src/update.rs +++ b/rust/src/update.rs @@ -100,7 +100,7 @@ impl UpdateChannel { mut progress: F, ) -> Result<UpdateResult, BnString> where - F: FnMut(u64, u64) -> bool, + F: FnMut(usize, usize) -> bool, { let mut errors = ptr::null_mut(); let result = unsafe { @@ -142,7 +142,7 @@ impl UpdateChannel { mut progress: F, ) -> Result<UpdateResult, BnString> where - F: FnMut(u64, u64) -> bool, + F: FnMut(usize, usize) -> bool, { let mut errors = ptr::null_mut(); let result = unsafe { @@ -256,16 +256,16 @@ pub fn updates_checked() { unsafe extern "C" fn cb_progress_nop( _ctxt: *mut ::std::os::raw::c_void, - _progress: u64, - _total: u64, + _progress: usize, + _total: usize, ) -> bool { true } -unsafe extern "C" fn cb_progress<F: FnMut(u64, u64) -> bool>( +unsafe extern "C" fn cb_progress<F: FnMut(usize, usize) -> bool>( ctxt: *mut ::std::os::raw::c_void, - progress: u64, - total: u64, + progress: usize, + total: usize, ) -> bool { let ctxt: &mut F = &mut *(ctxt as *mut F); ctxt(progress, total) |
