Skip to content

Commit

Permalink
implement rust BaseAddressDetection
Browse files Browse the repository at this point in the history
  • Loading branch information
rbran committed Feb 5, 2025
1 parent 88a574f commit e453004
Show file tree
Hide file tree
Showing 2 changed files with 271 additions and 0 deletions.
270 changes: 270 additions & 0 deletions rust/src/basedetection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
use binaryninjacore_sys::*;

use core::{ffi, mem, ptr};

use crate::architecture::CoreArchitecture;
use crate::binary_view::{BinaryView, BinaryViewExt};
use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner};

pub type BaseAddressDetectionPOISetting = BNBaseAddressDetectionPOISetting;
pub type BaseAddressDetectionConfidence = BNBaseAddressDetectionConfidence;
pub type BaseAddressDetectionPOIType = BNBaseAddressDetectionPOIType;

pub enum BaseAddressDetectionAnalysis {
Basic,
ControlFlow,
Full,
}

impl BaseAddressDetectionAnalysis {
pub fn as_raw(&self) -> &'static ffi::CStr {
let bytes: &[u8] = match self {
BaseAddressDetectionAnalysis::Basic => b"basic\x00",
BaseAddressDetectionAnalysis::ControlFlow => b"controlFlow\x00",
BaseAddressDetectionAnalysis::Full => b"full\x00",
};
unsafe { ffi::CStr::from_bytes_with_nul_unchecked(bytes) }
}
}

pub struct BaseAddressDetectionResult {
pub scores: Vec<BaseAddressDetectionScore>,
pub confidence: BaseAddressDetectionConfidence,
pub last_base: u64,
}

#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct BaseAddressDetectionScore {
pub score: usize,
pub base_address: u64,
}

#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct BaseAddressDetectionReason {
pub pointer: u64,
pub poi_offset: u64,
pub poi_type: BaseAddressDetectionPOIType,
}

impl CoreArrayProvider for BaseAddressDetectionReason {
type Raw = BNBaseAddressDetectionReason;
type Context = ();
type Wrapped<'a> = &'a Self;
}

unsafe impl CoreArrayProviderInner for BaseAddressDetectionReason {
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
BNFreeBaseAddressDetectionReasons(raw)
}

unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
// SAFETY BNBaseAddressDetectionReason and BaseAddressDetectionReason
// are transparent
mem::transmute::<&BNBaseAddressDetectionReason, &BaseAddressDetectionReason>(raw)
}
}

/// Build the initial analysis.
///
/// * `arch` - CPU architecture of the binary (defaults to using auto-detection)
/// * `analysis` - analysis mode
/// * `min_strlen` - minimum length of a string to be considered a point-of-interest
/// * `alignment` - byte boundary to align the base address to while brute-forcing
/// * `low_boundary` - lower boundary of the base address range to test
/// * `high_boundary` - upper boundary of the base address range to test
/// * `poi_analysis` - specifies types of points-of-interest to use for analysis
/// * `max_pointers` - maximum number of candidate pointers to collect per pointer cluster
pub struct BaseAddressDetectionBuilder {
bv: BinaryView,
arch: Option<CoreArchitecture>,
analysis: Option<BaseAddressDetectionAnalysis>,
min_strlen: Option<u32>,
alignment: Option<core::num::NonZeroU32>,
low_boundary: Option<u64>,
high_boundary: Option<u64>,
poi_analysis: Option<BaseAddressDetectionPOISetting>,
max_pointers: Option<u32>,
}

impl BaseAddressDetectionBuilder {
pub fn new(bv: BinaryView) -> Self {
BaseAddressDetectionBuilder {
bv,
arch: None,
analysis: None,
min_strlen: None,
alignment: None,
low_boundary: None,
high_boundary: None,
poi_analysis: None,
max_pointers: None,
}
}

pub fn arch(mut self, value: CoreArchitecture) -> Self {
self.arch = Some(value);
self
}

pub fn analysis(mut self, value: BaseAddressDetectionAnalysis) -> Self {
self.analysis = Some(value);
self
}

pub fn min_strlen(mut self, value: u32) -> Self {
self.min_strlen = Some(value);
self
}

pub fn alignment(mut self, value: core::num::NonZeroU32) -> Self {
self.alignment = Some(value);
self
}

pub fn low_boundary(mut self, value: u64) -> Self {
if let Some(high) = self.high_boundary {
assert!(
high >= value,
"upper boundary must be greater than lower boundary"
);
}
self.low_boundary = Some(value);
self
}

pub fn high_boundary(mut self, value: u64) -> Self {
if let Some(low) = self.low_boundary {
assert!(
low <= value,
"upper boundary must be greater than lower boundary"
);
}
self.high_boundary = Some(value);
self
}

pub fn poi_analysis(mut self, value: BaseAddressDetectionPOISetting) -> Self {
self.poi_analysis = Some(value);
self
}

pub fn max_pointers(mut self, value: u32) -> Self {
assert!(value > 2, "max pointers must be at least 2");
self.max_pointers = Some(value);
self
}

/// Initial analysis and attempts to identify candidate base addresses
///
/// .. note:: This operation can take a long time to complete depending on
/// the size and complexity of the binary and the settings used
pub fn process(self) -> Result<BaseAddressDetection, ()> {
let arch = self.arch.or_else(|| self.bv.default_arch());
let arch_name = arch.map(|a| a.name());
let arch_ptr = arch_name
.map(|a| a.as_ptr())
.unwrap_or("\x00".as_ptr() as *const ffi::c_char);

let analysis = self.analysis.unwrap_or(BaseAddressDetectionAnalysis::Full);
let min_strlen = self.min_strlen.unwrap_or(10);
let alignment = self.alignment.map(|a| a.get()).unwrap_or(1024);
let low_boundary = self.low_boundary.unwrap_or(u64::MIN);
let high_boundary = self.high_boundary.unwrap_or(u64::MAX);
let poi_analysis = self
.poi_analysis
.unwrap_or(BaseAddressDetectionPOISetting::POIAnalysisAll);
let max_pointers = self.max_pointers.unwrap_or(128);
let mut settings = BNBaseAddressDetectionSettings {
Architecture: arch_ptr,
Analysis: analysis.as_raw().as_ptr(),
MinStrlen: min_strlen,
Alignment: alignment,
LowerBoundary: low_boundary,
UpperBoundary: high_boundary,
POIAnalysis: poi_analysis,
MaxPointersPerCluster: max_pointers,
};
let base =
ptr::NonNull::new(unsafe { BNCreateBaseAddressDetection(self.bv.handle) }).unwrap();
let success = unsafe { BNDetectBaseAddress(base.as_ptr(), &mut settings) };
if success {
Ok(unsafe { BaseAddressDetection::from_raw(base) })
} else {
Err(())
}
}
}

pub struct BaseAddressDetection {
handle: ptr::NonNull<BNBaseAddressDetection>,
}

impl Drop for BaseAddressDetection {
fn drop(&mut self) {
unsafe { BNFreeBaseAddressDetection(self.as_raw()) }
}
}

impl BaseAddressDetection {
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNBaseAddressDetection>) -> Self {
Self { handle }
}

#[allow(clippy::mut_from_ref)]
pub(crate) unsafe fn as_raw(&self) -> &mut BNBaseAddressDetection {
&mut *self.handle.as_ptr()
}

/// Indicates whether or not base address detection analysis was aborted early
pub fn aborted(&self) -> bool {
unsafe { BNIsBaseAddressDetectionAborted(self.as_raw()) }
}

/// Aborts base address detection analysis
///
/// .. note:: `abort` does not stop base address detection until after
/// initial analysis has completed and it is in the base address enumeration
/// phase
pub fn abort(&self) {
unsafe { BNAbortBaseAddressDetection(self.as_raw()) }
}

/// Returns a list of reasons that can be used to determine why a base
/// address is a candidate
pub fn get_reasons(&self, base_address: u64) -> Array<BaseAddressDetectionReason> {
let mut count = 0;
let reasons =
unsafe { BNGetBaseAddressDetectionReasons(self.as_raw(), base_address, &mut count) };
unsafe { Array::new(reasons, count, ()) }
}

pub fn scores(&self, max_candidates: usize) -> BaseAddressDetectionResult {
let mut scores = vec![BNBaseAddressDetectionScore::default(); max_candidates];
let mut confidence = BNBaseAddressDetectionConfidence::NoConfidence;
let mut last_base = 0;
let num_candidates = unsafe {
BNGetBaseAddressDetectionScores(
self.as_raw(),
scores.as_mut_ptr(),
scores.len(),
&mut confidence,
&mut last_base,
)
};
scores.truncate(num_candidates);
// SAFETY BNBaseAddressDetectionScore and BaseAddressDetectionScore
// are transparent
let scores = unsafe {
mem::transmute::<Vec<BNBaseAddressDetectionScore>, Vec<BaseAddressDetectionScore>>(
scores,
)
};
BaseAddressDetectionResult {
scores,
confidence,
last_base,
}
}
}
1 change: 1 addition & 0 deletions rust/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ mod operand_iter;

pub mod architecture;
pub mod background_task;
pub mod basedetection;
pub mod basic_block;
pub mod binary_reader;
pub mod binary_view;
Expand Down

0 comments on commit e453004

Please sign in to comment.