diff options
| -rw-r--r-- | binaryninjaapi.h | 6 | ||||
| -rw-r--r-- | binaryninjacore.h | 9 | ||||
| -rw-r--r-- | function.cpp | 36 | ||||
| -rw-r--r-- | python/function.py | 63 | ||||
| -rw-r--r-- | rust/src/function.rs | 43 | ||||
| -rw-r--r-- | rust/tests/function.rs | 69 |
6 files changed, 225 insertions, 1 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 95c54095..0d2bdfe2 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -11458,6 +11458,12 @@ namespace BinaryNinja { void CollapseRegion(uint64_t hash); void ExpandRegion(uint64_t hash); void ExpandAll(); + + void StoreMetadata(const std::string& key, Ref<Metadata> value, bool isAuto = false); + Ref<Metadata> QueryMetadata(const std::string& key); + Ref<Metadata> GetMetadata(); + Ref<Metadata> GetAutoMetadata(); + void RemoveMetadata(const std::string& key); }; /*! diff --git a/binaryninjacore.h b/binaryninjacore.h index 1cd87b37..e5dfba4f 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -37,7 +37,7 @@ // 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 105 +#define BN_CURRENT_CORE_ABI_VERSION 106 // 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 @@ -5011,6 +5011,13 @@ extern "C" BINARYNINJACOREAPI void BNFunctionCollapseRegion(BNFunction* func, uint64_t hash); BINARYNINJACOREAPI void BNFunctionExpandRegion(BNFunction* func, uint64_t hash); + BINARYNINJACOREAPI void BNFunctionStoreMetadata( + BNFunction* func, const char* key, BNMetadata* value, bool isAuto); + BINARYNINJACOREAPI BNMetadata* BNFunctionQueryMetadata(BNFunction* func, const char* key); + BINARYNINJACOREAPI void BNFunctionRemoveMetadata(BNFunction* func, const char* key); + BINARYNINJACOREAPI BNMetadata* BNFunctionGetMetadata(BNFunction* func); + BINARYNINJACOREAPI BNMetadata* BNFunctionGetAutoMetadata(BNFunction* func); + BINARYNINJACOREAPI void BNSetAutoCallTypeAdjustment( BNFunction* func, BNArchitecture* arch, uint64_t addr, BNTypeWithConfidence* type); BINARYNINJACOREAPI void BNSetUserCallTypeAdjustment( diff --git a/function.cpp b/function.cpp index 07f685a5..cc9a8e13 100644 --- a/function.cpp +++ b/function.cpp @@ -3369,6 +3369,42 @@ void Function::ExpandAll() BNFunctionExpandAll(m_object); } + +void Function::StoreMetadata(const std::string& key, Ref<Metadata> value, bool isAuto) +{ + if (!value) + return; + BNFunctionStoreMetadata(m_object, key.c_str(), value->GetObject(), isAuto); +} + + +Ref<Metadata> Function::QueryMetadata(const std::string& key) +{ + BNMetadata* value = BNFunctionQueryMetadata(m_object, key.c_str()); + if (!value) + return nullptr; + return new Metadata(value); +} + + +Ref<Metadata> Function::GetMetadata() +{ + return new Metadata(BNFunctionGetMetadata(m_object)); +} + + +Ref<Metadata> Function::GetAutoMetadata() +{ + return new Metadata(BNFunctionGetAutoMetadata(m_object)); +} + + +void Function::RemoveMetadata(const std::string& key) +{ + BNFunctionRemoveMetadata(m_object, key.c_str()); +} + + AdvancedFunctionAnalysisDataRequestor::AdvancedFunctionAnalysisDataRequestor(Function* func) : m_func(func) { if (m_func) diff --git a/python/function.py b/python/function.py index b101a44f..2616cba9 100644 --- a/python/function.py +++ b/python/function.py @@ -47,6 +47,7 @@ from . import callingconvention from . import workflow from . import languagerepresentation from . import deprecation +from . import metadata from . import __version__ # we define the following as such so the linter doesn't confuse 'highlight' the module with the @@ -3390,6 +3391,68 @@ class Function: """ return core.BNFunctionIsRegionCollapsed(self.handle, hash) + def store_metadata(self, key: str, md: metadata.MetadataValueType, isAuto: bool = False) -> None: + """ + `store_metadata` stores an object for the given key in the current Function. Objects stored using + `store_metadata` can be retrieved when the database is reopened unless isAuto is set to True. + + :param str key: key value to associate the Metadata object with + :param Varies md: object to store + :param bool isAuto: whether the metadata is an auto metadata + :rtype: None + """ + _md = md + if not isinstance(_md, metadata.Metadata): + _md = metadata.Metadata(_md) + core.BNFunctionStoreMetadata(self.handle, key, _md.handle, isAuto) + + def query_metadata(self, key: str) -> 'metadata.MetadataValueType': + """ + `query_metadata` retrieves metadata associated with the given key stored in the current function. + + :param str key: key to query + :rtype: metadata associated with the key + """ + md_handle = core.BNFunctionQueryMetadata(self.handle, key) + if md_handle is None: + raise KeyError(key) + return metadata.Metadata(handle=md_handle).value + + def remove_metadata(self, key: str) -> None: + """ + `remove_metadata` removes the metadata associated with key from the current function. + + :param str key: key associated with metadata to remove from the function + :rtype: None + """ + core.BNFunctionRemoveMetadata(self.handle, key) + + @property + def metadata(self) -> Dict[str, 'metadata.MetadataValueType']: + """ + `metadata` retrieves the metadata associated with the current function. + + :rtype: metadata associated with the function + """ + md_handle = core.BNFunctionGetMetadata(self.handle) + assert md_handle is not None, "core.BNFunctionGetMetadata returned None" + value = metadata.Metadata(handle=md_handle).value + assert isinstance(value, dict), "core.BNFunctionGetMetadata did not return a dict" + return value + + @property + def auto_metadata(self) -> Dict[str, 'metadata.MetadataValueType']: + """ + `metadata` retrieves the metadata associated with the current function. + + :rtype: metadata associated with the function + """ + md_handle = core.BNFunctionGetAutoMetadata(self.handle) + assert md_handle is not None, "core.BNFunctionGetAutoMetadata returned None" + value = metadata.Metadata(handle=md_handle).value + assert isinstance(value, dict), "core.BNFunctionGetAutoMetadata did not return a dict" + return value + class AdvancedFunctionAnalysisDataRequestor: def __init__(self, func: Optional['Function'] = None): diff --git a/rust/src/function.rs b/rust/src/function.rs index 60c8970c..89ade8f8 100644 --- a/rust/src/function.rs +++ b/rust/src/function.rs @@ -43,6 +43,7 @@ use crate::high_level_il::HighLevelILFunction; use crate::language_representation::CoreLanguageRepresentationFunction; use crate::low_level_il::LowLevelILRegularFunction; use crate::medium_level_il::MediumLevelILFunction; +use crate::metadata::Metadata; use crate::variable::{ IndirectBranchInfo, MergedVariable, NamedVariableWithType, RegisterValue, RegisterValueType, StackVariableReference, Variable, @@ -2428,6 +2429,48 @@ impl Function { assert!(!result.is_null()); unsafe { Array::new(result, count, ()) } } + + pub fn query_metadata(&self, key: &str) -> Option<Ref<Metadata>> { + let key = key.to_cstr(); + let value: *mut BNMetadata = unsafe { BNFunctionQueryMetadata(self.handle, key.as_ptr()) }; + if value.is_null() { + None + } else { + Some(unsafe { Metadata::ref_from_raw(value) }) + } + } + + pub fn get_metadata(&self) -> Option<Ref<Metadata>> { + let value: *mut BNMetadata = unsafe { BNFunctionGetMetadata(self.handle) }; + if value.is_null() { + None + } else { + Some(unsafe { Metadata::ref_from_raw(value) }) + } + } + + pub fn get_auto_metadata(&self) -> Option<Ref<Metadata>> { + let value: *mut BNMetadata = unsafe { BNFunctionGetAutoMetadata(self.handle) }; + if value.is_null() { + None + } else { + Some(unsafe { Metadata::ref_from_raw(value) }) + } + } + + pub fn store_metadata<V>(&self, key: &str, value: V, is_auto: bool) + where + V: Into<Ref<Metadata>>, + { + let md = value.into(); + let key = key.to_cstr(); + unsafe { BNFunctionStoreMetadata(self.handle, key.as_ptr(), md.as_ref().handle, is_auto) }; + } + + pub fn remove_metadata(&self, key: &str) { + let key = key.to_cstr(); + unsafe { BNFunctionRemoveMetadata(self.handle, key.as_ptr()) }; + } } impl Debug for Function { diff --git a/rust/tests/function.rs b/rust/tests/function.rs new file mode 100644 index 00000000..455ba752 --- /dev/null +++ b/rust/tests/function.rs @@ -0,0 +1,69 @@ +use binaryninja::binary_view::BinaryViewExt; +use binaryninja::headless::Session; +use binaryninja::metadata::Metadata; +use binaryninja::rc::Ref; +use std::path::PathBuf; + +#[test] +fn store_and_query_function_metadata() { + let _session = Session::new().expect("Failed to initialize session"); + let out_dir = env!("OUT_DIR").parse::<PathBuf>().unwrap(); + let view = binaryninja::load(out_dir.join("atox.obj")).expect("Failed to create view"); + let func = view + .entry_point_function() + .expect("Failed to get entry point function"); + + // Store key/value pairs to user and auto metadata + func.store_metadata("one", "one", false); + func.store_metadata("two", 2 as u64, true); + func.store_metadata("three", "three", true); + func.remove_metadata("three"); + + // Assert that we can query from both user and auto metadata + assert_eq!( + func.query_metadata("one") + .expect("Failed to query key \"one\"") + .get_string() + .unwrap() + .to_string_lossy(), + "one" + ); + assert_eq!( + func.query_metadata("two") + .expect("Failed to query key \"two\"") + .get_unsigned_integer() + .unwrap(), + 2 + ); + assert!( + func.query_metadata("three") == None, + "Query for key \"three\" returned a value" + ); + + // Assert that user metadata only includes key/values from user data (not auto) and vice-versa + let user_metadata = func.get_metadata().expect("Failed to query user metadata"); + assert_eq!( + user_metadata + .get("one") + .expect("Failed to query key \"one\" from user metadata") + .expect("User metadata ref is None") + .get_string() + .unwrap() + .to_string_lossy(), + "one" + ); + assert_eq!(user_metadata.get("two"), Ok(None)); + let auto_metadata = func + .get_auto_metadata() + .expect("Failed to query auto metadata"); + assert_eq!( + auto_metadata + .get("two") + .expect("Failed to query key \"two\" from auto metadata") + .expect("Auto metadata ref is None") + .get_unsigned_integer() + .unwrap(), + 2 + ); + assert_eq!(auto_metadata.get("one"), Ok(None)); +} |
