Skip to content

Commit ac527f7

Browse files
committed
Rust bindings for custom basic block analysis
1 parent 10f9319 commit ac527f7

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

@@ -1922,6 +1941,222 @@ impl Architecture for CoreArchitecture {
19221941
}
19231942
}
19241943

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

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

0 commit comments

Comments
 (0)