binaryninja/binary_view/
memory_map.rs

1use crate::binary_view::BinaryView;
2use crate::data_buffer::DataBuffer;
3use crate::file_accessor::{Accessor, FileAccessor};
4use crate::rc::Ref;
5use crate::segment::SegmentFlags;
6use crate::string::{BnString, IntoCStr};
7use binaryninjacore_sys::*;
8
9/// MemoryMap provides access to the system-level memory map describing how a BinaryView is loaded into memory.
10///
11/// # Architecture Note
12///
13/// This Rust `MemoryMap` struct is a proxy that accesses the BinaryView's current MemoryMap state through
14/// the FFI boundary. The proxy provides a simple mutable interface: when you call modification operations
15/// (add_memory_region, remove_memory_region, etc.), the proxy automatically accesses the updated MemoryMap.
16/// Internally, the core uses immutable copy-on-write data structures, but the proxy abstracts this away.
17///
18/// When you access a BinaryView's MemoryMap, you always see the current state. For lock-free access during
19/// analysis, AnalysisContext provides memory layout query methods (is_valid_offset, is_offset_readable, get_start,
20/// get_length, etc.) that operate on an immutable snapshot of the MemoryMap cached when the analysis was initiated.
21///
22/// A MemoryMap can contain multiple, arbitrarily overlapping memory regions. When modified, address space
23/// segmentation is automatically managed. If multiple regions overlap, the most recently added region takes
24/// precedence by default.
25#[derive(PartialEq, Eq, Hash)]
26pub struct MemoryMap {
27    view: Ref<BinaryView>,
28}
29
30impl MemoryMap {
31    pub fn new(view: Ref<BinaryView>) -> Self {
32        Self { view }
33    }
34
35    // TODO: There does not seem to be a way to enumerate memory regions.
36
37    /// JSON string representation of the base [`MemoryMap`], consisting of unresolved auto and user segments.
38    pub fn base_description(&self) -> String {
39        let desc_raw = unsafe { BNGetBaseMemoryMapDescription(self.view.handle) };
40        unsafe { BnString::into_string(desc_raw) }
41    }
42
43    /// JSON string representation of the [`MemoryMap`].
44    pub fn description(&self) -> String {
45        let desc_raw = unsafe { BNGetMemoryMapDescription(self.view.handle) };
46        unsafe { BnString::into_string(desc_raw) }
47    }
48
49    // When enabled, the memory map will present a simplified, logical view that merges and abstracts virtual memory
50    // regions based on criteria such as contiguity and flag consistency. This view is designed to provide a higher-level
51    // representation for user analysis, hiding underlying mapping details.
52    //
53    // When disabled, the memory map will revert to displaying the virtual view, which corresponds directly to the individual
54    // segments mapped from the raw file without any merging or abstraction.
55    pub fn set_logical_enabled(&mut self, enabled: bool) {
56        unsafe { BNSetLogicalMemoryMapEnabled(self.view.handle, enabled) };
57    }
58
59    /// Whether the memory map is activated for the associated view.
60    ///
61    /// Returns `true` if this MemoryMap represents a parsed BinaryView with real segments
62    /// (ELF, PE, Mach-O, etc.). Returns `false` for Raw BinaryViews or views that failed
63    /// to parse segments.
64    ///
65    /// This is determined by whether the BinaryView has a parent view - parsed views have a
66    /// parent Raw view, while Raw views have no parent.
67    ///
68    /// Use this to gate features that require parsed binary structure (sections, imports,
69    /// relocations, etc.). For basic analysis queries (start, length, is_offset_readable, etc.),
70    /// use the MemoryMap directly regardless of activation state - all BinaryViews have a
71    /// usable MemoryMap.
72    pub fn is_activated(&self) -> bool {
73        unsafe { BNIsMemoryMapActivated(self.view.handle) }
74    }
75
76    pub fn add_binary_memory_region(
77        &mut self,
78        name: &str,
79        start: u64,
80        view: &BinaryView,
81        segment_flags: Option<SegmentFlags>,
82    ) -> bool {
83        let name_raw = name.to_cstr();
84        unsafe {
85            BNAddBinaryMemoryRegion(
86                self.view.handle,
87                name_raw.as_ptr(),
88                start,
89                view.handle,
90                segment_flags.unwrap_or_default().into_raw(),
91            )
92        }
93    }
94
95    /// Adds the memory region using a [`DataBuffer`].
96    ///
97    /// This will add the contents of the [`DataBuffer`] to the database.
98    pub fn add_data_memory_region(
99        &mut self,
100        name: &str,
101        start: u64,
102        data: &DataBuffer,
103        segment_flags: Option<SegmentFlags>,
104    ) -> bool {
105        let name_raw = name.to_cstr();
106        unsafe {
107            BNAddDataMemoryRegion(
108                self.view.handle,
109                name_raw.as_ptr(),
110                start,
111                data.as_raw(),
112                segment_flags.unwrap_or_default().into_raw(),
113            )
114        }
115    }
116
117    // TODO: This really cant be safe until BNFileAccessor is ARC'd and can be freed. Probably need another thing
118    // TODO: Ontop of a file accessor in the core that would manage it. (I.e. BNFileAccessorHandle) or something.
119    /// Adds the memory region using a [`FileAccessor`].
120    ///
121    /// This does not add the region contents to the database, instead accesses to the contents
122    /// are done "remotely" to a [`FileAccessor`].
123    ///
124    /// NOTE: The [`FileAccessor`] MUST live as long as the region is available, currently there is no gurentee by
125    /// the type checker that the file accessor is tied to that of the memory region.
126    pub fn add_remote_memory_region<A: Accessor>(
127        &mut self,
128        name: &str,
129        start: u64,
130        accessor: &mut FileAccessor<A>,
131        segment_flags: Option<SegmentFlags>,
132    ) -> bool {
133        let name_raw = name.to_cstr();
134        unsafe {
135            BNAddRemoteMemoryRegion(
136                self.view.handle,
137                name_raw.as_ptr(),
138                start,
139                &mut accessor.raw,
140                segment_flags.unwrap_or_default().into_raw(),
141            )
142        }
143    }
144
145    /// Adds an unbacked memory region with a given length and fill byte.
146    pub fn add_unbacked_memory_region(
147        &mut self,
148        name: &str,
149        start: u64,
150        length: u64,
151        segment_flags: Option<SegmentFlags>,
152        fill: Option<u8>,
153    ) -> bool {
154        let name_raw = name.to_cstr();
155        unsafe {
156            BNAddUnbackedMemoryRegion(
157                self.view.handle,
158                name_raw.as_ptr(),
159                start,
160                length,
161                segment_flags.unwrap_or_default().into_raw(),
162                fill.unwrap_or_default(),
163            )
164        }
165    }
166
167    pub fn remove_memory_region(&mut self, name: &str) -> bool {
168        let name_raw = name.to_cstr();
169        unsafe { BNRemoveMemoryRegion(self.view.handle, name_raw.as_ptr()) }
170    }
171
172    pub fn active_memory_region_at(&self, addr: u64) -> String {
173        unsafe {
174            let name_raw = BNGetActiveMemoryRegionAt(self.view.handle, addr);
175            BnString::into_string(name_raw)
176        }
177    }
178
179    pub fn memory_region_flags(&self, name: &str) -> SegmentFlags {
180        let name_raw = name.to_cstr();
181        let flags_raw = unsafe { BNGetMemoryRegionFlags(self.view.handle, name_raw.as_ptr()) };
182        SegmentFlags::from_raw(flags_raw)
183    }
184
185    pub fn set_memory_region_flags(&mut self, name: &str, flags: SegmentFlags) -> bool {
186        let name_raw = name.to_cstr();
187        unsafe { BNSetMemoryRegionFlags(self.view.handle, name_raw.as_ptr(), flags.into_raw()) }
188    }
189
190    pub fn is_memory_region_enabled(&self, name: &str) -> bool {
191        let name_raw = name.to_cstr();
192        unsafe { BNIsMemoryRegionEnabled(self.view.handle, name_raw.as_ptr()) }
193    }
194
195    pub fn set_memory_region_enabled(&mut self, name: &str, enabled: bool) -> bool {
196        let name_raw = name.to_cstr();
197        unsafe { BNSetMemoryRegionEnabled(self.view.handle, name_raw.as_ptr(), enabled) }
198    }
199
200    // TODO: Should we just call this is_memory_region_relocatable?
201    pub fn is_memory_region_rebaseable(&self, name: &str) -> bool {
202        let name_raw = name.to_cstr();
203        unsafe { BNIsMemoryRegionRebaseable(self.view.handle, name_raw.as_ptr()) }
204    }
205
206    pub fn set_memory_region_rebaseable(&mut self, name: &str, enabled: bool) -> bool {
207        let name_raw = name.to_cstr();
208        unsafe { BNSetMemoryRegionRebaseable(self.view.handle, name_raw.as_ptr(), enabled) }
209    }
210
211    pub fn memory_region_fill(&self, name: &str) -> u8 {
212        let name_raw = name.to_cstr();
213        unsafe { BNGetMemoryRegionFill(self.view.handle, name_raw.as_ptr()) }
214    }
215
216    pub fn set_memory_region_fill(&mut self, name: &str, fill: u8) -> bool {
217        let name_raw = name.to_cstr();
218        unsafe { BNSetMemoryRegionFill(self.view.handle, name_raw.as_ptr(), fill) }
219    }
220
221    pub fn reset(&mut self) {
222        unsafe { BNResetMemoryMap(self.view.handle) }
223    }
224}