Skip to content

Commit 23a9b9e

Browse files
committed
Rust bindings for custom basic block analysis
1 parent f4068c2 commit 23a9b9e

File tree

6 files changed

+433
-19
lines changed

6 files changed

+433
-19
lines changed

rust/src/architecture.rs

Lines changed: 248 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,13 @@ use crate::{
2323
calling_convention::CoreCallingConvention,
2424
data_buffer::DataBuffer,
2525
disassembly::InstructionTextToken,
26-
function::Function,
26+
function::{ArchAndAddr, Function, NativeBlock},
2727
platform::Platform,
2828
rc::*,
2929
relocation::CoreRelocationHandler,
30-
string::IntoCStr,
31-
string::*,
30+
string::{IntoCStr, *},
3231
types::{NameAndType, Type},
33-
Endianness,
32+
BranchType, Endianness,
3433
};
3534
use std::ops::Deref;
3635
use std::{
@@ -42,8 +41,10 @@ use std::{
4241
mem::MaybeUninit,
4342
};
4443

44+
use crate::basic_block::BasicBlock;
4545
use crate::function_recognizer::FunctionRecognizer;
4646
use crate::relocation::{CustomRelocationHandlerHandle, RelocationHandler};
47+
use crate::variable::IndirectBranchInfo;
4748

4849
use crate::confidence::Conf;
4950
use crate::low_level_il::expression::ValueExpr;
@@ -54,6 +55,7 @@ use crate::low_level_il::{LowLevelILMutableExpression, LowLevelILMutableFunction
5455
pub use binaryninjacore_sys::BNFlagRole as FlagRole;
5556
pub use binaryninjacore_sys::BNImplicitRegisterExtend as ImplicitRegisterExtend;
5657
pub use binaryninjacore_sys::BNLowLevelILFlagCondition as FlagCondition;
58+
use std::collections::HashSet;
5759

5860
macro_rules! newtype {
5961
($name:ident, $inner_type:ty) => {
@@ -171,6 +173,23 @@ impl From<BranchKind> for BranchInfo {
171173
}
172174
}
173175

176+
impl From<BranchKind> for BranchType {
177+
fn from(value: BranchKind) -> Self {
178+
match value {
179+
BranchKind::Unresolved => BranchType::UnresolvedBranch,
180+
BranchKind::Unconditional(_) => BranchType::UnconditionalBranch,
181+
BranchKind::True(_) => BranchType::TrueBranch,
182+
BranchKind::False(_) => BranchType::FalseBranch,
183+
BranchKind::Call(_) => BranchType::CallDestination,
184+
BranchKind::FunctionReturn => BranchType::FunctionReturn,
185+
BranchKind::SystemCall => BranchType::SystemCall,
186+
BranchKind::Indirect => BranchType::IndirectBranch,
187+
BranchKind::Exception => BranchType::ExceptionBranch,
188+
BranchKind::UserDefined => BranchType::UserDefinedBranch,
189+
}
190+
}
191+
}
192+
174193
/// This is the number of branches that can be specified in an [`InstructionInfo`].
175194
pub const NUM_BRANCH_INFO: usize = 3;
176195

@@ -471,13 +490,13 @@ pub trait Architecture: 'static + Sized + AsRef<CoreArchitecture> {
471490
il: &LowLevelILMutableFunction,
472491
) -> Option<(usize, bool)>;
473492

474-
unsafe fn analyze_basic_blocks(
493+
fn analyze_basic_blocks(
475494
&self,
476495
function: &mut Function,
477-
context: *mut BNBasicBlockAnalysisContext,
496+
context: &mut BasicBlockAnalysisContext,
478497
) {
479498
unsafe {
480-
BNArchitectureDefaultAnalyzeBasicBlocks(function.handle, context);
499+
BNArchitectureDefaultAnalyzeBasicBlocks(function.handle, context.handle);
481500
}
482501
}
483502

@@ -1544,13 +1563,13 @@ impl Architecture for CoreArchitecture {
15441563
}
15451564
}
15461565

1547-
unsafe fn analyze_basic_blocks(
1566+
fn analyze_basic_blocks(
15481567
&self,
15491568
function: &mut Function,
1550-
context: *mut BNBasicBlockAnalysisContext,
1569+
context: &mut BasicBlockAnalysisContext,
15511570
) {
15521571
unsafe {
1553-
BNArchitectureAnalyzeBasicBlocks(self.handle, function.handle, context);
1572+
BNArchitectureAnalyzeBasicBlocks(self.handle, function.handle, context.handle);
15541573
}
15551574
}
15561575

@@ -1925,6 +1944,222 @@ impl Architecture for CoreArchitecture {
19251944
}
19261945
}
19271946

1947+
pub struct BasicBlockAnalysisContext {
1948+
pub(crate) handle: *mut BNBasicBlockAnalysisContext,
1949+
contextual_returns_dirty: bool,
1950+
1951+
// In
1952+
pub indirect_branches: Vec<IndirectBranchInfo>,
1953+
pub indirect_no_return_calls: HashSet<ArchAndAddr>,
1954+
pub analysis_skip_override: BNFunctionAnalysisSkipOverride,
1955+
pub translate_tail_calls: bool,
1956+
pub disallow_branch_to_string: bool,
1957+
pub max_function_size: u64,
1958+
pub halt_on_invalid_instruction: bool,
1959+
pub max_size_reached: bool,
1960+
1961+
// In/Out
1962+
contextual_returns: HashMap<ArchAndAddr, bool>,
1963+
1964+
// Out
1965+
direct_code_references: HashMap<u64, ArchAndAddr>,
1966+
direct_no_return_calls: HashSet<ArchAndAddr>,
1967+
halted_disassembly_addresses: HashSet<ArchAndAddr>,
1968+
}
1969+
1970+
impl BasicBlockAnalysisContext {
1971+
pub unsafe fn from_raw(handle: *mut BNBasicBlockAnalysisContext) -> Self {
1972+
debug_assert!(!handle.is_null());
1973+
1974+
let ctx_ref = &*handle;
1975+
1976+
let indirect_branches = (0..ctx_ref.indirectBranchesCount)
1977+
.map(|i| {
1978+
let raw: BNIndirectBranchInfo =
1979+
unsafe { std::ptr::read(ctx_ref.indirectBranches.add(i)) };
1980+
IndirectBranchInfo::from(raw)
1981+
})
1982+
.collect::<Vec<_>>();
1983+
1984+
let indirect_no_return_calls = (0..ctx_ref.indirectNoReturnCallsCount)
1985+
.map(|i| {
1986+
let raw = unsafe { std::ptr::read(ctx_ref.indirectNoReturnCalls.add(i)) };
1987+
ArchAndAddr::from(raw)
1988+
})
1989+
.collect::<HashSet<_>>();
1990+
1991+
let contextual_returns = (0..ctx_ref.contextualFunctionReturnCount)
1992+
.map(|i| {
1993+
let loc = unsafe {
1994+
let raw = std::ptr::read(ctx_ref.contextualFunctionReturnLocations.add(i));
1995+
ArchAndAddr::from(raw)
1996+
};
1997+
let val = unsafe { *ctx_ref.contextualFunctionReturnValues.add(i) };
1998+
(loc, val)
1999+
})
2000+
.collect::<HashMap<_, _>>();
2001+
2002+
let direct_code_references = (0..ctx_ref.directRefCount)
2003+
.map(|i| {
2004+
let src = unsafe {
2005+
let raw = std::ptr::read(ctx_ref.directRefSources.add(i));
2006+
ArchAndAddr::from(raw)
2007+
};
2008+
let tgt = unsafe { *ctx_ref.directRefTargets.add(i) };
2009+
(tgt, src)
2010+
})
2011+
.collect::<HashMap<_, _>>();
2012+
2013+
let direct_no_return_calls = (0..ctx_ref.directNoReturnCallsCount)
2014+
.map(|i| {
2015+
let raw = unsafe { std::ptr::read(ctx_ref.directNoReturnCalls.add(i)) };
2016+
ArchAndAddr::from(raw)
2017+
})
2018+
.collect::<HashSet<_>>();
2019+
2020+
let halted_disassembly_addresses = (0..ctx_ref.haltedDisassemblyAddressesCount)
2021+
.map(|i| {
2022+
let raw = unsafe { std::ptr::read(ctx_ref.haltedDisassemblyAddresses.add(i)) };
2023+
ArchAndAddr::from(raw)
2024+
})
2025+
.collect::<HashSet<_>>();
2026+
2027+
BasicBlockAnalysisContext {
2028+
handle,
2029+
contextual_returns_dirty: false,
2030+
indirect_branches,
2031+
indirect_no_return_calls,
2032+
analysis_skip_override: ctx_ref.analysisSkipOverride,
2033+
translate_tail_calls: ctx_ref.translateTailCalls,
2034+
disallow_branch_to_string: ctx_ref.disallowBranchToString,
2035+
max_function_size: ctx_ref.maxFunctionSize,
2036+
halt_on_invalid_instruction: ctx_ref.haltOnInvalidInstructions,
2037+
max_size_reached: ctx_ref.maxSizeReached,
2038+
contextual_returns,
2039+
direct_code_references,
2040+
direct_no_return_calls,
2041+
halted_disassembly_addresses,
2042+
}
2043+
}
2044+
2045+
pub fn add_contextual_return(&mut self, loc: ArchAndAddr, value: bool) {
2046+
if !self.contextual_returns.contains_key(&loc) {
2047+
self.contextual_returns_dirty = true;
2048+
}
2049+
2050+
self.contextual_returns.insert(loc, value);
2051+
}
2052+
2053+
pub fn add_direct_code_reference(&mut self, target: u64, src: ArchAndAddr) {
2054+
self.direct_code_references.entry(target).or_insert(src);
2055+
}
2056+
2057+
pub fn add_direct_no_return_call(&mut self, loc: ArchAndAddr) {
2058+
self.direct_no_return_calls.insert(loc);
2059+
}
2060+
2061+
pub fn add_halted_disassembly_address(&mut self, loc: ArchAndAddr) {
2062+
self.halted_disassembly_addresses.insert(loc);
2063+
}
2064+
2065+
pub fn create_basic_block(
2066+
&self,
2067+
arch: CoreArchitecture,
2068+
start: u64,
2069+
) -> Option<Ref<BasicBlock<NativeBlock>>> {
2070+
let raw_block =
2071+
unsafe { BNAnalyzeBasicBlocksContextCreateBasicBlock(self.handle, arch.handle, start) };
2072+
2073+
if raw_block.is_null() {
2074+
return None;
2075+
}
2076+
2077+
unsafe { Some(BasicBlock::ref_from_raw(raw_block, NativeBlock::new())) }
2078+
}
2079+
2080+
pub fn add_basic_block(&self, block: Ref<BasicBlock<NativeBlock>>) {
2081+
unsafe {
2082+
BNAnalyzeBasicBlocksContextAddBasicBlockToFunction(self.handle, block.handle);
2083+
}
2084+
}
2085+
2086+
pub fn add_temp_outgoing_reference(&self, target: &Function) {
2087+
unsafe {
2088+
BNAnalyzeBasicBlocksContextAddTempReference(self.handle, target.handle);
2089+
}
2090+
}
2091+
2092+
pub fn finalize(&mut self) {
2093+
if !self.direct_code_references.is_empty() {
2094+
let total = self.direct_code_references.len();
2095+
let mut sources: Vec<BNArchitectureAndAddress> = Vec::with_capacity(total);
2096+
let mut targets: Vec<u64> = Vec::with_capacity(total);
2097+
for (target, src) in &self.direct_code_references {
2098+
sources.push(src.into_raw());
2099+
targets.push(*target);
2100+
}
2101+
unsafe {
2102+
BNAnalyzeBasicBlocksContextSetDirectCodeReferences(
2103+
self.handle,
2104+
sources.as_mut_ptr(),
2105+
targets.as_mut_ptr(),
2106+
total,
2107+
);
2108+
}
2109+
}
2110+
2111+
if !self.direct_no_return_calls.is_empty() {
2112+
let total = self.direct_no_return_calls.len();
2113+
let mut locations: Vec<BNArchitectureAndAddress> = Vec::with_capacity(total);
2114+
for loc in &self.direct_no_return_calls {
2115+
locations.push(loc.into_raw());
2116+
}
2117+
unsafe {
2118+
BNAnalyzeBasicBlocksContextSetDirectNoReturnCalls(
2119+
self.handle,
2120+
locations.as_mut_ptr(),
2121+
total,
2122+
);
2123+
}
2124+
}
2125+
2126+
if !self.halted_disassembly_addresses.is_empty() {
2127+
let total = self.halted_disassembly_addresses.len();
2128+
let mut locations: Vec<BNArchitectureAndAddress> = Vec::with_capacity(total);
2129+
for loc in &self.halted_disassembly_addresses {
2130+
locations.push(loc.into_raw());
2131+
}
2132+
unsafe {
2133+
BNAnalyzeBasicBlocksContextSetHaltedDisassemblyAddresses(
2134+
self.handle,
2135+
locations.as_mut_ptr(),
2136+
total,
2137+
);
2138+
}
2139+
}
2140+
2141+
if self.contextual_returns_dirty {
2142+
let total = self.contextual_returns.len();
2143+
let mut locations: Vec<BNArchitectureAndAddress> = Vec::with_capacity(total);
2144+
let mut values: Vec<bool> = Vec::with_capacity(total);
2145+
for (loc, value) in &self.contextual_returns {
2146+
locations.push(loc.into_raw());
2147+
values.push(*value);
2148+
}
2149+
unsafe {
2150+
BNAnalyzeBasicBlocksContextSetContextualFunctionReturns(
2151+
self.handle,
2152+
locations.as_mut_ptr(),
2153+
values.as_mut_ptr(),
2154+
total,
2155+
);
2156+
}
2157+
}
2158+
2159+
unsafe { BNAnalyzeBasicBlocksContextFinalize(self.handle) };
2160+
}
2161+
}
2162+
19282163
impl Debug for CoreArchitecture {
19292164
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
19302165
f.debug_struct("CoreArchitecture")
@@ -2267,9 +2502,9 @@ where
22672502
{
22682503
let custom_arch = unsafe { &*(ctxt as *mut A) };
22692504
let mut function = unsafe { Function::from_raw(function) };
2270-
unsafe {
2271-
custom_arch.analyze_basic_blocks(&mut function, context);
2272-
}
2505+
let mut context: BasicBlockAnalysisContext =
2506+
unsafe { BasicBlockAnalysisContext::from_raw(context) };
2507+
custom_arch.analyze_basic_blocks(&mut function, &mut context);
22732508
}
22742509

22752510
extern "C" fn cb_reg_name<A>(ctxt: *mut c_void, reg: u32) -> *mut c_char

0 commit comments

Comments
 (0)