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