diff options
| author | Brian Potchik <brian@vector35.com> | 2026-03-11 12:12:36 -0400 |
|---|---|---|
| committer | Brian Potchik <brian@vector35.com> | 2026-03-11 12:12:36 -0400 |
| commit | 6b57ef1d2c82d263655364588546e6211b0a99a8 (patch) | |
| tree | 83593089f530368b6d83905fc32858f8251904dc /rust/src | |
| parent | b8fdf800de345f93b2e68713d14bac425a62feb3 (diff) | |
Enhance MemoryMap bindings and add support to re-enable disabled regions in the UI.
Diffstat (limited to 'rust/src')
| -rw-r--r-- | rust/src/binary_view.rs | 2 | ||||
| -rw-r--r-- | rust/src/binary_view/memory_map.rs | 205 |
2 files changed, 203 insertions, 4 deletions
diff --git a/rust/src/binary_view.rs b/rust/src/binary_view.rs index 93819098..bd26dad3 100644 --- a/rust/src/binary_view.rs +++ b/rust/src/binary_view.rs @@ -68,7 +68,7 @@ pub mod reader; pub mod search; pub mod writer; -pub use memory_map::MemoryMap; +pub use memory_map::{MemoryMap, MemoryRegionInfo, ResolvedRange}; pub use reader::BinaryReader; pub use writer::BinaryWriter; diff --git a/rust/src/binary_view/memory_map.rs b/rust/src/binary_view/memory_map.rs index bb7902b9..44bb58c6 100644 --- a/rust/src/binary_view/memory_map.rs +++ b/rust/src/binary_view/memory_map.rs @@ -3,10 +3,101 @@ 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 crate::string::{raw_to_string, BnString, IntoCStr}; use binaryninjacore_sys::*; -/// MemoryMap provides access to the system-level memory map describing how a BinaryView is loaded into memory. +/// Snapshot of a memory region's properties at the time of query. +/// +/// This is a value type — modifying the memory map will not update existing +/// `MemoryRegionInfo` instances. To mutate a region, use the corresponding +/// [`MemoryMap`] methods. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct MemoryRegionInfo { + pub name: String, + pub display_name: String, + pub start: u64, + pub length: u64, + pub flags: SegmentFlags, + pub enabled: bool, + pub rebaseable: bool, + pub fill: u8, + pub has_target: bool, + pub absolute_address_mode: bool, + pub local: bool, +} + +impl MemoryRegionInfo { + pub fn end(&self) -> u64 { + self.start + self.length + } + + fn from_raw(region: &BNMemoryRegionInfo) -> Self { + Self { + name: raw_to_string(region.name).unwrap_or_default(), + display_name: raw_to_string(region.displayName).unwrap_or_default(), + start: region.start, + length: region.length, + flags: SegmentFlags::from_raw(region.flags), + enabled: region.enabled, + rebaseable: region.rebaseable, + fill: region.fill, + has_target: region.hasTarget, + absolute_address_mode: region.absoluteAddressMode, + local: region.local, + } + } +} + +/// A resolved, non-overlapping address range in the memory map. +/// +/// Each range contains an ordered list of memory regions that overlap at this +/// interval. The first region is the active (highest priority) one. +/// +/// This is a snapshot value — it is not updated by later mutations to the +/// memory map. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ResolvedRange { + pub start: u64, + pub length: u64, + pub regions: Vec<MemoryRegionInfo>, +} + +impl ResolvedRange { + pub fn end(&self) -> u64 { + self.start + self.length + } + + /// The highest-priority region at this range. + pub fn active_region(&self) -> Option<&MemoryRegionInfo> { + self.regions.first() + } + + /// Name of the active region, or `None` if empty. + pub fn name(&self) -> Option<&str> { + self.active_region().map(|r| r.name.as_str()) + } + + /// Flags of the active (highest-priority) region. + pub fn flags(&self) -> SegmentFlags { + self.active_region() + .map(|r| r.flags) + .unwrap_or(SegmentFlags::from_raw(0)) + } +} + +/// Live proxy to the memory map of a [`BinaryView`]. +/// +/// A `MemoryMap` describes how a [`BinaryView`] is loaded into memory. It +/// contains *regions* — raw, possibly overlapping memory definitions — and +/// exposes *resolved ranges* — a computed, disjoint view of the address space +/// produced by splitting overlapping regions. The most recently added region +/// takes precedence when regions overlap. Mutation is always by region name. +/// +/// - [`regions()`](Self::regions) returns configured memory regions, including +/// disabled ones. +/// - [`ranges()`](Self::ranges) returns resolved, non-overlapping address +/// ranges — the computed active view. +/// - Both return snapshot values that are not updated after later mutations. /// /// # Architecture Note /// @@ -22,6 +113,13 @@ use binaryninjacore_sys::*; /// 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. +/// +/// All MemoryMap APIs support undo and redo operations. During BinaryView::Init, these APIs should be used +/// conditionally: +/// +/// * Initial load: Use the MemoryMap APIs to define the memory regions that compose the system. +/// * Database load: Do not use the MemoryMap APIs, as the regions are already persisted and will be restored +/// automatically. #[derive(PartialEq, Eq, Hash)] pub struct MemoryMap { view: Ref<BinaryView>, @@ -32,7 +130,106 @@ impl MemoryMap { Self { view } } - // TODO: There does not seem to be a way to enumerate memory regions. + /// Returns a snapshot of all configured memory regions, including disabled ones. + pub fn regions(&self) -> Vec<MemoryRegionInfo> { + let mut count: usize = 0; + let regions_raw = unsafe { BNGetMemoryRegions(self.view.handle, &mut count) }; + if regions_raw.is_null() { + return Vec::new(); + } + let mut result = Vec::with_capacity(count); + for i in 0..count { + let region = unsafe { &*regions_raw.add(i) }; + result.push(MemoryRegionInfo::from_raw(region)); + } + unsafe { BNFreeMemoryRegions(regions_raw, count) }; + result + } + + /// Returns a snapshot of the resolved, non-overlapping address ranges. + /// + /// Each range contains an ordered list of memory regions, with the first + /// being the active (highest priority) region at that interval. + pub fn ranges(&self) -> Vec<ResolvedRange> { + let mut count: usize = 0; + let ranges_raw = unsafe { BNGetResolvedMemoryRanges(self.view.handle, &mut count) }; + if ranges_raw.is_null() { + return Vec::new(); + } + let mut result = Vec::with_capacity(count); + for i in 0..count { + let range = unsafe { &*ranges_raw.add(i) }; + let mut regions = Vec::with_capacity(range.regionCount); + for j in 0..range.regionCount { + let region = unsafe { &*range.regions.add(j) }; + regions.push(MemoryRegionInfo::from_raw(region)); + } + result.push(ResolvedRange { + start: range.start, + length: range.length, + regions, + }); + } + unsafe { BNFreeResolvedMemoryRanges(ranges_raw, count) }; + result + } + + /// Look up a configured memory region by name. + /// + /// Returns a snapshot of the region's properties, or `None` if no region + /// with the given name exists. + pub fn get_region(&self, name: &str) -> Option<MemoryRegionInfo> { + let name_raw = name.to_cstr(); + let mut result: BNMemoryRegionInfo = unsafe { std::mem::zeroed() }; + let found = unsafe { + BNGetMemoryRegionInfo(self.view.handle, name_raw.as_ptr(), &mut result) + }; + if !found { + return None; + } + let info = MemoryRegionInfo::from_raw(&result); + unsafe { BNFreeMemoryRegionInfo(&mut result) }; + Some(info) + } + + /// Return the active region snapshot covering `addr`, or `None` if no + /// enabled region covers the address. + pub fn get_active_region_at(&self, addr: u64) -> Option<MemoryRegionInfo> { + let mut result: BNMemoryRegionInfo = unsafe { std::mem::zeroed() }; + let found = unsafe { + BNGetActiveMemoryRegionInfoAt(self.view.handle, addr, &mut result) + }; + if !found { + return None; + } + let info = MemoryRegionInfo::from_raw(&result); + unsafe { BNFreeMemoryRegionInfo(&mut result) }; + Some(info) + } + + /// Return the resolved range snapshot covering `addr`, or `None` if no + /// range covers the address. + pub fn get_resolved_range_at(&self, addr: u64) -> Option<ResolvedRange> { + let mut result: BNResolvedMemoryRange = unsafe { std::mem::zeroed() }; + let found = unsafe { + BNGetResolvedMemoryRangeAt(self.view.handle, addr, &mut result) + }; + if !found { + return None; + } + let mut regions = Vec::with_capacity(result.regionCount); + for j in 0..result.regionCount { + let region = unsafe { &*result.regions.add(j) }; + regions.push(MemoryRegionInfo::from_raw(region)); + } + let resolved = ResolvedRange { + start: result.start, + length: result.length, + regions, + }; + unsafe { BNFreeResolvedMemoryRange(&mut result) }; + Some(resolved) + } /// JSON string representation of the base [`MemoryMap`], consisting of unresolved auto and user segments. pub fn base_description(&self) -> String { @@ -169,6 +366,8 @@ impl MemoryMap { unsafe { BNRemoveMemoryRegion(self.view.handle, name_raw.as_ptr()) } } + /// Return the name of the active region at `addr`, or an empty string if + /// no region covers the address. pub fn active_memory_region_at(&self, addr: u64) -> String { unsafe { let name_raw = BNGetActiveMemoryRegionAt(self.view.handle, addr); |
