diff options
| -rw-r--r-- | binaryninjaapi.h | 76 | ||||
| -rw-r--r-- | binaryninjacore.h | 26 | ||||
| -rw-r--r-- | python/binaryview.py | 34 | ||||
| -rw-r--r-- | python/workflow.py | 198 | ||||
| -rw-r--r-- | rust/src/binary_view/memory_map.rs | 28 | ||||
| -rw-r--r-- | rust/src/workflow.rs | 147 | ||||
| -rw-r--r-- | workflow.cpp | 154 |
7 files changed, 659 insertions, 4 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h index fa8ec3e4..ea5c9feb 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -8129,6 +8129,22 @@ namespace BinaryNinja { std::optional<std::pair<std::string, BNStringType>> StringifyUnicodeData(Architecture* arch, const DataBuffer& buffer, bool nullTerminates = true, bool allowShortStrings = false); }; + /*! MemoryMap provides access to the system-level memory map describing how a BinaryView is loaded into memory. + + \note Architecture: This API-side MemoryMap class is a proxy that accesses the BinaryView's current + MemoryMap state through the core API. The proxy provides a simple mutable interface: when you call + modification operations (AddMemoryRegion, RemoveMemoryRegion, etc.), the proxy automatically accesses + the updated MemoryMap. Internally, the core uses immutable copy-on-write data structures, but the proxy + abstracts this away. + + When you access a BinaryView's MemoryMap, you always see the current state. For lock-free access during + analysis, AnalysisContext provides memory layout query methods (IsValidOffset, IsOffsetReadable, GetStart, + GetLength, etc.) that operate on an immutable snapshot of the MemoryMap cached when the analysis was initiated. + + A MemoryMap can contain multiple, arbitrarily overlapping memory regions. When modified, address space + segmentation is automatically managed. If multiple regions overlap, the most recently added region takes + precedence by default. + */ class MemoryMap { BNBinaryView* m_object; @@ -8142,6 +8158,22 @@ namespace BinaryNinja { BNSetLogicalMemoryMapEnabled(m_object, enabled); } + /*! Returns true if this MemoryMap represents a parsed BinaryView with real segments. + + This is determined by whether the BinaryView has a parent view - parsed views + (ELF, PE, Mach-O, etc.) have a parent Raw view, while Raw views have no parent. + + Returns true for parsed BinaryViews (ELF, PE, Mach-O, etc.) with segments from + binary format parsing. Returns false for Raw BinaryViews (flat file view with + synthetic MemoryMap) or views that failed to parse segments. + + Use this to gate features that require parsed binary structure (sections, imports, + relocations, etc.). For basic analysis queries (GetStart, IsOffsetReadable, + GetLength, etc.), use the MemoryMap directly regardless of activation state - all + BinaryViews have a usable MemoryMap. + + \return True if this is an activated (parsed) memory map, false otherwise + */ bool IsActivated() { return BNIsMemoryMapActivated(m_object); @@ -11420,8 +11452,52 @@ namespace BinaryNinja { request.Accept(writer); return Inform(buffer.GetString()); } + + // Settings cache access - lock-free access to cached settings + /*! Get a setting value from the cached settings + + \code{.cpp} + bool enabled = analysisContext->GetSetting<bool>("analysis.conservative"); + \endcode + + \tparam T type for the value you are retrieving + \param key Key for the setting + \return Value for the setting, with type T + */ + template <typename T> + T GetSetting(const std::string& key); + + // Memory map access - lock-free access to cached MemoryMap + bool IsValidOffset(uint64_t offset); + bool IsOffsetReadable(uint64_t offset); + bool IsOffsetWritable(uint64_t offset); + bool IsOffsetExecutable(uint64_t offset); + bool IsOffsetBackedByFile(uint64_t offset); + uint64_t GetStart(); + uint64_t GetEnd(); + uint64_t GetLength(); + uint64_t GetNextValidOffset(uint64_t offset); + uint64_t GetNextMappedAddress(uint64_t addr, uint32_t flags = 0); + uint64_t GetNextBackedAddress(uint64_t addr, uint32_t flags = 0); + Ref<Segment> GetSegmentAt(uint64_t addr); + std::vector<BNAddressRange> GetMappedAddressRanges(); + std::vector<BNAddressRange> GetBackedAddressRanges(); }; + // Explicit template specialization declarations for AnalysisContext::GetSetting<T> + template <> + bool AnalysisContext::GetSetting<bool>(const std::string& key); + template <> + double AnalysisContext::GetSetting<double>(const std::string& key); + template <> + int64_t AnalysisContext::GetSetting<int64_t>(const std::string& key); + template <> + uint64_t AnalysisContext::GetSetting<uint64_t>(const std::string& key); + template <> + std::string AnalysisContext::GetSetting<std::string>(const std::string& key); + template <> + std::vector<std::string> AnalysisContext::GetSetting<std::vector<std::string>>(const std::string& key); + /*! \ingroup workflow */ diff --git a/binaryninjacore.h b/binaryninjacore.h index b0248d52..763dce0e 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 147 +#define BN_CURRENT_CORE_ABI_VERSION 148 // 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 @@ -5930,6 +5930,30 @@ extern "C" BNAnalysisContext* analysisContext, BNHighLevelILFunction* highLevelIL); BINARYNINJACOREAPI bool BNAnalysisContextInform(BNAnalysisContext* analysisContext, const char* request); + // Settings cache access + BINARYNINJACOREAPI bool BNAnalysisContextGetSettingBool(BNAnalysisContext* analysisContext, const char* key); + BINARYNINJACOREAPI double BNAnalysisContextGetSettingDouble(BNAnalysisContext* analysisContext, const char* key); + BINARYNINJACOREAPI int64_t BNAnalysisContextGetSettingInt64(BNAnalysisContext* analysisContext, const char* key); + BINARYNINJACOREAPI uint64_t BNAnalysisContextGetSettingUInt64(BNAnalysisContext* analysisContext, const char* key); + BINARYNINJACOREAPI char* BNAnalysisContextGetSettingString(BNAnalysisContext* analysisContext, const char* key); + BINARYNINJACOREAPI char** BNAnalysisContextGetSettingStringList(BNAnalysisContext* analysisContext, const char* key, size_t* count); + + // Memory map access + BINARYNINJACOREAPI bool BNAnalysisContextIsValidOffset(BNAnalysisContext* analysisContext, uint64_t offset); + BINARYNINJACOREAPI bool BNAnalysisContextIsOffsetReadable(BNAnalysisContext* analysisContext, uint64_t offset); + BINARYNINJACOREAPI bool BNAnalysisContextIsOffsetWritable(BNAnalysisContext* analysisContext, uint64_t offset); + BINARYNINJACOREAPI bool BNAnalysisContextIsOffsetExecutable(BNAnalysisContext* analysisContext, uint64_t offset); + BINARYNINJACOREAPI bool BNAnalysisContextIsOffsetBackedByFile(BNAnalysisContext* analysisContext, uint64_t offset); + BINARYNINJACOREAPI uint64_t BNAnalysisContextGetStart(BNAnalysisContext* analysisContext); + BINARYNINJACOREAPI uint64_t BNAnalysisContextGetEnd(BNAnalysisContext* analysisContext); + BINARYNINJACOREAPI uint64_t BNAnalysisContextGetLength(BNAnalysisContext* analysisContext); + BINARYNINJACOREAPI uint64_t BNAnalysisContextGetNextValidOffset(BNAnalysisContext* analysisContext, uint64_t offset); + BINARYNINJACOREAPI uint64_t BNAnalysisContextGetNextMappedAddress(BNAnalysisContext* analysisContext, uint64_t addr, uint32_t flags); + BINARYNINJACOREAPI uint64_t BNAnalysisContextGetNextBackedAddress(BNAnalysisContext* analysisContext, uint64_t addr, uint32_t flags); + BINARYNINJACOREAPI BNSegment* BNAnalysisContextGetSegmentAt(BNAnalysisContext* analysisContext, uint64_t addr); + BINARYNINJACOREAPI BNAddressRange* BNAnalysisContextGetMappedAddressRanges(BNAnalysisContext* analysisContext, size_t* count); + BINARYNINJACOREAPI BNAddressRange* BNAnalysisContextGetBackedAddressRanges(BNAnalysisContext* analysisContext, size_t* count); + // Activity BINARYNINJACOREAPI BNActivity* BNCreateActivity(const char* configuration, void* ctxt, void (*action)(void*, BNAnalysisContext*)); BINARYNINJACOREAPI BNActivity* BNCreateActivityWithEligibility(const char* configuration, void* ctxt, void (*action)(void*, BNAnalysisContext*), bool (*eligibilityHandler)(void*, BNActivity*, BNAnalysisContext*)); diff --git a/python/binaryview.py b/python/binaryview.py index d0d20405..4170302b 100644 --- a/python/binaryview.py +++ b/python/binaryview.py @@ -2457,8 +2457,19 @@ class AdvancedILFunctionList: class MemoryMap: r""" - The MemoryMap object describes a system-level memory map into which a BinaryView is loaded. Each BinaryView - exposes its portion of the MemoryMap through the Segments defined within that view. + The MemoryMap object provides access to the system-level memory map describing how a BinaryView is loaded + into memory. Each BinaryView exposes its portion of the MemoryMap through the Segments defined within that view. + + **Architecture Note:** This Python MemoryMap object is a proxy that accesses the BinaryView's current + MemoryMap state through the FFI boundary. The proxy provides a simple mutable interface: when you call + modification operations (``add_memory_region``, ``remove_memory_region``, etc.), the proxy automatically + accesses the updated MemoryMap. Internally, the core uses immutable copy-on-write data structures, but + the proxy abstracts this away. + + When you access ``view.memory_map``, you always see the current state. For lock-free access during analysis, + AnalysisContext provides memory layout query methods (``is_valid_offset()``, ``is_offset_readable()``, + ``get_start()``, ``get_length()``, etc.) that operate on an immutable snapshot of the MemoryMap cached when + the analysis was initiated. A MemoryMap can contain multiple, arbitrarily overlapping memory regions. When modified, address space segmentation is automatically managed. If multiple regions overlap, the most recently added region takes @@ -2615,7 +2626,24 @@ class MemoryMap: @property def is_activated(self): - """Whether the memory map is activated for the associated view.""" + """ + Whether the memory map is activated for the associated view. + + Returns ``True`` if this MemoryMap represents a parsed BinaryView with real segments + (ELF, PE, Mach-O, etc.). Returns ``False`` for Raw BinaryViews or views that failed + to parse segments. + + This is determined by whether the BinaryView has a parent view - parsed views have a + parent Raw view, while Raw views have no parent. + + Use this to gate features that require parsed binary structure (sections, imports, + relocations, etc.). For basic analysis queries (start, length, is_offset_readable, etc.), + use the MemoryMap directly regardless of activation state - all BinaryViews have a + usable MemoryMap. + + :return: True if this is an activated (parsed) memory map, False otherwise + :rtype: bool + """ return core.BNIsMemoryMapActivated(self.handle) def add_memory_region(self, name: str, start: int, source: Optional[Union['os.PathLike', str, bytes, bytearray, 'BinaryView', 'databuffer.DataBuffer', 'fileaccessor.FileAccessor']] = None, flags: SegmentFlag = 0, fill: int = 0, length: Optional[int] = None) -> bool: diff --git a/python/workflow.py b/python/workflow.py index 6f3b2dd8..ed6bc876 100644 --- a/python/workflow.py +++ b/python/workflow.py @@ -188,6 +188,204 @@ class AnalysisContext: def inform(self, request: str) -> bool: return core.BNAnalysisContextInform(self.handle, request) + def get_setting_bool(self, key: str) -> bool: + """ + Get a boolean setting from the cached settings. + + :param key: Setting key + :return: Boolean setting value + """ + return core.BNAnalysisContextGetSettingBool(self.handle, key) + + def get_setting_double(self, key: str) -> float: + """ + Get a double setting from the cached settings. + + :param key: Setting key + :return: Double setting value + """ + return core.BNAnalysisContextGetSettingDouble(self.handle, key) + + def get_setting_int64(self, key: str) -> int: + """ + Get a 64-bit signed integer setting from the cached settings. + + :param key: Setting key + :return: Int64 setting value + """ + return core.BNAnalysisContextGetSettingInt64(self.handle, key) + + def get_setting_uint64(self, key: str) -> int: + """ + Get a 64-bit unsigned integer setting from the cached settings. + + :param key: Setting key + :return: UInt64 setting value + """ + return core.BNAnalysisContextGetSettingUInt64(self.handle, key) + + def get_setting_string(self, key: str) -> str: + """ + Get a string setting from the cached settings. + + :param key: Setting key + :return: String setting value + """ + return core.BNAnalysisContextGetSettingString(self.handle, key) + + def get_setting_string_list(self, key: str) -> List[str]: + """ + Get a string list setting from the cached settings. + + :param key: Setting key + :return: List of strings + """ + count = ctypes.c_size_t() + result = core.BNAnalysisContextGetSettingStringList(self.handle, key, ctypes.byref(count)) + out_list = [] + for i in range(count.value): + out_list.append(result[i].decode('utf-8')) + core.BNFreeStringList(result, count.value) + return out_list + + def is_valid_offset(self, offset: int) -> bool: + """ + Check if an offset is mapped in the cached memory map. + + :param offset: Offset to check + :return: True if offset is mapped + """ + return core.BNAnalysisContextIsValidOffset(self.handle, offset) + + def is_offset_readable(self, offset: int) -> bool: + """ + Check if an offset is readable in the cached memory map. + + :param offset: Offset to check + :return: True if offset is readable + """ + return core.BNAnalysisContextIsOffsetReadable(self.handle, offset) + + def is_offset_writable(self, offset: int) -> bool: + """ + Check if an offset is writable in the cached memory map. + + :param offset: Offset to check + :return: True if offset is writable + """ + return core.BNAnalysisContextIsOffsetWritable(self.handle, offset) + + def is_offset_executable(self, offset: int) -> bool: + """ + Check if an offset is executable in the cached memory map. + + :param offset: Offset to check + :return: True if offset is executable + """ + return core.BNAnalysisContextIsOffsetExecutable(self.handle, offset) + + def is_offset_backed_by_file(self, offset: int) -> bool: + """ + Check if an offset is backed by the file in the cached memory map. + + :param offset: Offset to check + :return: True if offset is backed by file + """ + return core.BNAnalysisContextIsOffsetBackedByFile(self.handle, offset) + + def get_start(self) -> int: + """ + Get the start address from the cached memory map. + + :return: Start address + """ + return core.BNAnalysisContextGetStart(self.handle) + + def get_end(self) -> int: + """ + Get the end address from the cached memory map. + + :return: End address + """ + return core.BNAnalysisContextGetEnd(self.handle) + + def get_length(self) -> int: + """ + Get the length of the cached memory map. + + :return: Length + """ + return core.BNAnalysisContextGetLength(self.handle) + + def get_next_valid_offset(self, offset: int) -> int: + """ + Get the next valid offset after the given offset from the cached memory map. + + :param offset: Starting offset + :return: Next valid offset + """ + return core.BNAnalysisContextGetNextValidOffset(self.handle, offset) + + def get_next_mapped_address(self, addr: int, flags: int = 0) -> int: + """ + Get the next mapped address after the given address from the cached memory map. + + :param addr: Starting address + :param flags: Optional flags to filter by + :return: Next mapped address + """ + return core.BNAnalysisContextGetNextMappedAddress(self.handle, addr, flags) + + def get_next_backed_address(self, addr: int, flags: int = 0) -> int: + """ + Get the next backed address after the given address from the cached memory map. + + :param addr: Starting address + :param flags: Optional flags to filter by + :return: Next backed address + """ + return core.BNAnalysisContextGetNextBackedAddress(self.handle, addr, flags) + + def get_segment_at(self, addr: int) -> Optional['binaryview.Segment']: + """ + Get the segment containing the given address from the cached memory map. + + :param addr: Address to query + :return: Segment containing the address, or None + """ + segment = core.BNAnalysisContextGetSegmentAt(self.handle, addr) + if not segment: + return None + return binaryview.Segment(segment) + + def get_mapped_address_ranges(self) -> List[tuple]: + """ + Get all mapped address ranges from the cached memory map. + + :return: List of (start, end) tuples + """ + count = ctypes.c_size_t() + ranges = core.BNAnalysisContextGetMappedAddressRanges(self.handle, ctypes.byref(count)) + result = [] + for i in range(count.value): + result.append((ranges[i].start, ranges[i].end)) + core.BNFreeAddressRanges(ranges) + return result + + def get_backed_address_ranges(self) -> List[tuple]: + """ + Get all backed address ranges from the cached memory map. + + :return: List of (start, end) tuples + """ + count = ctypes.c_size_t() + ranges = core.BNAnalysisContextGetBackedAddressRanges(self.handle, ctypes.byref(count)) + result = [] + for i in range(count.value): + result.append((ranges[i].start, ranges[i].end)) + core.BNFreeAddressRanges(ranges) + return result + class Activity(object): """ diff --git a/rust/src/binary_view/memory_map.rs b/rust/src/binary_view/memory_map.rs index 914c3aee..bb7902b9 100644 --- a/rust/src/binary_view/memory_map.rs +++ b/rust/src/binary_view/memory_map.rs @@ -6,6 +6,22 @@ use crate::segment::SegmentFlags; use crate::string::{BnString, IntoCStr}; use binaryninjacore_sys::*; +/// MemoryMap provides access to the system-level memory map describing how a BinaryView is loaded into memory. +/// +/// # Architecture Note +/// +/// This Rust `MemoryMap` struct is a proxy that accesses the BinaryView's current MemoryMap state through +/// the FFI boundary. The proxy provides a simple mutable interface: when you call modification operations +/// (add_memory_region, remove_memory_region, etc.), the proxy automatically accesses the updated MemoryMap. +/// Internally, the core uses immutable copy-on-write data structures, but the proxy abstracts this away. +/// +/// When you access a BinaryView's MemoryMap, you always see the current state. For lock-free access during +/// analysis, AnalysisContext provides memory layout query methods (is_valid_offset, is_offset_readable, get_start, +/// get_length, etc.) that operate on an immutable snapshot of the MemoryMap cached when the analysis was initiated. +/// +/// A MemoryMap can contain multiple, arbitrarily overlapping memory regions. When modified, address space +/// segmentation is automatically managed. If multiple regions overlap, the most recently added region takes +/// precedence by default. #[derive(PartialEq, Eq, Hash)] pub struct MemoryMap { view: Ref<BinaryView>, @@ -41,6 +57,18 @@ impl MemoryMap { } /// Whether the memory map is activated for the associated view. + /// + /// Returns `true` if this MemoryMap represents a parsed BinaryView with real segments + /// (ELF, PE, Mach-O, etc.). Returns `false` for Raw BinaryViews or views that failed + /// to parse segments. + /// + /// This is determined by whether the BinaryView has a parent view - parsed views have a + /// parent Raw view, while Raw views have no parent. + /// + /// Use this to gate features that require parsed binary structure (sections, imports, + /// relocations, etc.). For basic analysis queries (start, length, is_offset_readable, etc.), + /// use the MemoryMap directly regardless of activation state - all BinaryViews have a + /// usable MemoryMap. pub fn is_activated(&self) -> bool { unsafe { BNIsMemoryMapActivated(self.view.handle) } } diff --git a/rust/src/workflow.rs b/rust/src/workflow.rs index 49c5760e..618a290c 100644 --- a/rust/src/workflow.rs +++ b/rust/src/workflow.rs @@ -8,6 +8,7 @@ use crate::high_level_il::HighLevelILFunction; use crate::low_level_il::{LowLevelILMutableFunction, LowLevelILRegularFunction}; use crate::medium_level_il::MediumLevelILFunction; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; +use crate::segment::Segment; use crate::string::{BnString, IntoCStr}; use std::ffi::c_char; use std::ptr; @@ -125,6 +126,152 @@ impl AnalysisContext { blocks.iter().map(|block| block.handle).collect(); unsafe { BNSetBasicBlockList(self.handle.as_ptr(), blocks_raw.as_mut_ptr(), blocks.len()) } } + + // Settings cache access - lock-free access to cached settings + + /// Get a boolean setting from the cached settings + pub fn get_setting_bool(&self, key: &str) -> bool { + let key = key.to_cstr(); + unsafe { BNAnalysisContextGetSettingBool(self.handle.as_ptr(), key.as_ptr()) } + } + + /// Get a double setting from the cached settings + pub fn get_setting_double(&self, key: &str) -> f64 { + let key = key.to_cstr(); + unsafe { BNAnalysisContextGetSettingDouble(self.handle.as_ptr(), key.as_ptr()) } + } + + /// Get a signed 64-bit integer setting from the cached settings + pub fn get_setting_int64(&self, key: &str) -> i64 { + let key = key.to_cstr(); + unsafe { BNAnalysisContextGetSettingInt64(self.handle.as_ptr(), key.as_ptr()) } + } + + /// Get an unsigned 64-bit integer setting from the cached settings + pub fn get_setting_uint64(&self, key: &str) -> u64 { + let key = key.to_cstr(); + unsafe { BNAnalysisContextGetSettingUInt64(self.handle.as_ptr(), key.as_ptr()) } + } + + /// Get a string setting from the cached settings + pub fn get_setting_string(&self, key: &str) -> BnString { + let key = key.to_cstr(); + unsafe { + let result = BNAnalysisContextGetSettingString(self.handle.as_ptr(), key.as_ptr()); + BnString::from_raw(result) + } + } + + /// Get a string list setting from the cached settings + pub fn get_setting_string_list(&self, key: &str) -> Array<BnString> { + let key = key.to_cstr(); + unsafe { + let mut count = 0; + let result = BNAnalysisContextGetSettingStringList(self.handle.as_ptr(), key.as_ptr(), &mut count); + Array::new(result, count, ()) + } + } + + // Memory map access - lock-free access to cached MemoryMap + + /// Check if an offset is mapped in the cached memory map + pub fn is_valid_offset(&self, offset: u64) -> bool { + unsafe { BNAnalysisContextIsValidOffset(self.handle.as_ptr(), offset) } + } + + /// Check if an offset is readable in the cached memory map + pub fn is_offset_readable(&self, offset: u64) -> bool { + unsafe { BNAnalysisContextIsOffsetReadable(self.handle.as_ptr(), offset) } + } + + /// Check if an offset is writable in the cached memory map + pub fn is_offset_writable(&self, offset: u64) -> bool { + unsafe { BNAnalysisContextIsOffsetWritable(self.handle.as_ptr(), offset) } + } + + /// Check if an offset is executable in the cached memory map + pub fn is_offset_executable(&self, offset: u64) -> bool { + unsafe { BNAnalysisContextIsOffsetExecutable(self.handle.as_ptr(), offset) } + } + + /// Check if an offset is backed by file in the cached memory map + pub fn is_offset_backed_by_file(&self, offset: u64) -> bool { + unsafe { BNAnalysisContextIsOffsetBackedByFile(self.handle.as_ptr(), offset) } + } + + /// Get the start address from the cached memory map + pub fn get_start(&self) -> u64 { + unsafe { BNAnalysisContextGetStart(self.handle.as_ptr()) } + } + + /// Get the end address from the cached memory map + pub fn get_end(&self) -> u64 { + unsafe { BNAnalysisContextGetEnd(self.handle.as_ptr()) } + } + + /// Get the length of the cached memory map + pub fn get_length(&self) -> u64 { + unsafe { BNAnalysisContextGetLength(self.handle.as_ptr()) } + } + + /// Get the next valid offset after the given offset from the cached memory map + pub fn get_next_valid_offset(&self, offset: u64) -> u64 { + unsafe { BNAnalysisContextGetNextValidOffset(self.handle.as_ptr(), offset) } + } + + /// Get the next mapped address after the given address from the cached memory map + pub fn get_next_mapped_address(&self, addr: u64, flags: u32) -> u64 { + unsafe { BNAnalysisContextGetNextMappedAddress(self.handle.as_ptr(), addr, flags) } + } + + /// Get the next backed address after the given address from the cached memory map + pub fn get_next_backed_address(&self, addr: u64, flags: u32) -> u64 { + unsafe { BNAnalysisContextGetNextBackedAddress(self.handle.as_ptr(), addr, flags) } + } + + /// Get the segment containing the given address from the cached memory map + pub fn get_segment_at(&self, addr: u64) -> Option<Ref<Segment>> { + unsafe { + let result = BNAnalysisContextGetSegmentAt(self.handle.as_ptr(), addr); + if result.is_null() { + None + } else { + Some(Segment::ref_from_raw(result)) + } + } + } + + /// Get all mapped address ranges from the cached memory map + pub fn get_mapped_address_ranges(&self) -> Vec<(u64, u64)> { + unsafe { + let mut count = 0; + let ranges = BNAnalysisContextGetMappedAddressRanges(self.handle.as_ptr(), &mut count); + let result = (0..count) + .map(|i| { + let range = *ranges.add(i); + (range.start, range.end) + }) + .collect(); + BNFreeAddressRanges(ranges); + result + } + } + + /// Get all backed address ranges from the cached memory map + pub fn get_backed_address_ranges(&self) -> Vec<(u64, u64)> { + unsafe { + let mut count = 0; + let ranges = BNAnalysisContextGetBackedAddressRanges(self.handle.as_ptr(), &mut count); + let result = (0..count) + .map(|i| { + let range = *ranges.add(i); + (range.start, range.end) + }) + .collect(); + BNFreeAddressRanges(ranges); + result + } + } } impl ToOwned for AnalysisContext { diff --git a/workflow.cpp b/workflow.cpp index 3b0366db..d4981c1a 100644 --- a/workflow.cpp +++ b/workflow.cpp @@ -152,6 +152,160 @@ bool AnalysisContext::Inform(const string& request) } +// Template specializations for GetSetting<T> +template<> +bool AnalysisContext::GetSetting<bool>(const string& key) +{ + return BNAnalysisContextGetSettingBool(m_object, key.c_str()); +} + + +template<> +double AnalysisContext::GetSetting<double>(const string& key) +{ + return BNAnalysisContextGetSettingDouble(m_object, key.c_str()); +} + + +template<> +int64_t AnalysisContext::GetSetting<int64_t>(const string& key) +{ + return BNAnalysisContextGetSettingInt64(m_object, key.c_str()); +} + + +template<> +uint64_t AnalysisContext::GetSetting<uint64_t>(const string& key) +{ + return BNAnalysisContextGetSettingUInt64(m_object, key.c_str()); +} + + +template<> +string AnalysisContext::GetSetting<string>(const string& key) +{ + char* str = BNAnalysisContextGetSettingString(m_object, key.c_str()); + string result = str; + BNFreeString(str); + return result; +} + + +template<> +vector<string> AnalysisContext::GetSetting<vector<string>>(const string& key) +{ + size_t count = 0; + char** list = BNAnalysisContextGetSettingStringList(m_object, key.c_str(), &count); + vector<string> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + result.push_back(list[i]); + BNFreeStringList(list, count); + return result; +} + + +bool AnalysisContext::IsValidOffset(uint64_t offset) +{ + return BNAnalysisContextIsValidOffset(m_object, offset); +} + + +bool AnalysisContext::IsOffsetReadable(uint64_t offset) +{ + return BNAnalysisContextIsOffsetReadable(m_object, offset); +} + + +bool AnalysisContext::IsOffsetWritable(uint64_t offset) +{ + return BNAnalysisContextIsOffsetWritable(m_object, offset); +} + + +bool AnalysisContext::IsOffsetExecutable(uint64_t offset) +{ + return BNAnalysisContextIsOffsetExecutable(m_object, offset); +} + + +bool AnalysisContext::IsOffsetBackedByFile(uint64_t offset) +{ + return BNAnalysisContextIsOffsetBackedByFile(m_object, offset); +} + + +uint64_t AnalysisContext::GetStart() +{ + return BNAnalysisContextGetStart(m_object); +} + + +uint64_t AnalysisContext::GetEnd() +{ + return BNAnalysisContextGetEnd(m_object); +} + + +uint64_t AnalysisContext::GetLength() +{ + return BNAnalysisContextGetLength(m_object); +} + + +uint64_t AnalysisContext::GetNextValidOffset(uint64_t offset) +{ + return BNAnalysisContextGetNextValidOffset(m_object, offset); +} + + +uint64_t AnalysisContext::GetNextMappedAddress(uint64_t addr, uint32_t flags) +{ + return BNAnalysisContextGetNextMappedAddress(m_object, addr, flags); +} + + +uint64_t AnalysisContext::GetNextBackedAddress(uint64_t addr, uint32_t flags) +{ + return BNAnalysisContextGetNextBackedAddress(m_object, addr, flags); +} + + +Ref<Segment> AnalysisContext::GetSegmentAt(uint64_t addr) +{ + BNSegment* segment = BNAnalysisContextGetSegmentAt(m_object, addr); + if (!segment) + return nullptr; + return new Segment(segment); +} + + +vector<BNAddressRange> AnalysisContext::GetMappedAddressRanges() +{ + size_t count = 0; + BNAddressRange* ranges = BNAnalysisContextGetMappedAddressRanges(m_object, &count); + vector<BNAddressRange> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + result.push_back(ranges[i]); + BNFreeAddressRanges(ranges); + return result; +} + + +vector<BNAddressRange> AnalysisContext::GetBackedAddressRanges() +{ + size_t count = 0; + BNAddressRange* ranges = BNAnalysisContextGetBackedAddressRanges(m_object, &count); + vector<BNAddressRange> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + result.push_back(ranges[i]); + BNFreeAddressRanges(ranges); + return result; +} + + bool WorkflowMachine::PostRequest(const std::string& command) { rapidjson::Document request(rapidjson::kObjectType); |
