summaryrefslogtreecommitdiff
path: root/rust/src
diff options
context:
space:
mode:
authorMason Reed <mason@vector35.com>2025-02-14 23:35:40 -0500
committerMason Reed <mason@vector35.com>2025-02-24 20:08:01 -0500
commita87f4888eddf804baed4df3de3ac81d737d78ed3 (patch)
tree0fa5c8ad295e0cc5eaab2a6157e30ea1b39fe976 /rust/src
parent57d04f2f88adad219e5d526b05bfea88de4818bd (diff)
Implement Rust MemoryMap
Also split out SegmentFlags and fix some UB with the section creation
Diffstat (limited to 'rust/src')
-rw-r--r--rust/src/binary_view.rs9
-rw-r--r--rust/src/binary_view/memory_map.rs215
-rw-r--r--rust/src/section.rs70
-rw-r--r--rust/src/segment.rs167
4 files changed, 365 insertions, 96 deletions
diff --git a/rust/src/binary_view.rs b/rust/src/binary_view.rs
index 396c04ce..aa5f47f5 100644
--- a/rust/src/binary_view.rs
+++ b/rust/src/binary_view.rs
@@ -26,6 +26,7 @@ use binaryninjacore_sys::*;
use crate::architecture::{Architecture, CoreArchitecture};
use crate::base_detection::BaseAddressDetection;
use crate::basic_block::BasicBlock;
+use crate::binary_view::memory_map::MemoryMap;
use crate::component::{Component, IntoComponentGuid};
use crate::confidence::Conf;
use crate::data_buffer::DataBuffer;
@@ -64,6 +65,8 @@ use std::ptr::NonNull;
use std::{result, slice};
// TODO : general reorg of modules related to bv
+pub mod memory_map;
+
pub type Result<R> = result::Result<R, ()>;
pub type BinaryViewEventType = BNBinaryViewEventType;
pub type AnalysisState = BNAnalysisState;
@@ -870,7 +873,7 @@ pub trait BinaryViewExt: BinaryViewBase {
unsafe { BNCancelBulkAddSegments(self.as_ref().handle) }
}
- fn add_section<S: BnStrCompatible>(&self, section: SectionBuilder<S>) {
+ fn add_section(&self, section: SectionBuilder) {
section.create(self.as_ref());
}
@@ -918,6 +921,10 @@ pub trait BinaryViewExt: BinaryViewBase {
}
}
+ fn memory_map(&self) -> MemoryMap {
+ MemoryMap::new(self.as_ref().to_owned())
+ }
+
fn add_auto_function(&self, plat: &Platform, addr: u64) -> Option<Ref<Function>> {
unsafe {
let handle = BNAddFunctionForAnalysis(
diff --git a/rust/src/binary_view/memory_map.rs b/rust/src/binary_view/memory_map.rs
new file mode 100644
index 00000000..3e248c54
--- /dev/null
+++ b/rust/src/binary_view/memory_map.rs
@@ -0,0 +1,215 @@
+use crate::binary_view::BinaryView;
+use crate::data_buffer::DataBuffer;
+use crate::file_accessor::FileAccessor;
+use crate::rc::Ref;
+use crate::segment::SegmentFlags;
+use crate::string::{BnStrCompatible, BnString};
+use binaryninjacore_sys::*;
+use std::ffi::c_char;
+
+#[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::from_raw(desc_raw) }.to_string()
+ }
+
+ /// JSON string representation of the [`MemoryMap`].
+ pub fn description(&self) -> String {
+ let desc_raw = unsafe { BNGetMemoryMapDescription(self.view.handle) };
+ unsafe { BnString::from_raw(desc_raw) }.to_string()
+ }
+
+ // 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) };
+ }
+
+ pub fn add_binary_memory_region(
+ &mut self,
+ name: impl BnStrCompatible,
+ start: u64,
+ view: &BinaryView,
+ segment_flags: Option<SegmentFlags>,
+ ) -> bool {
+ let name_raw = name.into_bytes_with_nul();
+ unsafe {
+ BNAddBinaryMemoryRegion(
+ self.view.handle,
+ name_raw.as_ref().as_ptr() as *const c_char,
+ start,
+ view.handle,
+ segment_flags.unwrap_or_default().into_raw(),
+ )
+ }
+ }
+
+ pub fn add_data_memory_region(
+ &mut self,
+ name: impl BnStrCompatible,
+ start: u64,
+ data: &DataBuffer,
+ segment_flags: Option<SegmentFlags>,
+ ) -> bool {
+ let name_raw = name.into_bytes_with_nul();
+ unsafe {
+ BNAddDataMemoryRegion(
+ self.view.handle,
+ name_raw.as_ref().as_ptr() as *const c_char,
+ start,
+ data.as_raw(),
+ segment_flags.unwrap_or_default().into_raw(),
+ )
+ }
+ }
+
+ pub fn add_remote_memory_region(
+ &mut self,
+ name: impl BnStrCompatible,
+ start: u64,
+ accessor: &mut FileAccessor,
+ segment_flags: Option<SegmentFlags>,
+ ) -> bool {
+ let name_raw = name.into_bytes_with_nul();
+ unsafe {
+ BNAddRemoteMemoryRegion(
+ self.view.handle,
+ name_raw.as_ref().as_ptr() as *const c_char,
+ start,
+ &mut accessor.api_object,
+ segment_flags.unwrap_or_default().into_raw(),
+ )
+ }
+ }
+
+ pub fn remove_memory_region(&mut self, name: impl BnStrCompatible) -> bool {
+ let name_raw = name.into_bytes_with_nul();
+ unsafe {
+ BNRemoveMemoryRegion(
+ self.view.handle,
+ name_raw.as_ref().as_ptr() as *const c_char,
+ )
+ }
+ }
+
+ pub fn active_memory_region_at(&self, addr: u64) -> BnString {
+ unsafe {
+ let name_raw = BNGetActiveMemoryRegionAt(self.view.handle, addr);
+ BnString::from_raw(name_raw)
+ }
+ }
+
+ pub fn memory_region_flags(&self, name: impl BnStrCompatible) -> SegmentFlags {
+ let name_raw = name.into_bytes_with_nul();
+ let flags_raw = unsafe {
+ BNGetMemoryRegionFlags(
+ self.view.handle,
+ name_raw.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ SegmentFlags::from_raw(flags_raw)
+ }
+
+ pub fn set_memory_region_flags(
+ &mut self,
+ name: impl BnStrCompatible,
+ flags: SegmentFlags,
+ ) -> bool {
+ let name_raw = name.into_bytes_with_nul();
+ unsafe {
+ BNSetMemoryRegionFlags(
+ self.view.handle,
+ name_raw.as_ref().as_ptr() as *const c_char,
+ flags.into_raw(),
+ )
+ }
+ }
+
+ pub fn is_memory_region_enabled(&self, name: impl BnStrCompatible) -> bool {
+ let name_raw = name.into_bytes_with_nul();
+ unsafe {
+ BNIsMemoryRegionEnabled(
+ self.view.handle,
+ name_raw.as_ref().as_ptr() as *const c_char,
+ )
+ }
+ }
+
+ pub fn set_memory_region_enabled(&mut self, name: impl BnStrCompatible, enabled: bool) -> bool {
+ let name_raw = name.into_bytes_with_nul();
+ unsafe {
+ BNSetMemoryRegionEnabled(
+ self.view.handle,
+ name_raw.as_ref().as_ptr() as *const c_char,
+ enabled,
+ )
+ }
+ }
+
+ // TODO: Should we just call this is_memory_region_relocatable?
+ pub fn is_memory_region_rebaseable(&self, name: impl BnStrCompatible) -> bool {
+ let name_raw = name.into_bytes_with_nul();
+ unsafe {
+ BNIsMemoryRegionRebaseable(
+ self.view.handle,
+ name_raw.as_ref().as_ptr() as *const c_char,
+ )
+ }
+ }
+
+ pub fn set_memory_region_rebaseable(
+ &mut self,
+ name: impl BnStrCompatible,
+ enabled: bool,
+ ) -> bool {
+ let name_raw = name.into_bytes_with_nul();
+ unsafe {
+ BNSetMemoryRegionRebaseable(
+ self.view.handle,
+ name_raw.as_ref().as_ptr() as *const c_char,
+ enabled,
+ )
+ }
+ }
+
+ pub fn memory_region_fill(&self, name: impl BnStrCompatible) -> u8 {
+ let name_raw = name.into_bytes_with_nul();
+ unsafe {
+ BNGetMemoryRegionFill(
+ self.view.handle,
+ name_raw.as_ref().as_ptr() as *const c_char,
+ )
+ }
+ }
+
+ pub fn set_memory_region_fill(&mut self, name: impl BnStrCompatible, fill: u8) -> bool {
+ let name_raw = name.into_bytes_with_nul();
+ unsafe {
+ BNSetMemoryRegionFill(
+ self.view.handle,
+ name_raw.as_ref().as_ptr() as *const c_char,
+ fill,
+ )
+ }
+ }
+
+ pub fn reset(&mut self) {
+ unsafe { BNResetMemoryMap(self.view.handle) }
+ }
+}
diff --git a/rust/src/section.rs b/rust/src/section.rs
index 1a03ca36..2c63693d 100644
--- a/rust/src/section.rs
+++ b/rust/src/section.rs
@@ -14,6 +14,7 @@
//! Sections are [crate::segment::Segment]s that are loaded into memory at run time
+use std::ffi::c_char;
use std::fmt;
use std::ops::Range;
@@ -23,8 +24,9 @@ use crate::binary_view::BinaryView;
use crate::rc::*;
use crate::string::*;
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
pub enum Semantics {
+ #[default]
DefaultSection,
ReadOnlyCode,
ReadOnlyData,
@@ -82,9 +84,9 @@ impl Section {
/// # use binaryninja::section::Section;
/// # use binaryninja::binary_view::BinaryViewExt;
/// let bv = binaryninja::load("example").unwrap();
- /// bv.add_section(Section::builder("example", 0..1024).align(4).entry_size(4))
+ /// bv.add_section(Section::builder("example".to_string(), 0..1024).align(4).entry_size(4))
/// ```
- pub fn builder<S: BnStrCompatible>(name: S, range: Range<u64>) -> SectionBuilder<S> {
+ pub fn builder(name: String, range: Range<u64>) -> SectionBuilder {
SectionBuilder::new(name, range)
}
@@ -197,31 +199,32 @@ unsafe impl CoreArrayProviderInner for Section {
}
#[must_use]
-pub struct SectionBuilder<S: BnStrCompatible> {
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub struct SectionBuilder {
is_auto: bool,
- name: S,
+ name: String,
range: Range<u64>,
semantics: Semantics,
- _ty: Option<S>,
+ ty: String,
align: u64,
entry_size: u64,
- linked_section: Option<S>,
- info_section: Option<S>,
+ linked_section: String,
+ info_section: String,
info_data: u64,
}
-impl<S: BnStrCompatible> SectionBuilder<S> {
- pub fn new(name: S, range: Range<u64>) -> Self {
+impl SectionBuilder {
+ pub fn new(name: String, range: Range<u64>) -> Self {
Self {
is_auto: false,
name,
range,
semantics: Semantics::DefaultSection,
- _ty: None,
+ ty: "".to_string(),
align: 1,
entry_size: 1,
- linked_section: None,
- info_section: None,
+ linked_section: "".to_string(),
+ info_section: "".to_string(),
info_data: 0,
}
}
@@ -231,8 +234,8 @@ impl<S: BnStrCompatible> SectionBuilder<S> {
self
}
- pub fn section_type(mut self, ty: S) -> Self {
- self._ty = Some(ty);
+ pub fn section_type(mut self, ty: String) -> Self {
+ self.ty = ty;
self
}
@@ -246,13 +249,13 @@ impl<S: BnStrCompatible> SectionBuilder<S> {
self
}
- pub fn linked_section(mut self, linked_section: S) -> Self {
- self.linked_section = Some(linked_section);
+ pub fn linked_section(mut self, linked_section: String) -> Self {
+ self.linked_section = linked_section;
self
}
- pub fn info_section(mut self, info_section: S) -> Self {
- self.info_section = Some(info_section);
+ pub fn info_section(mut self, info_section: String) -> Self {
+ self.info_section = info_section;
self
}
@@ -268,47 +271,40 @@ impl<S: BnStrCompatible> SectionBuilder<S> {
pub(crate) fn create(self, view: &BinaryView) {
let name = self.name.into_bytes_with_nul();
- let ty = self._ty.map(|s| s.into_bytes_with_nul());
- let linked_section = self.linked_section.map(|s| s.into_bytes_with_nul());
- let info_section = self.info_section.map(|s| s.into_bytes_with_nul());
+ let ty = self.ty.into_bytes_with_nul();
+ let linked_section = self.linked_section.into_bytes_with_nul();
+ let info_section = self.info_section.into_bytes_with_nul();
let start = self.range.start;
let len = self.range.end.wrapping_sub(start);
unsafe {
- let nul_str = c"".as_ptr();
- let name_ptr = name.as_ref().as_ptr() as *mut _;
- let ty_ptr = ty.map_or(nul_str, |s| s.as_ref().as_ptr() as *mut _);
- let linked_section_ptr =
- linked_section.map_or(nul_str, |s| s.as_ref().as_ptr() as *mut _);
- let info_section_ptr = info_section.map_or(nul_str, |s| s.as_ref().as_ptr() as *mut _);
-
if self.is_auto {
BNAddAutoSection(
view.handle,
- name_ptr,
+ name.as_ptr() as *const c_char,
start,
len,
self.semantics.into(),
- ty_ptr,
+ ty.as_ptr() as *const c_char,
self.align,
self.entry_size,
- linked_section_ptr,
- info_section_ptr,
+ linked_section.as_ptr() as *const c_char,
+ info_section.as_ptr() as *const c_char,
self.info_data,
);
} else {
BNAddUserSection(
view.handle,
- name_ptr,
+ name.as_ptr() as *const c_char,
start,
len,
self.semantics.into(),
- ty_ptr,
+ ty.as_ptr() as *const c_char,
self.align,
self.entry_size,
- linked_section_ptr,
- info_section_ptr,
+ linked_section.as_ptr() as *const c_char,
+ info_section.as_ptr() as *const c_char,
self.info_data,
);
}
diff --git a/rust/src/segment.rs b/rust/src/segment.rs
index c6ad0c34..1ceeb181 100644
--- a/rust/src/segment.rs
+++ b/rust/src/segment.rs
@@ -22,15 +22,12 @@ use std::ops::Range;
use crate::binary_view::BinaryView;
use crate::rc::*;
-fn set_bit(val: u32, bit_mask: u32, new_val: bool) -> u32 {
- (val & !bit_mask) | if new_val { bit_mask } else { 0 }
-}
-
#[must_use]
+#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct SegmentBuilder {
ea: Range<u64>,
parent_backing: Option<Range<u64>>,
- flags: u32,
+ flags: SegmentFlags,
is_auto: bool,
}
@@ -39,7 +36,7 @@ impl SegmentBuilder {
SegmentBuilder {
ea,
parent_backing: None,
- flags: 0,
+ flags: Default::default(),
is_auto: false,
}
}
@@ -49,38 +46,8 @@ impl SegmentBuilder {
self
}
- pub fn executable(mut self, executable: bool) -> Self {
- self.flags = set_bit(self.flags, 0x01, executable);
- self
- }
-
- pub fn writable(mut self, writable: bool) -> Self {
- self.flags = set_bit(self.flags, 0x02, writable);
- self
- }
-
- pub fn readable(mut self, readable: bool) -> Self {
- self.flags = set_bit(self.flags, 0x04, readable);
- self
- }
-
- pub fn contains_data(mut self, contains_data: bool) -> Self {
- self.flags = set_bit(self.flags, 0x08, contains_data);
- self
- }
-
- pub fn contains_code(mut self, contains_code: bool) -> Self {
- self.flags = set_bit(self.flags, 0x10, contains_code);
- self
- }
-
- pub fn deny_write(mut self, deny_write: bool) -> Self {
- self.flags = set_bit(self.flags, 0x20, deny_write);
- self
- }
-
- pub fn deny_execute(mut self, deny_execute: bool) -> Self {
- self.flags = set_bit(self.flags, 0x40, deny_execute);
+ pub fn flags(mut self, flags: SegmentFlags) -> Self {
+ self.flags = flags;
self
}
@@ -98,9 +65,23 @@ impl SegmentBuilder {
unsafe {
if self.is_auto {
- BNAddAutoSegment(view.handle, ea_start, ea_len, b_start, b_len, self.flags);
+ BNAddAutoSegment(
+ view.handle,
+ ea_start,
+ ea_len,
+ b_start,
+ b_len,
+ self.flags.into_raw(),
+ );
} else {
- BNAddUserSegment(view.handle, ea_start, ea_len, b_start, b_len, self.flags);
+ BNAddUserSegment(
+ view.handle,
+ ea_start,
+ ea_len,
+ b_start,
+ b_len,
+ self.flags.into_raw(),
+ );
}
}
}
@@ -125,10 +106,11 @@ impl Segment {
/// You need to create a segment builder, customize that segment, then add it to a binary view:
///
/// ```no_run
- /// # use binaryninja::segment::Segment;
+ /// # use binaryninja::segment::{Segment, SegmentFlags};
/// # use binaryninja::binary_view::BinaryViewExt;
/// let bv = binaryninja::load("example").unwrap();
- /// bv.add_segment(Segment::builder(0..0x1000).writable(true).readable(true))
+ /// let segment_flags = SegmentFlags::new().writable(true).readable(true);
+ /// bv.add_segment(Segment::builder(0..0x1000).flags(segment_flags))
/// ```
pub fn builder(ea_range: Range<u64>) -> SegmentBuilder {
SegmentBuilder::new(ea_range)
@@ -151,36 +133,37 @@ impl Segment {
}
}
- fn flags(&self) -> u32 {
- unsafe { BNSegmentGetFlags(self.handle) }
+ pub fn flags(&self) -> SegmentFlags {
+ let raw_flags = unsafe { BNSegmentGetFlags(self.handle) };
+ SegmentFlags::from_raw(raw_flags)
}
pub fn executable(&self) -> bool {
- self.flags() & 0x01 != 0
+ self.flags().executable
}
pub fn writable(&self) -> bool {
- self.flags() & 0x02 != 0
+ self.flags().writable
}
pub fn readable(&self) -> bool {
- self.flags() & 0x04 != 0
+ self.flags().readable
}
pub fn contains_data(&self) -> bool {
- self.flags() & 0x08 != 0
+ self.flags().contains_data
}
pub fn contains_code(&self) -> bool {
- self.flags() & 0x10 != 0
+ self.flags().contains_code
}
pub fn deny_write(&self) -> bool {
- self.flags() & 0x20 != 0
+ self.flags().deny_write
}
pub fn deny_execute(&self) -> bool {
- self.flags() & 0x40 != 0
+ self.flags().deny_execute
}
pub fn auto_defined(&self) -> bool {
@@ -193,14 +176,8 @@ impl Debug for Segment {
f.debug_struct("Segment")
.field("address_range", &self.address_range())
.field("parent_backing", &self.parent_backing())
- .field("executable", &self.executable())
- .field("writable", &self.writable())
- .field("readable", &self.readable())
- .field("contains_data", &self.contains_data())
- .field("contains_code", &self.contains_code())
- .field("deny_write", &self.deny_write())
- .field("deny_execute", &self.deny_execute())
.field("auto_defined", &self.auto_defined())
+ .field("flags", &self.flags())
.finish()
}
}
@@ -240,3 +217,77 @@ unsafe impl CoreArrayProviderInner for Segment {
Guard::new(Segment::from_raw(*raw), context)
}
}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
+pub struct SegmentFlags {
+ pub executable: bool,
+ pub writable: bool,
+ pub readable: bool,
+ pub contains_data: bool,
+ pub contains_code: bool,
+ pub deny_write: bool,
+ pub deny_execute: bool,
+}
+
+impl SegmentFlags {
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ pub fn executable(mut self, executable: bool) -> Self {
+ self.executable = executable;
+ self
+ }
+
+ pub fn writable(mut self, writable: bool) -> Self {
+ self.writable = writable;
+ self
+ }
+
+ pub fn readable(mut self, readable: bool) -> Self {
+ self.readable = readable;
+ self
+ }
+
+ pub fn contains_data(mut self, contains_data: bool) -> Self {
+ self.contains_data = contains_data;
+ self
+ }
+
+ pub fn contains_code(mut self, contains_code: bool) -> Self {
+ self.contains_code = contains_code;
+ self
+ }
+
+ pub fn deny_write(mut self, deny_write: bool) -> Self {
+ self.deny_write = deny_write;
+ self
+ }
+
+ pub fn deny_execute(mut self, deny_execute: bool) -> Self {
+ self.deny_execute = deny_execute;
+ self
+ }
+
+ pub(crate) fn from_raw(flags: u32) -> Self {
+ Self {
+ executable: flags & 0x01 != 0,
+ writable: flags & 0x02 != 0,
+ readable: flags & 0x04 != 0,
+ contains_data: flags & 0x08 != 0,
+ contains_code: flags & 0x10 != 0,
+ deny_write: flags & 0x20 != 0,
+ deny_execute: flags & 0x40 != 0,
+ }
+ }
+
+ pub(crate) fn into_raw(&self) -> u32 {
+ (self.executable as u32)
+ | (self.writable as u32) << 1
+ | (self.readable as u32) << 2
+ | (self.contains_data as u32) << 3
+ | (self.contains_code as u32) << 4
+ | (self.deny_write as u32) << 5
+ | (self.deny_execute as u32) << 6
+ }
+}