binaryninja/binary_view/memory_map.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
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<BinaryView>,
}
impl MemoryMap {
pub fn new(view: Ref<BinaryView>) -> 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<SegmentFlags>,
) -> 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<SegmentFlags>,
) -> 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<A: Accessor>(
&mut self,
name: &str,
start: u64,
accessor: &mut FileAccessor<A>,
segment_flags: Option<SegmentFlags>,
) -> 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<SegmentFlags>,
fill: Option<u8>,
) -> 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) }
}
}