diff options
| author | Brandon Miller <brandon@vector35.com> | 2025-05-20 15:49:11 -0400 |
|---|---|---|
| committer | Brandon Miller <brandon@vector35.com> | 2025-09-29 07:07:41 -0400 |
| commit | 604a22a0050c000ed4d142589c9ce0e34ee7b570 (patch) | |
| tree | a3e2df1331303be24bbb82cb6966f1604ffe0bc9 /rust/src/architecture.rs | |
| parent | 288edd311e3312e98c934d0d35e5980d6147b6ca (diff) | |
Rust bindings for custom basic block analysis
Diffstat (limited to 'rust/src/architecture.rs')
| -rw-r--r-- | rust/src/architecture.rs | 289 |
1 files changed, 276 insertions, 13 deletions
diff --git a/rust/src/architecture.rs b/rust/src/architecture.rs index 6b33b6bb..87182a56 100644 --- a/rust/src/architecture.rs +++ b/rust/src/architecture.rs @@ -23,14 +23,13 @@ use crate::{ calling_convention::CoreCallingConvention, data_buffer::DataBuffer, disassembly::InstructionTextToken, - function::Function, + function::{ArchAndAddr, Function, NativeBlock}, platform::Platform, rc::*, relocation::CoreRelocationHandler, - string::IntoCStr, - string::*, + string::{IntoCStr, *}, types::{NameAndType, Type}, - Endianness, + BranchType, Endianness, }; use std::ops::Deref; use std::{ @@ -42,8 +41,10 @@ use std::{ mem::MaybeUninit, }; +use crate::basic_block::BasicBlock; use crate::function_recognizer::FunctionRecognizer; use crate::relocation::{CustomRelocationHandlerHandle, RelocationHandler}; +use crate::variable::IndirectBranchInfo; use crate::confidence::Conf; use crate::low_level_il::expression::ValueExpr; @@ -54,6 +55,7 @@ use crate::low_level_il::{LowLevelILMutableExpression, LowLevelILMutableFunction pub use binaryninjacore_sys::BNFlagRole as FlagRole; pub use binaryninjacore_sys::BNImplicitRegisterExtend as ImplicitRegisterExtend; pub use binaryninjacore_sys::BNLowLevelILFlagCondition as FlagCondition; +use std::collections::HashSet; macro_rules! newtype { ($name:ident, $inner_type:ty) => { @@ -171,6 +173,23 @@ impl From<BranchKind> for BranchInfo { } } +impl From<BranchKind> for BranchType { + fn from(value: BranchKind) -> Self { + match value { + BranchKind::Unresolved => BranchType::UnresolvedBranch, + BranchKind::Unconditional(_) => BranchType::UnconditionalBranch, + BranchKind::True(_) => BranchType::TrueBranch, + BranchKind::False(_) => BranchType::FalseBranch, + BranchKind::Call(_) => BranchType::CallDestination, + BranchKind::FunctionReturn => BranchType::FunctionReturn, + BranchKind::SystemCall => BranchType::SystemCall, + BranchKind::Indirect => BranchType::IndirectBranch, + BranchKind::Exception => BranchType::ExceptionBranch, + BranchKind::UserDefined => BranchType::UserDefinedBranch, + } + } +} + /// This is the number of branches that can be specified in an [`InstructionInfo`]. pub const NUM_BRANCH_INFO: usize = 3; @@ -471,13 +490,13 @@ pub trait Architecture: 'static + Sized + AsRef<CoreArchitecture> { il: &LowLevelILMutableFunction, ) -> Option<(usize, bool)>; - unsafe fn analyze_basic_blocks( + fn analyze_basic_blocks( &self, function: &mut Function, - context: *mut BNBasicBlockAnalysisContext, + context: &mut BasicBlockAnalysisContext, ) { unsafe { - BNArchitectureDefaultAnalyzeBasicBlocks(function.handle, context); + BNArchitectureDefaultAnalyzeBasicBlocks(function.handle, context.handle); } } @@ -1544,13 +1563,13 @@ impl Architecture for CoreArchitecture { } } - unsafe fn analyze_basic_blocks( + fn analyze_basic_blocks( &self, function: &mut Function, - context: *mut BNBasicBlockAnalysisContext, + context: &mut BasicBlockAnalysisContext, ) { unsafe { - BNArchitectureAnalyzeBasicBlocks(self.handle, function.handle, context); + BNArchitectureAnalyzeBasicBlocks(self.handle, function.handle, context.handle); } } @@ -1922,6 +1941,250 @@ impl Architecture for CoreArchitecture { } } +pub struct BasicBlockAnalysisContext { + pub(crate) handle: *mut BNBasicBlockAnalysisContext, + contextual_returns_dirty: bool, + + // In + pub indirect_branches: Vec<IndirectBranchInfo>, + pub indirect_no_return_calls: HashSet<ArchAndAddr>, + pub analysis_skip_override: BNFunctionAnalysisSkipOverride, + pub translate_tail_calls: bool, + pub disallow_branch_to_string: bool, + pub max_function_size: u64, + pub max_size_reached: bool, + + // In/Out + contextual_returns: HashMap<ArchAndAddr, bool>, + + // Out + direct_code_references: HashMap<u64, ArchAndAddr>, + direct_no_return_calls: HashSet<ArchAndAddr>, + halted_disassembly_addresses: HashSet<ArchAndAddr>, + inlined_unresolved_indirect_branches: HashSet<ArchAndAddr>, +} + +impl BasicBlockAnalysisContext { + pub unsafe fn from_raw(handle: *mut BNBasicBlockAnalysisContext) -> Self { + debug_assert!(!handle.is_null()); + + let ctx_ref = &*handle; + + let indirect_branches = (0..ctx_ref.indirectBranchesCount) + .map(|i| { + let raw: BNIndirectBranchInfo = + unsafe { std::ptr::read(ctx_ref.indirectBranches.add(i)) }; + IndirectBranchInfo::from(raw) + }) + .collect::<Vec<_>>(); + + let indirect_no_return_calls = (0..ctx_ref.indirectNoReturnCallsCount) + .map(|i| { + let raw = unsafe { std::ptr::read(ctx_ref.indirectNoReturnCalls.add(i)) }; + ArchAndAddr::from(raw) + }) + .collect::<HashSet<_>>(); + + let contextual_returns = (0..ctx_ref.contextualFunctionReturnCount) + .map(|i| { + let loc = unsafe { + let raw = std::ptr::read(ctx_ref.contextualFunctionReturnLocations.add(i)); + ArchAndAddr::from(raw) + }; + let val = unsafe { *ctx_ref.contextualFunctionReturnValues.add(i) }; + (loc, val) + }) + .collect::<HashMap<_, _>>(); + + let direct_code_references = (0..ctx_ref.directRefCount) + .map(|i| { + let src = unsafe { + let raw = std::ptr::read(ctx_ref.directRefSources.add(i)); + ArchAndAddr::from(raw) + }; + let tgt = unsafe { *ctx_ref.directRefTargets.add(i) }; + (tgt, src) + }) + .collect::<HashMap<_, _>>(); + + let direct_no_return_calls = (0..ctx_ref.directNoReturnCallsCount) + .map(|i| { + let raw = unsafe { std::ptr::read(ctx_ref.directNoReturnCalls.add(i)) }; + ArchAndAddr::from(raw) + }) + .collect::<HashSet<_>>(); + + let halted_disassembly_addresses = (0..ctx_ref.haltedDisassemblyAddressesCount) + .map(|i| { + let raw = unsafe { std::ptr::read(ctx_ref.haltedDisassemblyAddresses.add(i)) }; + ArchAndAddr::from(raw) + }) + .collect::<HashSet<_>>(); + + let inlined_unresolved_indirect_branches = (0..ctx_ref + .inlinedUnresolvedIndirectBranchCount) + .map(|i| { + let raw = + unsafe { std::ptr::read(ctx_ref.inlinedUnresolvedIndirectBranches.add(i)) }; + ArchAndAddr::from(raw) + }) + .collect::<HashSet<_>>(); + + BasicBlockAnalysisContext { + handle, + contextual_returns_dirty: false, + indirect_branches, + indirect_no_return_calls, + analysis_skip_override: ctx_ref.analysisSkipOverride, + translate_tail_calls: ctx_ref.translateTailCalls, + disallow_branch_to_string: ctx_ref.disallowBranchToString, + max_function_size: ctx_ref.maxFunctionSize, + max_size_reached: ctx_ref.maxSizeReached, + contextual_returns, + direct_code_references, + direct_no_return_calls, + halted_disassembly_addresses, + inlined_unresolved_indirect_branches, + } + } + + pub fn add_contextual_return(&mut self, loc: ArchAndAddr, value: bool) { + if !self.contextual_returns.contains_key(&loc) { + self.contextual_returns_dirty = true; + } + + self.contextual_returns.insert(loc, value); + } + + pub fn add_direct_code_reference(&mut self, target: u64, src: ArchAndAddr) { + self.direct_code_references.entry(target).or_insert(src); + } + + pub fn add_direct_no_return_call(&mut self, loc: ArchAndAddr) { + self.direct_no_return_calls.insert(loc); + } + + pub fn add_halted_disassembly_address(&mut self, loc: ArchAndAddr) { + self.halted_disassembly_addresses.insert(loc); + } + + pub fn add_inlined_unresolved_indirect_branch(&mut self, loc: ArchAndAddr) { + self.inlined_unresolved_indirect_branches.insert(loc); + } + + pub fn create_basic_block( + &self, + arch: CoreArchitecture, + start: u64, + ) -> Option<Ref<BasicBlock<NativeBlock>>> { + let raw_block = + unsafe { BNAnalyzeBasicBlocksContextCreateBasicBlock(self.handle, arch.handle, start) }; + + if raw_block.is_null() { + return None; + } + + unsafe { Some(BasicBlock::ref_from_raw(raw_block, NativeBlock::new())) } + } + + pub fn add_basic_block(&self, block: Ref<BasicBlock<NativeBlock>>) { + unsafe { + BNAnalyzeBasicBlocksContextAddBasicBlockToFunction(self.handle, block.handle); + } + } + + pub fn add_temp_outgoing_reference(&self, target: &Function) { + unsafe { + BNAnalyzeBasicBlocksContextAddTempReference(self.handle, target.handle); + } + } + + pub fn finalize(&mut self) { + if !self.direct_code_references.is_empty() { + let total = self.direct_code_references.len(); + let mut sources: Vec<BNArchitectureAndAddress> = Vec::with_capacity(total); + let mut targets: Vec<u64> = Vec::with_capacity(total); + for (target, src) in &self.direct_code_references { + sources.push(src.into_raw()); + targets.push(*target); + } + unsafe { + BNAnalyzeBasicBlocksContextSetDirectCodeReferences( + self.handle, + sources.as_mut_ptr(), + targets.as_mut_ptr(), + total, + ); + } + } + + if !self.direct_no_return_calls.is_empty() { + let total = self.direct_no_return_calls.len(); + let mut locations: Vec<BNArchitectureAndAddress> = Vec::with_capacity(total); + for loc in &self.direct_no_return_calls { + locations.push(loc.into_raw()); + } + unsafe { + BNAnalyzeBasicBlocksContextSetDirectNoReturnCalls( + self.handle, + locations.as_mut_ptr(), + total, + ); + } + } + + if !self.halted_disassembly_addresses.is_empty() { + let total = self.halted_disassembly_addresses.len(); + let mut locations: Vec<BNArchitectureAndAddress> = Vec::with_capacity(total); + for loc in &self.halted_disassembly_addresses { + locations.push(loc.into_raw()); + } + unsafe { + BNAnalyzeBasicBlocksContextSetHaltedDisassemblyAddresses( + self.handle, + locations.as_mut_ptr(), + total, + ); + } + } + + if !self.inlined_unresolved_indirect_branches.is_empty() { + let total = self.inlined_unresolved_indirect_branches.len(); + let mut locations: Vec<BNArchitectureAndAddress> = Vec::with_capacity(total); + for loc in &self.inlined_unresolved_indirect_branches { + locations.push(loc.into_raw()); + } + unsafe { + BNAnalyzeBasicBlocksContextSetInlinedUnresolvedIndirectBranches( + self.handle, + locations.as_mut_ptr(), + total, + ); + } + } + + if self.contextual_returns_dirty { + let total = self.contextual_returns.len(); + let mut locations: Vec<BNArchitectureAndAddress> = Vec::with_capacity(total); + let mut values: Vec<bool> = Vec::with_capacity(total); + for (loc, value) in &self.contextual_returns { + locations.push(loc.into_raw()); + values.push(*value); + } + unsafe { + BNAnalyzeBasicBlocksContextSetContextualFunctionReturns( + self.handle, + locations.as_mut_ptr(), + values.as_mut_ptr(), + total, + ); + } + } + + unsafe { BNAnalyzeBasicBlocksContextFinalize(self.handle) }; + } +} + impl Debug for CoreArchitecture { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("CoreArchitecture") @@ -2264,9 +2527,9 @@ where { let custom_arch = unsafe { &*(ctxt as *mut A) }; let mut function = unsafe { Function::from_raw(function) }; - unsafe { - custom_arch.analyze_basic_blocks(&mut function, context); - } + let mut context: BasicBlockAnalysisContext = + unsafe { BasicBlockAnalysisContext::from_raw(context) }; + custom_arch.analyze_basic_blocks(&mut function, &mut context); } extern "C" fn cb_reg_name<A>(ctxt: *mut c_void, reg: u32) -> *mut c_char |
