binaryninja/
base_detection.rs

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