Skip to content

Commit e453004

Browse files
committed
implement rust BaseAddressDetection
1 parent 88a574f commit e453004

File tree

2 files changed

+271
-0
lines changed

2 files changed

+271
-0
lines changed

rust/src/basedetection.rs

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
use binaryninjacore_sys::*;
2+
3+
use core::{ffi, mem, ptr};
4+
5+
use crate::architecture::CoreArchitecture;
6+
use crate::binary_view::{BinaryView, BinaryViewExt};
7+
use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner};
8+
9+
pub type BaseAddressDetectionPOISetting = BNBaseAddressDetectionPOISetting;
10+
pub type BaseAddressDetectionConfidence = BNBaseAddressDetectionConfidence;
11+
pub type BaseAddressDetectionPOIType = BNBaseAddressDetectionPOIType;
12+
13+
pub enum BaseAddressDetectionAnalysis {
14+
Basic,
15+
ControlFlow,
16+
Full,
17+
}
18+
19+
impl BaseAddressDetectionAnalysis {
20+
pub fn as_raw(&self) -> &'static ffi::CStr {
21+
let bytes: &[u8] = match self {
22+
BaseAddressDetectionAnalysis::Basic => b"basic\x00",
23+
BaseAddressDetectionAnalysis::ControlFlow => b"controlFlow\x00",
24+
BaseAddressDetectionAnalysis::Full => b"full\x00",
25+
};
26+
unsafe { ffi::CStr::from_bytes_with_nul_unchecked(bytes) }
27+
}
28+
}
29+
30+
pub struct BaseAddressDetectionResult {
31+
pub scores: Vec<BaseAddressDetectionScore>,
32+
pub confidence: BaseAddressDetectionConfidence,
33+
pub last_base: u64,
34+
}
35+
36+
#[repr(C)]
37+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
38+
pub struct BaseAddressDetectionScore {
39+
pub score: usize,
40+
pub base_address: u64,
41+
}
42+
43+
#[repr(C)]
44+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
45+
pub struct BaseAddressDetectionReason {
46+
pub pointer: u64,
47+
pub poi_offset: u64,
48+
pub poi_type: BaseAddressDetectionPOIType,
49+
}
50+
51+
impl CoreArrayProvider for BaseAddressDetectionReason {
52+
type Raw = BNBaseAddressDetectionReason;
53+
type Context = ();
54+
type Wrapped<'a> = &'a Self;
55+
}
56+
57+
unsafe impl CoreArrayProviderInner for BaseAddressDetectionReason {
58+
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
59+
BNFreeBaseAddressDetectionReasons(raw)
60+
}
61+
62+
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
63+
// SAFETY BNBaseAddressDetectionReason and BaseAddressDetectionReason
64+
// are transparent
65+
mem::transmute::<&BNBaseAddressDetectionReason, &BaseAddressDetectionReason>(raw)
66+
}
67+
}
68+
69+
/// Build the initial analysis.
70+
///
71+
/// * `arch` - CPU architecture of the binary (defaults to using auto-detection)
72+
/// * `analysis` - analysis mode
73+
/// * `min_strlen` - minimum length of a string to be considered a point-of-interest
74+
/// * `alignment` - byte boundary to align the base address to while brute-forcing
75+
/// * `low_boundary` - lower boundary of the base address range to test
76+
/// * `high_boundary` - upper boundary of the base address range to test
77+
/// * `poi_analysis` - specifies types of points-of-interest to use for analysis
78+
/// * `max_pointers` - maximum number of candidate pointers to collect per pointer cluster
79+
pub struct BaseAddressDetectionBuilder {
80+
bv: BinaryView,
81+
arch: Option<CoreArchitecture>,
82+
analysis: Option<BaseAddressDetectionAnalysis>,
83+
min_strlen: Option<u32>,
84+
alignment: Option<core::num::NonZeroU32>,
85+
low_boundary: Option<u64>,
86+
high_boundary: Option<u64>,
87+
poi_analysis: Option<BaseAddressDetectionPOISetting>,
88+
max_pointers: Option<u32>,
89+
}
90+
91+
impl BaseAddressDetectionBuilder {
92+
pub fn new(bv: BinaryView) -> Self {
93+
BaseAddressDetectionBuilder {
94+
bv,
95+
arch: None,
96+
analysis: None,
97+
min_strlen: None,
98+
alignment: None,
99+
low_boundary: None,
100+
high_boundary: None,
101+
poi_analysis: None,
102+
max_pointers: None,
103+
}
104+
}
105+
106+
pub fn arch(mut self, value: CoreArchitecture) -> Self {
107+
self.arch = Some(value);
108+
self
109+
}
110+
111+
pub fn analysis(mut self, value: BaseAddressDetectionAnalysis) -> Self {
112+
self.analysis = Some(value);
113+
self
114+
}
115+
116+
pub fn min_strlen(mut self, value: u32) -> Self {
117+
self.min_strlen = Some(value);
118+
self
119+
}
120+
121+
pub fn alignment(mut self, value: core::num::NonZeroU32) -> Self {
122+
self.alignment = Some(value);
123+
self
124+
}
125+
126+
pub fn low_boundary(mut self, value: u64) -> Self {
127+
if let Some(high) = self.high_boundary {
128+
assert!(
129+
high >= value,
130+
"upper boundary must be greater than lower boundary"
131+
);
132+
}
133+
self.low_boundary = Some(value);
134+
self
135+
}
136+
137+
pub fn high_boundary(mut self, value: u64) -> Self {
138+
if let Some(low) = self.low_boundary {
139+
assert!(
140+
low <= value,
141+
"upper boundary must be greater than lower boundary"
142+
);
143+
}
144+
self.high_boundary = Some(value);
145+
self
146+
}
147+
148+
pub fn poi_analysis(mut self, value: BaseAddressDetectionPOISetting) -> Self {
149+
self.poi_analysis = Some(value);
150+
self
151+
}
152+
153+
pub fn max_pointers(mut self, value: u32) -> Self {
154+
assert!(value > 2, "max pointers must be at least 2");
155+
self.max_pointers = Some(value);
156+
self
157+
}
158+
159+
/// Initial analysis and attempts to identify candidate base addresses
160+
///
161+
/// .. note:: This operation can take a long time to complete depending on
162+
/// the size and complexity of the binary and the settings used
163+
pub fn process(self) -> Result<BaseAddressDetection, ()> {
164+
let arch = self.arch.or_else(|| self.bv.default_arch());
165+
let arch_name = arch.map(|a| a.name());
166+
let arch_ptr = arch_name
167+
.map(|a| a.as_ptr())
168+
.unwrap_or("\x00".as_ptr() as *const ffi::c_char);
169+
170+
let analysis = self.analysis.unwrap_or(BaseAddressDetectionAnalysis::Full);
171+
let min_strlen = self.min_strlen.unwrap_or(10);
172+
let alignment = self.alignment.map(|a| a.get()).unwrap_or(1024);
173+
let low_boundary = self.low_boundary.unwrap_or(u64::MIN);
174+
let high_boundary = self.high_boundary.unwrap_or(u64::MAX);
175+
let poi_analysis = self
176+
.poi_analysis
177+
.unwrap_or(BaseAddressDetectionPOISetting::POIAnalysisAll);
178+
let max_pointers = self.max_pointers.unwrap_or(128);
179+
let mut settings = BNBaseAddressDetectionSettings {
180+
Architecture: arch_ptr,
181+
Analysis: analysis.as_raw().as_ptr(),
182+
MinStrlen: min_strlen,
183+
Alignment: alignment,
184+
LowerBoundary: low_boundary,
185+
UpperBoundary: high_boundary,
186+
POIAnalysis: poi_analysis,
187+
MaxPointersPerCluster: max_pointers,
188+
};
189+
let base =
190+
ptr::NonNull::new(unsafe { BNCreateBaseAddressDetection(self.bv.handle) }).unwrap();
191+
let success = unsafe { BNDetectBaseAddress(base.as_ptr(), &mut settings) };
192+
if success {
193+
Ok(unsafe { BaseAddressDetection::from_raw(base) })
194+
} else {
195+
Err(())
196+
}
197+
}
198+
}
199+
200+
pub struct BaseAddressDetection {
201+
handle: ptr::NonNull<BNBaseAddressDetection>,
202+
}
203+
204+
impl Drop for BaseAddressDetection {
205+
fn drop(&mut self) {
206+
unsafe { BNFreeBaseAddressDetection(self.as_raw()) }
207+
}
208+
}
209+
210+
impl BaseAddressDetection {
211+
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNBaseAddressDetection>) -> Self {
212+
Self { handle }
213+
}
214+
215+
#[allow(clippy::mut_from_ref)]
216+
pub(crate) unsafe fn as_raw(&self) -> &mut BNBaseAddressDetection {
217+
&mut *self.handle.as_ptr()
218+
}
219+
220+
/// Indicates whether or not base address detection analysis was aborted early
221+
pub fn aborted(&self) -> bool {
222+
unsafe { BNIsBaseAddressDetectionAborted(self.as_raw()) }
223+
}
224+
225+
/// Aborts base address detection analysis
226+
///
227+
/// .. note:: `abort` does not stop base address detection until after
228+
/// initial analysis has completed and it is in the base address enumeration
229+
/// phase
230+
pub fn abort(&self) {
231+
unsafe { BNAbortBaseAddressDetection(self.as_raw()) }
232+
}
233+
234+
/// Returns a list of reasons that can be used to determine why a base
235+
/// address is a candidate
236+
pub fn get_reasons(&self, base_address: u64) -> Array<BaseAddressDetectionReason> {
237+
let mut count = 0;
238+
let reasons =
239+
unsafe { BNGetBaseAddressDetectionReasons(self.as_raw(), base_address, &mut count) };
240+
unsafe { Array::new(reasons, count, ()) }
241+
}
242+
243+
pub fn scores(&self, max_candidates: usize) -> BaseAddressDetectionResult {
244+
let mut scores = vec![BNBaseAddressDetectionScore::default(); max_candidates];
245+
let mut confidence = BNBaseAddressDetectionConfidence::NoConfidence;
246+
let mut last_base = 0;
247+
let num_candidates = unsafe {
248+
BNGetBaseAddressDetectionScores(
249+
self.as_raw(),
250+
scores.as_mut_ptr(),
251+
scores.len(),
252+
&mut confidence,
253+
&mut last_base,
254+
)
255+
};
256+
scores.truncate(num_candidates);
257+
// SAFETY BNBaseAddressDetectionScore and BaseAddressDetectionScore
258+
// are transparent
259+
let scores = unsafe {
260+
mem::transmute::<Vec<BNBaseAddressDetectionScore>, Vec<BaseAddressDetectionScore>>(
261+
scores,
262+
)
263+
};
264+
BaseAddressDetectionResult {
265+
scores,
266+
confidence,
267+
last_base,
268+
}
269+
}
270+
}

rust/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ mod operand_iter;
3030

3131
pub mod architecture;
3232
pub mod background_task;
33+
pub mod basedetection;
3334
pub mod basic_block;
3435
pub mod binary_reader;
3536
pub mod binary_view;

0 commit comments

Comments
 (0)