binaryninja/
base_detection.rs

1use binaryninjacore_sys::*;
2use std::ffi::CStr;
3
4use crate::architecture::CoreArchitecture;
5use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner};
6use crate::string::IntoCStr;
7use std::num::NonZeroU32;
8use std::ptr::NonNull;
9
10pub type BaseAddressDetectionPOISetting = BNBaseAddressDetectionPOISetting;
11pub type BaseAddressDetectionConfidence = BNBaseAddressDetectionConfidence;
12pub type BaseAddressDetectionPOIType = BNBaseAddressDetectionPOIType;
13pub type BaseAddressDetectionAnalysisMode = BNBaseAddressDetectionAnalysisMode;
14
15/// This is the architecture name used to use the architecture auto-detection feature.
16const BASE_ADDRESS_AUTO_DETECTION_ARCH: &str = "auto detect";
17
18pub enum BaseAddressDetectionAnalysis {
19    Basic,
20    ControlFlow,
21    Full,
22}
23
24impl BaseAddressDetectionAnalysis {
25    pub fn as_raw(&self) -> &'static CStr {
26        match self {
27            BaseAddressDetectionAnalysis::Basic => c"basic",
28            BaseAddressDetectionAnalysis::ControlFlow => c"controlFlow",
29            BaseAddressDetectionAnalysis::Full => c"full",
30        }
31    }
32}
33
34#[derive(Debug, Clone, PartialEq, Eq, Hash)]
35pub struct BaseAddressDetectionResult {
36    pub scores: Vec<BaseAddressDetectionScore>,
37    pub confidence: BaseAddressDetectionConfidence,
38    pub last_base: u64,
39}
40
41#[repr(C)]
42#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
43pub struct BaseAddressDetectionScore {
44    pub score: usize,
45    pub base_address: u64,
46}
47
48#[repr(C)]
49#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
50pub struct BaseAddressDetectionReason {
51    pub pointer: u64,
52    pub poi_offset: u64,
53    pub poi_type: BaseAddressDetectionPOIType,
54}
55
56impl CoreArrayProvider for BaseAddressDetectionReason {
57    type Raw = BNBaseAddressDetectionReason;
58    type Context = ();
59    type Wrapped<'a> = &'a Self;
60}
61
62unsafe impl CoreArrayProviderInner for BaseAddressDetectionReason {
63    unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
64        BNFreeBaseAddressDetectionReasons(raw)
65    }
66
67    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
68        // SAFETY BNBaseAddressDetectionReason and BaseAddressDetectionReason
69        // are transparent
70        std::mem::transmute::<&BNBaseAddressDetectionReason, &BaseAddressDetectionReason>(raw)
71    }
72}
73
74pub struct BaseAddressDetection {
75    handle: NonNull<BNBaseAddressDetection>,
76}
77
78impl BaseAddressDetection {
79    pub(crate) unsafe fn from_raw(handle: NonNull<BNBaseAddressDetection>) -> Self {
80        Self { handle }
81    }
82
83    #[allow(clippy::mut_from_ref)]
84    pub(crate) unsafe fn as_raw(&self) -> &mut BNBaseAddressDetection {
85        &mut *self.handle.as_ptr()
86    }
87
88    /// Indicates whether base address detection analysis was aborted early
89    pub fn aborted(&self) -> bool {
90        unsafe { BNIsBaseAddressDetectionAborted(self.as_raw()) }
91    }
92
93    /// Aborts base address detection analysis
94    ///
95    /// NOTE: Does not stop base address detection until after initial analysis has completed, and
96    /// it is in the base address enumeration phase.
97    pub fn abort(&self) {
98        unsafe { BNAbortBaseAddressDetection(self.as_raw()) }
99    }
100
101    /// Returns a list of reasons that can be used to determine why a base
102    /// address is a candidate
103    pub fn get_reasons(&self, base_address: u64) -> Array<BaseAddressDetectionReason> {
104        let mut count = 0;
105        let reasons =
106            unsafe { BNGetBaseAddressDetectionReasons(self.as_raw(), base_address, &mut count) };
107        unsafe { Array::new(reasons, count, ()) }
108    }
109
110    pub fn scores(&self, max_candidates: usize) -> BaseAddressDetectionResult {
111        let mut scores = vec![BNBaseAddressDetectionScore::default(); max_candidates];
112        let mut confidence = BNBaseAddressDetectionConfidence::NoConfidence;
113        let mut last_base = 0;
114        let num_candidates = unsafe {
115            BNGetBaseAddressDetectionScores(
116                self.as_raw(),
117                scores.as_mut_ptr(),
118                scores.len(),
119                &mut confidence,
120                &mut last_base,
121            )
122        };
123        scores.truncate(num_candidates);
124        // SAFETY BNBaseAddressDetectionScore and BaseAddressDetectionScore
125        // are transparent
126        let scores = unsafe {
127            std::mem::transmute::<Vec<BNBaseAddressDetectionScore>, Vec<BaseAddressDetectionScore>>(
128                scores,
129            )
130        };
131        BaseAddressDetectionResult {
132            scores,
133            confidence,
134            last_base,
135        }
136    }
137
138    /// Initial analysis and attempts to identify candidate base addresses
139    ///
140    /// NOTE: This operation can take a long time to complete depending on the size and complexity
141    /// of the binary and the settings used.
142    pub fn detect(&self, settings: &BaseAddressDetectionSettings) -> bool {
143        let mut raw_settings = BaseAddressDetectionSettings::into_raw(settings);
144        unsafe { BNDetectBaseAddress(self.handle.as_ptr(), &mut raw_settings) }
145    }
146
147    /// Use instruction analysis to identify candidate base addresses.
148    pub fn detect_with_instruction_analysis(
149        &self,
150        settings: &BaseAddressDetectionInstructionAnalysisSettings,
151    ) -> bool {
152        let mut raw_settings = BaseAddressDetectionInstructionAnalysisSettings::into_raw(settings);
153        unsafe {
154            BNDetectBaseAddressWithInstructionAnalysis(self.handle.as_ptr(), &mut raw_settings)
155        }
156    }
157
158    /// Sample raw binary contents to identify candidate base addresses.
159    pub fn detect_with_sampling(&self, settings: &BaseAddressDetectionSamplingSettings) -> bool {
160        let mut raw_settings = BaseAddressDetectionSamplingSettings::into_raw(settings);
161        unsafe { BNDetectBaseAddressWithSampling(self.handle.as_ptr(), &mut raw_settings) }
162    }
163}
164
165impl Drop for BaseAddressDetection {
166    fn drop(&mut self) {
167        unsafe { BNFreeBaseAddressDetection(self.as_raw()) }
168    }
169}
170
171/// Builds the initial analysis settings for base address detection.
172pub struct BaseAddressDetectionSettings {
173    arch: Option<CoreArchitecture>,
174    /// Analysis mode to use
175    analysis: BaseAddressDetectionAnalysis,
176    /// Minimum length of a string to be considered a point-of-interest
177    min_string_len: u32,
178    /// Byte boundary to align the base address to while brute-forcing
179    alignment: NonZeroU32,
180    /// Lower boundary of the base address range to test
181    lower_boundary: u64,
182    /// Upper boundary of the base address range to test
183    upper_boundary: u64,
184    /// Specifies types of points-of-interest to use for analysis
185    poi_analysis: BaseAddressDetectionPOISetting,
186    /// Maximum number of candidate pointers to collect per pointer cluster
187    max_pointers: u32,
188    /// Base address detection algorithm to use
189    analysis_mode: BaseAddressDetectionAnalysisMode,
190}
191
192impl BaseAddressDetectionSettings {
193    pub(crate) fn into_raw(value: &Self) -> BNBaseAddressDetectionSettings {
194        let arch_name = value
195            .arch
196            .map(|a| a.name())
197            .unwrap_or(BASE_ADDRESS_AUTO_DETECTION_ARCH.to_string());
198        let c_arch_name = arch_name.to_cstr();
199        BNBaseAddressDetectionSettings {
200            Architecture: c_arch_name.into_raw(),
201            Analysis: value.analysis.as_raw().as_ptr(),
202            MinStrlen: value.min_string_len,
203            Alignment: value.alignment.get(),
204            LowerBoundary: value.lower_boundary,
205            UpperBoundary: value.upper_boundary,
206            POIAnalysis: value.poi_analysis,
207            MaxPointersPerCluster: value.max_pointers,
208            AnalysisMode: value.analysis_mode,
209        }
210    }
211
212    pub fn arch(mut self, value: CoreArchitecture) -> Self {
213        self.arch = Some(value);
214        self
215    }
216
217    pub fn analysis(mut self, value: BaseAddressDetectionAnalysis) -> Self {
218        self.analysis = value;
219        self
220    }
221
222    pub fn min_strlen(mut self, value: u32) -> Self {
223        self.min_string_len = value;
224        self
225    }
226
227    pub fn alignment(mut self, value: NonZeroU32) -> Self {
228        self.alignment = value;
229        self
230    }
231
232    /// Specify the lower boundary of the base address range to test.
233    ///
234    /// NOTE: The passed `value` **must** be less than the upper boundary.
235    pub fn low_boundary(mut self, value: u64) -> Self {
236        assert!(
237            self.upper_boundary >= value,
238            "upper boundary must be greater than lower boundary"
239        );
240        self.lower_boundary = value;
241        self
242    }
243
244    /// Specify the upper boundary of the base address range to test.
245    ///
246    /// NOTE: The passed `value` **must** be greater than the lower boundary.
247    pub fn high_boundary(mut self, value: u64) -> Self {
248        assert!(
249            self.lower_boundary <= value,
250            "upper boundary must be greater than lower boundary"
251        );
252        self.upper_boundary = value;
253        self
254    }
255
256    pub fn poi_analysis(mut self, value: BaseAddressDetectionPOISetting) -> Self {
257        self.poi_analysis = value;
258        self
259    }
260
261    /// Specify the maximum number of candidate pointers to collect per pointer cluster.
262    ///
263    /// NOTE: The passed `value` **must** be at least 2.
264    pub fn max_pointers(mut self, value: u32) -> Self {
265        assert!(value > 2, "max pointers must be at least 2");
266        self.max_pointers = value;
267        self
268    }
269
270    pub fn analysis_mode(mut self, value: BaseAddressDetectionAnalysisMode) -> Self {
271        self.analysis_mode = value;
272        self
273    }
274}
275
276impl Default for BaseAddressDetectionSettings {
277    fn default() -> Self {
278        BaseAddressDetectionSettings {
279            arch: None,
280            analysis: BaseAddressDetectionAnalysis::Full,
281            min_string_len: 10,
282            alignment: 1024.try_into().unwrap(),
283            lower_boundary: u64::MIN,
284            upper_boundary: u64::MAX,
285            poi_analysis: BaseAddressDetectionPOISetting::POIAnalysisAll,
286            max_pointers: 128,
287            analysis_mode:
288                BaseAddressDetectionAnalysisMode::InstructionAnalysisBaseAddressDetection,
289        }
290    }
291}
292
293/// Settings shared by each base address detection mode.
294pub struct BaseAddressDetectionCommonSettings {
295    arch: Option<CoreArchitecture>,
296    /// Minimum length of a string to be considered a point-of-interest
297    min_string_len: u32,
298    /// Lower boundary of the base address range to test
299    lower_boundary: u64,
300    /// Upper boundary of the base address range to test
301    upper_boundary: u64,
302}
303
304impl BaseAddressDetectionCommonSettings {
305    fn into_raw(value: &Self) -> BNBaseAddressDetectionCommonSettings {
306        let arch_name = value
307            .arch
308            .map(|a| a.name())
309            .unwrap_or(BASE_ADDRESS_AUTO_DETECTION_ARCH.to_string());
310        let c_arch_name = arch_name.to_cstr();
311        BNBaseAddressDetectionCommonSettings {
312            Architecture: c_arch_name.into_raw(),
313            MinStrlen: value.min_string_len,
314            LowerBoundary: value.lower_boundary,
315            UpperBoundary: value.upper_boundary,
316        }
317    }
318
319    pub fn arch(mut self, value: CoreArchitecture) -> Self {
320        self.arch = Some(value);
321        self
322    }
323
324    pub fn min_strlen(mut self, value: u32) -> Self {
325        self.min_string_len = value;
326        self
327    }
328
329    /// Specify the lower boundary of the base address range to test.
330    ///
331    /// NOTE: The passed `value` **must** be less than the upper boundary.
332    pub fn low_boundary(mut self, value: u64) -> Self {
333        assert!(
334            self.upper_boundary >= value,
335            "upper boundary must be greater than lower boundary"
336        );
337        self.lower_boundary = value;
338        self
339    }
340
341    /// Specify the upper boundary of the base address range to test.
342    ///
343    /// NOTE: The passed `value` **must** be greater than the lower boundary.
344    pub fn high_boundary(mut self, value: u64) -> Self {
345        assert!(
346            self.lower_boundary <= value,
347            "upper boundary must be greater than lower boundary"
348        );
349        self.upper_boundary = value;
350        self
351    }
352}
353
354impl Default for BaseAddressDetectionCommonSettings {
355    fn default() -> Self {
356        Self {
357            arch: None,
358            min_string_len: 10,
359            lower_boundary: u64::MIN,
360            upper_boundary: u64::MAX,
361        }
362    }
363}
364
365/// Settings for instruction analysis base address detection.
366pub struct BaseAddressDetectionInstructionAnalysisSettings {
367    common: BaseAddressDetectionCommonSettings,
368    /// Analysis mode to use
369    analysis: BaseAddressDetectionAnalysis,
370    /// Byte boundary to align the base address to while brute-forcing
371    alignment: NonZeroU32,
372    /// Specifies types of points-of-interest to use for analysis
373    poi_analysis: BaseAddressDetectionPOISetting,
374    /// Maximum number of candidate pointers to collect per pointer cluster
375    max_pointers: u32,
376}
377
378impl BaseAddressDetectionInstructionAnalysisSettings {
379    pub(crate) fn into_raw(value: &Self) -> BNBaseAddressDetectionInstructionAnalysisSettings {
380        BNBaseAddressDetectionInstructionAnalysisSettings {
381            Common: BaseAddressDetectionCommonSettings::into_raw(&value.common),
382            Analysis: value.analysis.as_raw().as_ptr(),
383            Alignment: value.alignment.get(),
384            POIAnalysis: value.poi_analysis,
385            MaxPointersPerCluster: value.max_pointers,
386        }
387    }
388
389    pub fn common(mut self, value: BaseAddressDetectionCommonSettings) -> Self {
390        self.common = value;
391        self
392    }
393
394    pub fn arch(mut self, value: CoreArchitecture) -> Self {
395        self.common = self.common.arch(value);
396        self
397    }
398
399    pub fn analysis(mut self, value: BaseAddressDetectionAnalysis) -> Self {
400        self.analysis = value;
401        self
402    }
403
404    pub fn min_strlen(mut self, value: u32) -> Self {
405        self.common = self.common.min_strlen(value);
406        self
407    }
408
409    pub fn alignment(mut self, value: NonZeroU32) -> Self {
410        self.alignment = value;
411        self
412    }
413
414    pub fn low_boundary(mut self, value: u64) -> Self {
415        self.common = self.common.low_boundary(value);
416        self
417    }
418
419    pub fn high_boundary(mut self, value: u64) -> Self {
420        self.common = self.common.high_boundary(value);
421        self
422    }
423
424    pub fn poi_analysis(mut self, value: BaseAddressDetectionPOISetting) -> Self {
425        self.poi_analysis = value;
426        self
427    }
428
429    /// Specify the maximum number of candidate pointers to collect per pointer cluster.
430    ///
431    /// NOTE: The passed `value` **must** be at least 2.
432    pub fn max_pointers(mut self, value: u32) -> Self {
433        assert!(value >= 2, "max pointers must be at least 2");
434        self.max_pointers = value;
435        self
436    }
437}
438
439impl Default for BaseAddressDetectionInstructionAnalysisSettings {
440    fn default() -> Self {
441        Self {
442            common: BaseAddressDetectionCommonSettings::default(),
443            analysis: BaseAddressDetectionAnalysis::Full,
444            alignment: 1024.try_into().unwrap(),
445            poi_analysis: BaseAddressDetectionPOISetting::POIAnalysisAll,
446            max_pointers: 128,
447        }
448    }
449}
450
451/// Settings for sampling base address detection.
452pub struct BaseAddressDetectionSamplingSettings {
453    common: BaseAddressDetectionCommonSettings,
454    /// Byte boundary to align sampled base address candidates to
455    alignment: NonZeroU32,
456}
457
458impl BaseAddressDetectionSamplingSettings {
459    pub(crate) fn into_raw(value: &Self) -> BNBaseAddressDetectionSamplingSettings {
460        BNBaseAddressDetectionSamplingSettings {
461            Common: BaseAddressDetectionCommonSettings::into_raw(&value.common),
462            Alignment: value.alignment.get(),
463        }
464    }
465
466    pub fn common(mut self, value: BaseAddressDetectionCommonSettings) -> Self {
467        self.common = value;
468        self
469    }
470
471    pub fn arch(mut self, value: CoreArchitecture) -> Self {
472        self.common = self.common.arch(value);
473        self
474    }
475
476    pub fn min_strlen(mut self, value: u32) -> Self {
477        self.common = self.common.min_strlen(value);
478        self
479    }
480
481    pub fn alignment(mut self, value: NonZeroU32) -> Self {
482        self.alignment = value;
483        self
484    }
485
486    pub fn low_boundary(mut self, value: u64) -> Self {
487        self.common = self.common.low_boundary(value);
488        self
489    }
490
491    pub fn high_boundary(mut self, value: u64) -> Self {
492        self.common = self.common.high_boundary(value);
493        self
494    }
495}
496
497impl Default for BaseAddressDetectionSamplingSettings {
498    fn default() -> Self {
499        Self {
500            common: BaseAddressDetectionCommonSettings::default(),
501            alignment: 4096.try_into().unwrap(),
502        }
503    }
504}