use crate::binary_view::BinaryView; use crate::data_buffer::DataBuffer; use crate::file_accessor::{Accessor, FileAccessor}; use crate::rc::Ref; 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, } impl MemoryMap { pub fn new(view: Ref) -> Self { Self { view } } // TODO: There does not seem to be a way to enumerate memory regions. /// JSON string representation of the base [`MemoryMap`], consisting of unresolved auto and user segments. pub fn base_description(&self) -> String { let desc_raw = unsafe { BNGetBaseMemoryMapDescription(self.view.handle) }; unsafe { BnString::into_string(desc_raw) } } /// JSON string representation of the [`MemoryMap`]. pub fn description(&self) -> String { let desc_raw = unsafe { BNGetMemoryMapDescription(self.view.handle) }; unsafe { BnString::into_string(desc_raw) } } // When enabled, the memory map will present a simplified, logical view that merges and abstracts virtual memory // regions based on criteria such as contiguity and flag consistency. This view is designed to provide a higher-level // representation for user analysis, hiding underlying mapping details. // // When disabled, the memory map will revert to displaying the virtual view, which corresponds directly to the individual // segments mapped from the raw file without any merging or abstraction. pub fn set_logical_enabled(&mut self, enabled: bool) { unsafe { BNSetLogicalMemoryMapEnabled(self.view.handle, enabled) }; } /// 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) } } pub fn add_binary_memory_region( &mut self, name: &str, start: u64, view: &BinaryView, segment_flags: Option, ) -> bool { let name_raw = name.to_cstr(); unsafe { BNAddBinaryMemoryRegion( self.view.handle, name_raw.as_ptr(), start, view.handle, segment_flags.unwrap_or_default().into_raw(), ) } } /// Adds the memory region using a [`DataBuffer`]. /// /// This will add the contents of the [`DataBuffer`] to the database. pub fn add_data_memory_region( &mut self, name: &str, start: u64, data: &DataBuffer, segment_flags: Option, ) -> bool { let name_raw = name.to_cstr(); unsafe { BNAddDataMemoryRegion( self.view.handle, name_raw.as_ptr(), start, data.as_raw(), segment_flags.unwrap_or_default().into_raw(), ) } } // TODO: This really cant be safe until BNFileAccessor is ARC'd and can be freed. Probably need another thing // TODO: Ontop of a file accessor in the core that would manage it. (I.e. BNFileAccessorHandle) or something. /// Adds the memory region using a [`FileAccessor`]. /// /// This does not add the region contents to the database, instead accesses to the contents /// are done "remotely" to a [`FileAccessor`]. /// /// NOTE: The [`FileAccessor`] MUST live as long as the region is available, currently there is no gurentee by /// the type checker that the file accessor is tied to that of the memory region. pub fn add_remote_memory_region( &mut self, name: &str, start: u64, accessor: &mut FileAccessor, segment_flags: Option, ) -> bool { let name_raw = name.to_cstr(); unsafe { BNAddRemoteMemoryRegion( self.view.handle, name_raw.as_ptr(), start, &mut accessor.raw, segment_flags.unwrap_or_default().into_raw(), ) } } /// Adds an unbacked memory region with a given length and fill byte. pub fn add_unbacked_memory_region( &mut self, name: &str, start: u64, length: u64, segment_flags: Option, fill: Option, ) -> bool { let name_raw = name.to_cstr(); unsafe { BNAddUnbackedMemoryRegion( self.view.handle, name_raw.as_ptr(), start, length, segment_flags.unwrap_or_default().into_raw(), fill.unwrap_or_default(), ) } } pub fn remove_memory_region(&mut self, name: &str) -> bool { let name_raw = name.to_cstr(); unsafe { BNRemoveMemoryRegion(self.view.handle, name_raw.as_ptr()) } } pub fn active_memory_region_at(&self, addr: u64) -> String { unsafe { let name_raw = BNGetActiveMemoryRegionAt(self.view.handle, addr); BnString::into_string(name_raw) } } pub fn memory_region_flags(&self, name: &str) -> SegmentFlags { let name_raw = name.to_cstr(); let flags_raw = unsafe { BNGetMemoryRegionFlags(self.view.handle, name_raw.as_ptr()) }; SegmentFlags::from_raw(flags_raw) } pub fn set_memory_region_flags(&mut self, name: &str, flags: SegmentFlags) -> bool { let name_raw = name.to_cstr(); unsafe { BNSetMemoryRegionFlags(self.view.handle, name_raw.as_ptr(), flags.into_raw()) } } pub fn is_memory_region_enabled(&self, name: &str) -> bool { let name_raw = name.to_cstr(); unsafe { BNIsMemoryRegionEnabled(self.view.handle, name_raw.as_ptr()) } } pub fn set_memory_region_enabled(&mut self, name: &str, enabled: bool) -> bool { let name_raw = name.to_cstr(); unsafe { BNSetMemoryRegionEnabled(self.view.handle, name_raw.as_ptr(), enabled) } } // TODO: Should we just call this is_memory_region_relocatable? pub fn is_memory_region_rebaseable(&self, name: &str) -> bool { let name_raw = name.to_cstr(); unsafe { BNIsMemoryRegionRebaseable(self.view.handle, name_raw.as_ptr()) } } pub fn set_memory_region_rebaseable(&mut self, name: &str, enabled: bool) -> bool { let name_raw = name.to_cstr(); unsafe { BNSetMemoryRegionRebaseable(self.view.handle, name_raw.as_ptr(), enabled) } } pub fn memory_region_fill(&self, name: &str) -> u8 { let name_raw = name.to_cstr(); unsafe { BNGetMemoryRegionFill(self.view.handle, name_raw.as_ptr()) } } pub fn set_memory_region_fill(&mut self, name: &str, fill: u8) -> bool { let name_raw = name.to_cstr(); unsafe { BNSetMemoryRegionFill(self.view.handle, name_raw.as_ptr(), fill) } } pub fn reset(&mut self) { unsafe { BNResetMemoryMap(self.view.handle) } } }