From a87f4888eddf804baed4df3de3ac81d737d78ed3 Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Fri, 14 Feb 2025 23:35:40 -0500 Subject: Implement Rust MemoryMap Also split out SegmentFlags and fix some UB with the section creation --- rust/src/binary_view.rs | 9 +- rust/src/binary_view/memory_map.rs | 215 +++++++++++++++++++++++++++++++++++++ rust/src/section.rs | 70 ++++++------ rust/src/segment.rs | 167 ++++++++++++++++++---------- 4 files changed, 365 insertions(+), 96 deletions(-) create mode 100644 rust/src/binary_view/memory_map.rs (limited to 'rust/src') 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 = result::Result; 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(&self, section: SectionBuilder) { + 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> { 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, +} + +impl MemoryMap { + pub fn new(view: Ref) -> 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, + ) -> 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, + ) -> 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, + ) -> 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(name: S, range: Range) -> SectionBuilder { + pub fn builder(name: String, range: Range) -> SectionBuilder { SectionBuilder::new(name, range) } @@ -197,31 +199,32 @@ unsafe impl CoreArrayProviderInner for Section { } #[must_use] -pub struct SectionBuilder { +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct SectionBuilder { is_auto: bool, - name: S, + name: String, range: Range, semantics: Semantics, - _ty: Option, + ty: String, align: u64, entry_size: u64, - linked_section: Option, - info_section: Option, + linked_section: String, + info_section: String, info_data: u64, } -impl SectionBuilder { - pub fn new(name: S, range: Range) -> Self { +impl SectionBuilder { + pub fn new(name: String, range: Range) -> 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 SectionBuilder { 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 SectionBuilder { 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 SectionBuilder { 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, parent_backing: Option>, - 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) -> 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 + } +} -- cgit v1.3.1