From ad27532d344d3bf2995f623002753d2845c6ce20 Mon Sep 17 00:00:00 2001 From: Glenn Smith Date: Mon, 18 Jul 2022 19:53:47 -0400 Subject: [Rust API] Add support for Metadata --- rust/src/binaryview.rs | 35 ++- rust/src/custombinaryview.rs | 2 +- rust/src/lib.rs | 13 +- rust/src/metadata.rs | 652 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 692 insertions(+), 10 deletions(-) create mode 100644 rust/src/metadata.rs (limited to 'rust/src') diff --git a/rust/src/binaryview.rs b/rust/src/binaryview.rs index a372b778..e64dbb61 100644 --- a/rust/src/binaryview.rs +++ b/rust/src/binaryview.rs @@ -18,10 +18,12 @@ //! TODO : Mirror the Python docs for this use binaryninjacore_sys::*; +use std::convert::TryFrom; pub use binaryninjacore_sys::BNModificationStatus as ModificationStatus; use std::ops; +use std::os::raw::c_char; use std::ptr; use std::result; @@ -36,6 +38,7 @@ use crate::flowgraph::FlowGraph; use crate::function::{Function, NativeBlock}; use crate::linearview::LinearDisassemblyLine; use crate::linearview::LinearViewCursor; +use crate::metadata::Metadata; use crate::platform::Platform; use crate::section::{Section, SectionBuilder}; use crate::segment::{Segment, SegmentBuilder}; @@ -133,7 +136,7 @@ pub trait BinaryViewBase: AsRef { } pub trait BinaryViewExt: BinaryViewBase { - fn metadata(&self) -> Ref { + fn file(&self) -> Ref { unsafe { let raw = BNGetFileForView(self.as_ref().handle); @@ -829,6 +832,36 @@ pub trait BinaryViewExt: BinaryViewBase { result } + + fn query_metadata(&self, key: S) -> Option> { + let value: *mut BNMetadata = unsafe { + BNBinaryViewQueryMetadata( + self.as_ref().handle, + key.as_bytes_with_nul().as_ref().as_ptr() as *const c_char, + ) + }; + if value.is_null() { + None + } else { + Some(unsafe { Metadata::ref_from_raw(value) }) + } + } + + fn get_metadata, S: BnStrCompatible>( + &self, + key: S, + ) -> Option> { + self.query_metadata(key).map(|md| T::try_from(md)) + } + + fn remove_metadata(&self, key: S) { + unsafe { + BNBinaryViewRemoveMetadata( + self.as_ref().handle, + key.as_bytes_with_nul().as_ref().as_ptr() as *const c_char, + ) + }; + } } impl BinaryViewExt for T {} diff --git a/rust/src/custombinaryview.rs b/rust/src/custombinaryview.rs index 14944cd1..13470517 100644 --- a/rust/src/custombinaryview.rs +++ b/rust/src/custombinaryview.rs @@ -361,7 +361,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> { where V: CustomBinaryView, { - let file = self.actual_parent.metadata(); + let file = self.actual_parent.file(); let view_type = self.view_type; let view_name = view_type.name(); diff --git a/rust/src/lib.rs b/rust/src/lib.rs index d1181bb4..c91c9792 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -146,6 +146,7 @@ pub mod headless; pub mod interaction; pub mod linearview; pub mod llil; +pub mod metadata; pub mod platform; pub mod rc; pub mod section; @@ -362,11 +363,7 @@ pub fn open_view>(filename: F) -> Result>(filename: F) -> Result>( if is_bndb { let view = view - .metadata() + .file() .open_database(filename.to_str().unwrap()) .expect("Couldn't open database"); let view_type_name = view_type.name(); - let bv = match view.metadata().get_view_of_type(view_type_name) { + let bv = match view.file().get_view_of_type(view_type_name) { Ok(bv) => bv, _ => view, }; diff --git a/rust/src/metadata.rs b/rust/src/metadata.rs new file mode 100644 index 00000000..f7d5e45e --- /dev/null +++ b/rust/src/metadata.rs @@ -0,0 +1,652 @@ +#[allow(non_snake_case)] +use crate::rc::{ + Array, CoreArrayProvider, CoreArrayWrapper, CoreOwnedArrayProvider, Guard, Ref, RefCountable, +}; +use crate::string::{BnStrCompatible, BnString}; +use binaryninjacore_sys::*; +use std::collections::HashMap; +use std::convert::TryFrom; +use std::ffi::CStr; +use std::ops::Index; +use std::os::raw::c_char; +use std::slice; + +pub type MetadataType = BNMetadataType; + +pub struct Metadata { + handle: *mut BNMetadata, +} + +impl Metadata { + pub(crate) unsafe fn from_raw(handle: *mut BNMetadata) -> Self { + debug_assert!(!handle.is_null()); + + Self { handle } + } + + pub(crate) unsafe fn ref_from_raw(handle: *mut BNMetadata) -> Ref { + Ref::new(Self::from_raw(handle)) + } + + pub fn get_type(&self) -> MetadataType { + unsafe { BNMetadataGetType(self.handle) } + } + + pub fn get_boolean(&self) -> Result { + match self.get_type() { + MetadataType::BooleanDataType => Ok(unsafe { BNMetadataGetBoolean(self.handle) }), + _ => Err(()), + } + } + + pub fn get_unsigned_integer(&self) -> Result { + match self.get_type() { + MetadataType::UnsignedIntegerDataType => { + Ok(unsafe { BNMetadataGetUnsignedInteger(self.handle) }) + } + _ => Err(()), + } + } + + pub fn get_signed_integer(&self) -> Result { + match self.get_type() { + MetadataType::SignedIntegerDataType => { + Ok(unsafe { BNMetadataGetSignedInteger(self.handle) }) + } + _ => Err(()), + } + } + + pub fn get_double(&self) -> Result { + match self.get_type() { + MetadataType::DoubleDataType => Ok(unsafe { BNMetadataGetDouble(self.handle) }), + _ => Err(()), + } + } + + pub fn get_string(&self) -> Result { + match self.get_type() { + MetadataType::StringDataType => { + let ptr: *mut c_char = unsafe { BNMetadataGetString(self.handle) }; + if ptr.is_null() { + return Err(()); + } + Ok(unsafe { BnString::from_raw(ptr) }) + } + _ => Err(()), + } + } + + pub fn get_boolean_list(&self) -> Result, ()> { + match self.get_type() { + MetadataType::ArrayDataType => { + let mut size: usize = 0; + let ptr: *mut bool = unsafe { BNMetadataGetBooleanList(self.handle, &mut size) }; + if ptr.is_null() { + return Err(()); + } + let list = unsafe { slice::from_raw_parts(ptr, size) }; + let vec = Vec::from(list); + unsafe { BNFreeMetadataBooleanList(ptr, size) }; + Ok(vec) + } + _ => Err(()), + } + } + + pub fn get_unsigned_integer_list(&self) -> Result, ()> { + match self.get_type() { + MetadataType::ArrayDataType => { + let mut size: usize = 0; + let ptr: *mut u64 = + unsafe { BNMetadataGetUnsignedIntegerList(self.handle, &mut size) }; + if ptr.is_null() { + return Err(()); + } + let list = unsafe { slice::from_raw_parts(ptr, size) }; + let vec = Vec::from(list); + unsafe { BNFreeMetadataUnsignedIntegerList(ptr, size) }; + Ok(vec) + } + _ => Err(()), + } + } + + pub fn get_signed_integer_list(&self) -> Result, ()> { + match self.get_type() { + MetadataType::ArrayDataType => { + let mut size: usize = 0; + let ptr: *mut i64 = + unsafe { BNMetadataGetSignedIntegerList(self.handle, &mut size) }; + if ptr.is_null() { + return Err(()); + } + let list = unsafe { slice::from_raw_parts(ptr, size) }; + let vec = Vec::from(list); + unsafe { BNFreeMetadataSignedIntegerList(ptr, size) }; + Ok(vec) + } + _ => Err(()), + } + } + + pub fn get_double_list(&self) -> Result, ()> { + match self.get_type() { + MetadataType::ArrayDataType => { + let mut size: usize = 0; + let ptr: *mut f64 = unsafe { BNMetadataGetDoubleList(self.handle, &mut size) }; + if ptr.is_null() { + return Err(()); + } + let list = unsafe { slice::from_raw_parts(ptr, size) }; + let vec = Vec::from(list); + unsafe { BNFreeMetadataDoubleList(ptr, size) }; + Ok(vec) + } + _ => Err(()), + } + } + + pub fn get_string_list(&self) -> Result, ()> { + match self.get_type() { + MetadataType::ArrayDataType => { + let mut size: usize = 0; + let ptr: *mut *mut c_char = + unsafe { BNMetadataGetStringList(self.handle, &mut size) }; + if ptr.is_null() { + return Err(()); + } + let list = unsafe { slice::from_raw_parts(ptr, size) }; + let vec = list + .iter() + .map(|ptr| unsafe { BnString::from_raw(*ptr) }) + .collect::>(); + unsafe { BNFreeMetadataStringList(ptr, size) }; + Ok(vec) + } + _ => Err(()), + } + } + + pub fn get_raw(&self) -> Result, ()> { + match self.get_type() { + MetadataType::RawDataType => { + let mut size: usize = 0; + let ptr: *mut u8 = unsafe { BNMetadataGetRaw(self.handle, &mut size) }; + if ptr.is_null() { + return Err(()); + } + + let list = unsafe { slice::from_raw_parts(ptr, size) }; + let vec = Vec::from(list); + unsafe { BNFreeMetadataRaw(ptr) }; + Ok(vec) + } + _ => Err(()), + } + } + + pub fn get_array(&self) -> Result, ()> { + match self.get_type() { + MetadataType::ArrayDataType => { + let mut size: usize = 0; + let ptr: *mut *mut BNMetadata = + unsafe { BNMetadataGetArray(self.handle, &mut size) }; + if ptr.is_null() { + return Err(()); + } + + Ok(unsafe { Array::new(ptr, size, ()) }) + } + _ => Err(()), + } + } + + pub fn get_value_store(&self) -> Result>, ()> { + match self.get_type() { + MetadataType::KeyValueDataType => { + let ptr: *mut BNMetadataValueStore = + unsafe { BNMetadataGetValueStore(self.handle) }; + if ptr.is_null() { + return Err(()); + } + + let size = unsafe { (*ptr).size }; + let keys_ptr: *mut *mut c_char = unsafe { (*ptr).keys }; + let keys = unsafe { slice::from_raw_parts(keys_ptr, size) }; + let values_ptr: *mut *mut BNMetadata = unsafe { (*ptr).values }; + let values: &[*mut BNMetadata] = unsafe { slice::from_raw_parts(values_ptr, size) }; + + let mut map = HashMap::new(); + for i in 0..size { + let key = unsafe { BnString::from_raw(keys[i]) }; + + let value = unsafe { + Ref::::new(Self { + handle: BNNewMetadataReference(values[i]), + }) + }; + map.insert(key, value); + } + + Ok(map) + } + _ => Err(()), + } + } + + pub fn len(&self) -> usize { + unsafe { BNMetadataSize(self.handle) } + } + + pub fn index(&self, index: usize) -> Result>, ()> { + if self.get_type() != MetadataType::ArrayDataType { + return Err(()); + } + let ptr: *mut BNMetadata = unsafe { BNMetadataGetForIndex(self.handle, index) }; + if ptr.is_null() { + return Ok(None); + } + Ok(Some(unsafe { Self::ref_from_raw(ptr) })) + } + + pub fn get(&self, key: S) -> Result>, ()> { + if self.get_type() != MetadataType::KeyValueDataType { + return Err(()); + } + let ptr: *mut BNMetadata = unsafe { + BNMetadataGetForKey( + self.handle, + key.as_bytes_with_nul().as_ref().as_ptr() as *const c_char, + ) + }; + if ptr.is_null() { + return Ok(None); + } + Ok(Some(unsafe { Self::ref_from_raw(ptr) })) + } + + pub fn push(&self, value: &Metadata) -> Result<(), ()> { + if self.get_type() != MetadataType::ArrayDataType { + return Err(()); + } + unsafe { BNMetadataArrayAppend(self.handle, value.handle) }; + Ok(()) + } + + pub fn insert(&self, key: S, value: &Metadata) -> Result<(), ()> { + if self.get_type() != MetadataType::KeyValueDataType { + return Err(()); + } + + unsafe { + BNMetadataSetValueForKey( + self.handle, + key.as_bytes_with_nul().as_ref().as_ptr() as *const c_char, + value.handle, + ) + }; + Ok(()) + } + + pub fn remove_index(&self, index: usize) -> Result<(), ()> { + if self.get_type() != MetadataType::ArrayDataType { + return Err(()); + } + + unsafe { BNMetadataRemoveIndex(self.handle, index) }; + Ok(()) + } + + pub fn remove_key(&self, key: S) -> Result<(), ()> { + if self.get_type() != MetadataType::KeyValueDataType { + return Err(()); + } + + unsafe { + BNMetadataRemoveKey( + self.handle, + key.as_bytes_with_nul().as_ref().as_ptr() as *const c_char, + ) + }; + Ok(()) + } +} + +unsafe impl Sync for Metadata {} +unsafe impl Send for Metadata {} + +unsafe impl RefCountable for Metadata { + unsafe fn inc_ref(handle: &Self) -> Ref { + Ref::new(Self { + handle: BNNewMetadataReference(handle.handle), + }) + } + + unsafe fn dec_ref(handle: &Self) { + BNFreeMetadata(handle.handle); + } +} + +impl CoreArrayProvider for Metadata { + type Raw = *mut BNMetadata; + type Context = (); +} + +unsafe impl CoreOwnedArrayProvider for Metadata { + unsafe fn free(raw: *mut *mut BNMetadata, _count: usize, _context: &()) { + BNFreeMetadataArray(raw); + } +} + +unsafe impl<'a> CoreArrayWrapper<'a> for Metadata { + type Wrapped = Guard<'a, Metadata>; + + unsafe fn wrap_raw(raw: &'a *mut BNMetadata, context: &'a ()) -> Guard<'a, Metadata> { + Guard::new(Metadata::from_raw(*raw), context) + } +} + +impl AsRef for Metadata { + fn as_ref(&self) -> &Self { + self + } +} + +impl ToOwned for Metadata { + type Owned = Ref; + + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } + } +} + +impl From for Ref { + fn from(value: bool) -> Self { + unsafe { Metadata::ref_from_raw(BNCreateMetadataBooleanData(value)) } + } +} + +impl From for Ref { + fn from(value: u64) -> Self { + unsafe { Metadata::ref_from_raw(BNCreateMetadataUnsignedIntegerData(value)) } + } +} + +impl From for Ref { + fn from(value: i64) -> Self { + unsafe { Metadata::ref_from_raw(BNCreateMetadataSignedIntegerData(value)) } + } +} + +impl From for Ref { + fn from(value: f64) -> Self { + unsafe { Metadata::ref_from_raw(BNCreateMetadataDoubleData(value)) } + } +} + +impl From for Ref { + fn from(value: S) -> Self { + unsafe { + Metadata::ref_from_raw(BNCreateMetadataStringData( + value.as_bytes_with_nul().as_ref().as_ptr() as *const c_char, + )) + } + } +} + +impl From<&Vec> for Ref { + fn from(value: &Vec) -> Self { + unsafe { Metadata::ref_from_raw(BNCreateMetadataRawData(value.as_ptr(), value.len())) } + } +} + +impl From<&Vec>> for Ref { + fn from(value: &Vec>) -> Self { + let mut pointers: Vec<*mut BNMetadata> = vec![]; + for v in value.iter() { + pointers.push(v.as_ref().handle); + } + unsafe { + Metadata::ref_from_raw(BNCreateMetadataArray(pointers.as_mut_ptr(), pointers.len())) + } + } +} + +impl From<&Array> for Ref { + fn from(value: &Array) -> Self { + let mut pointers: Vec<*mut BNMetadata> = vec![]; + for v in value.iter() { + pointers.push(v.as_ref().handle); + } + unsafe { + Metadata::ref_from_raw(BNCreateMetadataArray(pointers.as_mut_ptr(), pointers.len())) + } + } +} + +impl From>> for Ref { + fn from(value: HashMap>) -> Self { + let mut key_refs: Vec = vec![]; + let mut keys: Vec<*const c_char> = vec![]; + let mut values: Vec<*mut BNMetadata> = vec![]; + for (k, v) in value.into_iter() { + key_refs.push(k.as_bytes_with_nul()); + values.push(v.as_ref().handle); + } + for k in &key_refs { + keys.push(k.as_ref().as_ptr() as *const c_char); + } + + unsafe { + Metadata::ref_from_raw(BNCreateMetadataValueStore( + keys.as_mut_ptr(), + values.as_mut_ptr(), + keys.len(), + )) + } + } +} + +impl From<&Vec> for Ref { + fn from(value: &Vec) -> Self { + unsafe { + Metadata::ref_from_raw(BNCreateMetadataBooleanListData( + value.as_ptr() as *mut bool, + value.len(), + )) + } + } +} + +impl From<&Vec> for Ref { + fn from(value: &Vec) -> Self { + unsafe { + Metadata::ref_from_raw(BNCreateMetadataUnsignedIntegerListData( + value.as_ptr() as *mut u64, + value.len(), + )) + } + } +} + +impl From<&Vec> for Ref { + fn from(value: &Vec) -> Self { + unsafe { + Metadata::ref_from_raw(BNCreateMetadataSignedIntegerListData( + value.as_ptr() as *mut i64, + value.len(), + )) + } + } +} + +impl From<&Vec> for Ref { + fn from(value: &Vec) -> Self { + unsafe { + Metadata::ref_from_raw(BNCreateMetadataDoubleListData( + value.as_ptr() as *mut f64, + value.len(), + )) + } + } +} + +impl From> for Ref { + fn from(value: Vec) -> Self { + let mut refs = vec![]; + for v in value { + refs.push(v.as_bytes_with_nul()); + } + let mut pointers = vec![]; + for r in &refs { + pointers.push(r.as_ref().as_ptr() as *const c_char); + } + unsafe { + Metadata::ref_from_raw(BNCreateMetadataStringListData( + pointers.as_ptr() as *mut *const c_char, + pointers.len(), + )) + } + } +} + +impl PartialEq for Ref { + fn eq(&self, other: &Self) -> bool { + unsafe { BNMetadataIsEqual(self.as_ref().handle, other.as_ref().handle) } + } +} + +impl Eq for Ref {} + +impl TryFrom<&Metadata> for bool { + type Error = (); + + fn try_from(value: &Metadata) -> Result { + value.get_boolean() + } +} + +impl TryFrom<&Metadata> for u64 { + type Error = (); + + fn try_from(value: &Metadata) -> Result { + value.get_unsigned_integer() + } +} + +impl TryFrom<&Metadata> for i64 { + type Error = (); + + fn try_from(value: &Metadata) -> Result { + value.get_signed_integer() + } +} + +impl TryFrom<&Metadata> for f64 { + type Error = (); + + fn try_from(value: &Metadata) -> Result { + value.get_double() + } +} + +impl TryFrom<&Metadata> for BnString { + type Error = (); + + fn try_from(value: &Metadata) -> Result { + value.get_string() + } +} + +impl TryFrom<&Metadata> for String { + type Error = (); + + fn try_from(value: &Metadata) -> Result { + value.get_string().map(|s| s.to_string()) + } +} + +impl TryFrom<&Metadata> for Vec { + type Error = (); + + fn try_from(value: &Metadata) -> Result { + value.get_boolean_list() + } +} + +impl TryFrom<&Metadata> for Vec { + type Error = (); + + fn try_from(value: &Metadata) -> Result { + value.get_unsigned_integer_list() + } +} + +impl TryFrom<&Metadata> for Vec { + type Error = (); + + fn try_from(value: &Metadata) -> Result { + value.get_signed_integer_list() + } +} + +impl TryFrom<&Metadata> for Vec { + type Error = (); + + fn try_from(value: &Metadata) -> Result { + value.get_double_list() + } +} + +impl TryFrom<&Metadata> for Vec { + type Error = (); + + fn try_from(value: &Metadata) -> Result { + value.get_string_list() + } +} + +impl TryFrom<&Metadata> for Vec { + type Error = (); + + fn try_from(value: &Metadata) -> Result { + value + .get_string_list() + .map(|v| v.into_iter().map(|s| s.to_string()).collect()) + } +} + +impl TryFrom<&Metadata> for Vec { + type Error = (); + + fn try_from(value: &Metadata) -> Result { + value.get_raw() + } +} + +impl TryFrom<&Metadata> for Array { + type Error = (); + + fn try_from(value: &Metadata) -> Result { + value.get_array() + } +} + +impl TryFrom<&Metadata> for HashMap> { + type Error = (); + + fn try_from(value: &Metadata) -> Result { + value.get_value_store() + } +} + +impl TryFrom<&Metadata> for HashMap> { + type Error = (); + + fn try_from(value: &Metadata) -> Result { + value + .get_value_store() + .map(|m| m.into_iter().map(|(k, v)| (k.to_string(), v)).collect()) + } +} -- cgit v1.3.1