use std::collections::HashMap; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use std::{ffi, mem, ptr}; use binaryninjacore_sys::*; use crate::binaryview::BinaryView; use crate::databuffer::DataBuffer; use crate::disassembly::InstructionTextToken; use crate::filemetadata::FileMetadata; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref}; use crate::string::{BnStrCompatible, BnString}; #[repr(transparent)] pub struct Database { handle: ptr::NonNull, } impl Database { pub(crate) unsafe fn from_raw(handle: ptr::NonNull) -> Self { Self { handle } } #[allow(clippy::mut_from_ref)] pub(crate) unsafe fn as_raw(&self) -> &mut BNDatabase { &mut *self.handle.as_ptr() } /// Get a snapshot by its id, or None if no snapshot with that id exists pub fn snapshot(&self, id: i64) -> Option { let result = unsafe { BNGetDatabaseSnapshot(self.as_raw(), id) }; ptr::NonNull::new(result).map(|handle| unsafe { Snapshot::from_raw(handle) }) } /// Get a list of all snapshots in the database pub fn snapshots(&self) -> Array { let mut count = 0; let result = unsafe { BNGetDatabaseSnapshots(self.as_raw(), &mut count) }; assert!(!result.is_null()); unsafe { Array::new(result, count, ()) } } /// Get the current snapshot pub fn current_snapshot(&self) -> Option { let result = unsafe { BNGetDatabaseCurrentSnapshot(self.as_raw()) }; ptr::NonNull::new(result).map(|handle| unsafe { Snapshot::from_raw(handle) }) } pub fn set_current_snapshot(&self, value: &Snapshot) { unsafe { BNSetDatabaseCurrentSnapshot(self.as_raw(), value.id()) } } pub fn write_snapshot_data( &self, parents: &[i64], file: &BinaryView, name: N, data: &KeyValueStore, auto_save: bool, ) -> i64 { let name_raw = name.into_bytes_with_nul(); let name_ptr = name_raw.as_ref().as_ptr() as *const ffi::c_char; unsafe { BNWriteDatabaseSnapshotData( self.as_raw(), parents.as_ptr() as *mut _, parents.len(), file.handle, name_ptr, data.as_raw(), auto_save, ptr::null_mut(), Some(cb_progress_nop), ) } } pub fn write_snapshot_data_with_progress( &self, parents: &[i64], file: &BinaryView, name: N, data: &KeyValueStore, auto_save: bool, mut progress: F, ) -> i64 where N: BnStrCompatible, F: FnMut(usize, usize) -> bool, { let name_raw = name.into_bytes_with_nul(); let name_ptr = name_raw.as_ref().as_ptr() as *const ffi::c_char; let ctxt = &mut progress as *mut _ as *mut ffi::c_void; unsafe { BNWriteDatabaseSnapshotData( self.as_raw(), parents.as_ptr() as *mut _, parents.len(), file.handle, name_ptr, data.as_raw(), auto_save, ctxt, Some(cb_progress::), ) } } /// Trim a snapshot's contents in the database by id, but leave the parent/child /// hierarchy intact. Future references to this snapshot will return False for has_contents pub fn trim_snapshot(&self, id: i64) -> Result<(), ()> { if unsafe { BNTrimDatabaseSnapshot(self.as_raw(), id) } { Ok(()) } else { Err(()) } } /// Remove a snapshot in the database by id, deleting its contents and references. /// Attempting to remove a snapshot with children will raise an exception. pub fn remove_snapshot(&self, id: i64) -> Result<(), ()> { if unsafe { BNRemoveDatabaseSnapshot(self.as_raw(), id) } { Ok(()) } else { Err(()) } } pub fn has_global(&self, key: S) -> bool { let key_raw = key.into_bytes_with_nul(); let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char; unsafe { BNDatabaseHasGlobal(self.as_raw(), key_ptr) != 0 } } /// Get a list of keys for all globals in the database pub fn global_keys(&self) -> Array { let mut count = 0; let result = unsafe { BNGetDatabaseGlobalKeys(self.as_raw(), &mut count) }; assert!(!result.is_null()); unsafe { Array::new(result, count, ()) } } /// Get a dictionary of all globals pub fn globals(&self) -> HashMap { self.global_keys() .iter() .filter_map(|key| Some((key.to_string(), self.read_global(key)?.to_string()))) .collect() } /// Get a specific global by key pub fn read_global(&self, key: S) -> Option { let key_raw = key.into_bytes_with_nul(); let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char; let result = unsafe { BNReadDatabaseGlobal(self.as_raw(), key_ptr) }; unsafe { ptr::NonNull::new(result).map(|_| BnString::from_raw(result)) } } /// Write a global into the database pub fn write_global(&self, key: K, value: V) -> bool { let key_raw = key.into_bytes_with_nul(); let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char; let value_raw = value.into_bytes_with_nul(); let value_ptr = value_raw.as_ref().as_ptr() as *const ffi::c_char; unsafe { BNWriteDatabaseGlobal(self.as_raw(), key_ptr, value_ptr) } } /// Get a specific global by key, as a binary buffer pub fn read_global_data(&self, key: S) -> Option { let key_raw = key.into_bytes_with_nul(); let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char; let result = unsafe { BNReadDatabaseGlobalData(self.as_raw(), key_ptr) }; ptr::NonNull::new(result).map(|_| DataBuffer::from_raw(result)) } /// Write a binary buffer into a global in the database pub fn write_global_data(&self, key: K, value: &DataBuffer) -> bool { let key_raw = key.into_bytes_with_nul(); let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char; unsafe { BNWriteDatabaseGlobalData(self.as_raw(), key_ptr, value.as_raw()) } } /// Get the owning FileMetadata pub fn file(&self) -> Ref { let result = unsafe { BNGetDatabaseFile(self.as_raw()) }; assert!(!result.is_null()); unsafe { Ref::new(FileMetadata::from_raw(result)) } } /// Get the backing analysis cache kvs pub fn analysis_cache(&self) -> KeyValueStore { let result = unsafe { BNReadDatabaseAnalysisCache(self.as_raw()) }; unsafe { KeyValueStore::from_raw(ptr::NonNull::new(result).unwrap()) } } pub fn reload_connection(&self) { unsafe { BNDatabaseReloadConnection(self.as_raw()) } } pub fn write_analysis_cache(&self, val: &KeyValueStore) -> Result<(), ()> { if unsafe { BNWriteDatabaseAnalysisCache(self.as_raw(), val.as_raw()) } { Ok(()) } else { Err(()) } } pub fn snapshot_has_data(&self, id: i64) -> bool { unsafe { BNSnapshotHasData(self.as_raw(), id) } } } impl Clone for Database { fn clone(&self) -> Self { unsafe { Self::from_raw(ptr::NonNull::new(BNNewDatabaseReference(self.as_raw())).unwrap()) } } } impl Drop for Database { fn drop(&mut self) { unsafe { BNFreeDatabase(self.as_raw()) } } } #[repr(transparent)] pub struct Snapshot { handle: ptr::NonNull, } impl Snapshot { pub(crate) unsafe fn from_raw(handle: ptr::NonNull) -> Self { Self { handle } } pub(crate) unsafe fn ref_from_raw(handle: &*mut BNSnapshot) -> &Self { mem::transmute(handle) } #[allow(clippy::mut_from_ref)] pub(crate) unsafe fn as_raw(&self) -> &mut BNSnapshot { &mut *self.handle.as_ptr() } /// Get the owning database pub fn database(&self) -> Database { unsafe { Database::from_raw(ptr::NonNull::new(BNGetSnapshotDatabase(self.as_raw())).unwrap()) } } /// Get the numerical id (read-only) pub fn id(&self) -> i64 { unsafe { BNGetSnapshotId(self.as_raw()) } } /// Get the displayed snapshot name pub fn name(&self) -> BnString { unsafe { BnString::from_raw(BNGetSnapshotName(self.as_raw())) } } /// Set the displayed snapshot name pub fn set_name(&self, value: S) { let value_raw = value.into_bytes_with_nul(); let value_ptr = value_raw.as_ref().as_ptr() as *const ffi::c_char; unsafe { BNSetSnapshotName(self.as_raw(), value_ptr) } } /// If the snapshot was the result of an auto-save pub fn is_auto_save(&self) -> bool { unsafe { BNIsSnapshotAutoSave(self.as_raw()) } } /// If the snapshot has contents, and has not been trimmed pub fn has_contents(&self) -> bool { unsafe { BNSnapshotHasContents(self.as_raw()) } } /// If the snapshot has undo data pub fn has_undo(&self) -> bool { unsafe { BNSnapshotHasUndo(self.as_raw()) } } /// Get the first parent of the snapshot, or None if it has no parents pub fn first_parent(&self) -> Option { let result = unsafe { BNGetSnapshotFirstParent(self.as_raw()) }; ptr::NonNull::new(result).map(|s| unsafe { Snapshot::from_raw(s) }) } /// Get a list of all parent snapshots of the snapshot pub fn parents(&self) -> Array { let mut count = 0; let result = unsafe { BNGetSnapshotParents(self.as_raw(), &mut count) }; assert!(!result.is_null()); unsafe { Array::new(result, count, ()) } } /// Get a list of all child snapshots of the snapshot pub fn children(&self) -> Array { let mut count = 0; let result = unsafe { BNGetSnapshotChildren(self.as_raw(), &mut count) }; assert!(!result.is_null()); unsafe { Array::new(result, count, ()) } } /// Get a buffer of the raw data at the time of the snapshot pub fn file_contents(&self) -> Option { self.has_contents().then(|| unsafe { let result = BNGetSnapshotFileContents(self.as_raw()); assert!(!result.is_null()); DataBuffer::from_raw(result) }) } /// Get a hash of the data at the time of the snapshot pub fn file_contents_hash(&self) -> Option { self.has_contents().then(|| unsafe { let result = BNGetSnapshotFileContentsHash(self.as_raw()); assert!(!result.is_null()); DataBuffer::from_raw(result) }) } /// Get a list of undo entries at the time of the snapshot pub fn undo_entries(&self) -> Array { assert!(self.has_undo()); let mut count = 0; let result = unsafe { BNGetSnapshotUndoEntries(self.as_raw(), &mut count) }; assert!(!result.is_null()); unsafe { Array::new(result, count, ()) } } pub fn undo_entries_with_progress bool>( &self, mut progress: F, ) -> Array { assert!(self.has_undo()); let ctxt = &mut progress as *mut _ as *mut ffi::c_void; let mut count = 0; let result = unsafe { BNGetSnapshotUndoEntriesWithProgress( self.as_raw(), ctxt, Some(cb_progress::), &mut count, ) }; assert!(!result.is_null()); unsafe { Array::new(result, count, ()) } } /// Get the backing kvs data with snapshot fields pub fn read_data(&self) -> KeyValueStore { let result = unsafe { BNReadSnapshotData(self.as_raw()) }; unsafe { KeyValueStore::from_raw(ptr::NonNull::new(result).unwrap()) } } pub fn read_data_with_progress bool>( &self, mut progress: F, ) -> KeyValueStore { let ctxt = &mut progress as *mut _ as *mut ffi::c_void; let result = unsafe { BNReadSnapshotDataWithProgress(self.as_raw(), ctxt, Some(cb_progress::)) }; unsafe { KeyValueStore::from_raw(ptr::NonNull::new(result).unwrap()) } } pub fn undo_data(&self) -> DataBuffer { let result = unsafe { BNGetSnapshotUndoData(self.as_raw()) }; assert!(!result.is_null()); DataBuffer::from_raw(result) } pub fn store_data bool>( &self, data: KeyValueStore, mut progress: F, ) -> bool { let ctxt = &mut progress as *mut _ as *mut ffi::c_void; unsafe { BNSnapshotStoreData(self.as_raw(), data.as_raw(), ctxt, Some(cb_progress::)) } } /// Determine if this snapshot has another as an ancestor pub fn has_ancestor(self, other: &Snapshot) -> bool { unsafe { BNSnapshotHasAncestor(self.as_raw(), other.as_raw()) } } } impl Clone for Snapshot { fn clone(&self) -> Self { unsafe { Self::from_raw(ptr::NonNull::new(BNNewSnapshotReference(self.as_raw())).unwrap()) } } } impl Drop for Snapshot { fn drop(&mut self) { unsafe { BNFreeSnapshot(self.as_raw()) } } } impl CoreArrayProvider for Snapshot { type Raw = *mut BNSnapshot; type Context = (); type Wrapped<'a> = &'a Self; } unsafe impl CoreArrayProviderInner for Snapshot { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeSnapshotList(raw, count); } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { Self::ref_from_raw(raw) } } #[repr(transparent)] pub struct KeyValueStore { handle: ptr::NonNull, } impl KeyValueStore { pub(crate) unsafe fn from_raw(handle: ptr::NonNull) -> Self { Self { handle } } #[allow(clippy::mut_from_ref)] pub(crate) unsafe fn as_raw(&self) -> &mut BNKeyValueStore { &mut *self.handle.as_ptr() } /// Get a list of all keys stored in the kvs pub fn keys(&self) -> Array { let mut count = 0; let result = unsafe { BNGetKeyValueStoreKeys(self.as_raw(), &mut count) }; assert!(!result.is_null()); unsafe { Array::new(result, count, ()) } } /// Get the value for a single key pub fn value(&self, key: S) -> Option { let key_raw = key.into_bytes_with_nul(); let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char; let result = unsafe { BNGetKeyValueStoreBuffer(self.as_raw(), key_ptr) }; ptr::NonNull::new(result).map(|_| DataBuffer::from_raw(result)) } /// Set the value for a single key pub fn set_value(&self, key: S, value: &DataBuffer) -> bool { let key_raw = key.into_bytes_with_nul(); let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char; unsafe { BNSetKeyValueStoreBuffer(self.as_raw(), key_ptr, value.as_raw()) } } /// Get the stored representation of the kvs pub fn serialized_data(&self) -> DataBuffer { let result = unsafe { BNGetKeyValueStoreSerializedData(self.as_raw()) }; assert!(!result.is_null()); DataBuffer::from_raw(result) } /// Begin storing new keys into a namespace pub fn begin_namespace(&self, name: S) { let name_raw = name.into_bytes_with_nul(); let name_ptr = name_raw.as_ref().as_ptr() as *const ffi::c_char; unsafe { BNBeginKeyValueStoreNamespace(self.as_raw(), name_ptr) } } /// End storing new keys into a namespace pub fn end_namespace(&self) { unsafe { BNEndKeyValueStoreNamespace(self.as_raw()) } } /// If the kvs is empty pub fn empty(&self) -> bool { unsafe { BNIsKeyValueStoreEmpty(self.as_raw()) } } /// Number of values in the kvs pub fn value_size(&self) -> usize { unsafe { BNGetKeyValueStoreValueSize(self.as_raw()) } } /// Length of serialized data pub fn data_size(&self) -> usize { unsafe { BNGetKeyValueStoreDataSize(self.as_raw()) } } /// Size of all data in storage pub fn value_storage_size(self) -> usize { unsafe { BNGetKeyValueStoreValueStorageSize(self.as_raw()) } } /// Number of namespaces pushed with begin_namespace pub fn namespace_size(self) -> usize { unsafe { BNGetKeyValueStoreNamespaceSize(self.as_raw()) } } } impl Clone for KeyValueStore { fn clone(&self) -> Self { unsafe { Self::from_raw(ptr::NonNull::new(BNNewKeyValueStoreReference(self.as_raw())).unwrap()) } } } impl Drop for KeyValueStore { fn drop(&mut self) { unsafe { BNFreeKeyValueStore(self.as_raw()) } } } #[repr(transparent)] pub struct UndoEntry { handle: ptr::NonNull, } impl UndoEntry { pub(crate) unsafe fn from_raw(handle: ptr::NonNull) -> Self { Self { handle } } pub(crate) unsafe fn ref_from_raw(handle: &*mut BNUndoEntry) -> &Self { mem::transmute(handle) } #[allow(clippy::mut_from_ref)] pub(crate) unsafe fn as_raw(&self) -> &mut BNUndoEntry { &mut *self.handle.as_ptr() } pub fn id(&self) -> BnString { let result = unsafe { BNUndoEntryGetId(self.as_raw()) }; assert!(!result.is_null()); unsafe { BnString::from_raw(result) } } pub fn actions(&self) -> Array { let mut count = 0; let result = unsafe { BNUndoEntryGetActions(self.as_raw(), &mut count) }; assert!(!result.is_null()); unsafe { Array::new(result, count, ()) } } pub fn time(&self) -> SystemTime { let m = Duration::from_secs(unsafe { BNUndoEntryGetTimestamp(self.as_raw()) }); UNIX_EPOCH + m } } impl Clone for UndoEntry { fn clone(&self) -> Self { unsafe { Self::from_raw(ptr::NonNull::new(BNNewUndoEntryReference(self.as_raw())).unwrap()) } } } impl Drop for UndoEntry { fn drop(&mut self) { unsafe { BNFreeUndoEntry(self.as_raw()) } } } impl CoreArrayProvider for UndoEntry { type Raw = *mut BNUndoEntry; type Context = (); type Wrapped<'a> = &'a Self; } unsafe impl CoreArrayProviderInner for UndoEntry { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeUndoEntryList(raw, count); } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { Self::ref_from_raw(raw) } } #[repr(transparent)] pub struct UndoAction { handle: ptr::NonNull, } impl UndoAction { pub(crate) unsafe fn from_raw(handle: ptr::NonNull) -> Self { Self { handle } } pub(crate) unsafe fn ref_from_raw(handle: &*mut BNUndoAction) -> &Self { mem::transmute(handle) } #[allow(clippy::mut_from_ref)] pub(crate) unsafe fn as_raw(&self) -> &mut BNUndoAction { &mut *self.handle.as_ptr() } pub fn summary_text(&self) -> BnString { let result = unsafe { BNUndoActionGetSummaryText(self.as_raw()) }; assert!(!result.is_null()); unsafe { BnString::from_raw(result) } } pub fn summary(&self) -> Array { let mut count = 0; let result = unsafe { BNUndoActionGetSummary(self.as_raw(), &mut count) }; assert!(!result.is_null()); unsafe { Array::new(result, count, ()) } } } impl Clone for UndoAction { fn clone(&self) -> Self { unsafe { Self::from_raw(ptr::NonNull::new(BNNewUndoActionReference(self.as_raw())).unwrap()) } } } impl Drop for UndoAction { fn drop(&mut self) { unsafe { BNFreeUndoAction(self.as_raw()) } } } impl CoreArrayProvider for UndoAction { type Raw = *mut BNUndoAction; type Context = (); type Wrapped<'a> = &'a Self; } unsafe impl CoreArrayProviderInner for UndoAction { unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { BNFreeUndoActionList(raw, count); } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { Self::ref_from_raw(raw) } } unsafe extern "C" fn cb_progress bool>( ctxt: *mut ffi::c_void, arg1: usize, arg2: usize, ) -> bool { let ctxt: &mut F = &mut *(ctxt as *mut F); ctxt(arg1, arg2) } unsafe extern "C" fn cb_progress_nop(_ctxt: *mut ffi::c_void, _arg1: usize, _arg2: usize) -> bool { true }