binaryninja/binary_view/
memory_map.rs1use 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}