Skip to content

Commit 086a7ba

Browse files
committed
Rust APIs for custom function lifters
1 parent ac359f8 commit 086a7ba

9 files changed

Lines changed: 276 additions & 17 deletions

File tree

rust/src/architecture.rs

Lines changed: 138 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,15 @@ use crate::{
2626
calling_convention::CoreCallingConvention,
2727
data_buffer::DataBuffer,
2828
disassembly::InstructionTextToken,
29-
function::Function,
29+
function::{Function, Location, NativeBlock},
3030
platform::Platform,
3131
rc::*,
3232
relocation::CoreRelocationHandler,
3333
string::{IntoCStr, *},
3434
types::{NameAndType, Type},
3535
Endianness,
3636
};
37+
use std::collections::{HashMap, HashSet};
3738
use std::ops::Deref;
3839
use std::{
3940
borrow::Borrow,
@@ -47,7 +48,9 @@ use std::ptr::NonNull;
4748
use crate::function_recognizer::FunctionRecognizer;
4849
use crate::relocation::{CustomRelocationHandlerHandle, RelocationHandler};
4950

51+
use crate::basic_block::BasicBlock;
5052
use crate::confidence::Conf;
53+
use crate::logger::Logger;
5154
use crate::low_level_il::expression::ValueExpr;
5255
use crate::low_level_il::lifting::{
5356
get_default_flag_cond_llil, get_default_flag_write_llil, LowLevelILFlagWriteOp,
@@ -591,13 +594,133 @@ pub trait ArchitectureWithFunctionContext: Architecture {
591594

592595
pub struct FunctionLifterContext {
593596
pub(crate) handle: *mut BNFunctionLifterContext,
597+
pub function: *mut BNLowLevelILFunction,
598+
pub platform: Ref<Platform>,
599+
pub logger: Ref<Logger>,
600+
pub blocks: Vec<Ref<BasicBlock<NativeBlock>>>,
601+
pub no_return_calls: HashSet<Location>,
602+
pub contextual_returns: HashMap<Location, bool>,
603+
pub inlined_remapping: HashMap<Location, Location>,
604+
pub user_indirect_branches: HashMap<Location, HashSet<Location>>,
605+
pub auto_indirect_branches: HashMap<Location, HashSet<Location>>,
606+
//pub inlined_calls: HashSet<u64>,
607+
}
608+
609+
unsafe fn lifter_context_slice<'a, T>(ptr: *const T, len: usize) -> &'a [T] {
610+
if len == 0 {
611+
&[]
612+
} else {
613+
debug_assert!(!ptr.is_null());
614+
unsafe { std::slice::from_raw_parts(ptr, len) }
615+
}
594616
}
595617

596618
impl FunctionLifterContext {
597-
pub unsafe fn from_raw(handle: *mut BNFunctionLifterContext) -> Self {
619+
pub unsafe fn from_raw(
620+
function: *mut BNLowLevelILFunction,
621+
handle: *mut BNFunctionLifterContext,
622+
) -> Self {
623+
debug_assert!(!function.is_null());
598624
debug_assert!(!handle.is_null());
625+
let flc_ref = &*handle;
626+
let platform = unsafe { Platform::ref_from_raw(BNNewPlatformReference(flc_ref.platform)) };
627+
let logger = unsafe { Logger::ref_from_raw(BNNewLoggerReference(flc_ref.logger)) };
628+
629+
let mut blocks = Vec::new();
630+
for i in 0..flc_ref.basicBlockCount {
631+
let block = unsafe {
632+
Some(BasicBlock::ref_from_raw(
633+
BNNewBasicBlockReference(*flc_ref.basicBlocks.add(i)),
634+
NativeBlock::new(),
635+
))
636+
};
637+
638+
blocks.push(block.unwrap());
639+
}
640+
641+
let raw_no_return_calls: &[BNArchitectureAndAddress] =
642+
lifter_context_slice(flc_ref.noReturnCalls, flc_ref.noReturnCallsCount);
643+
let no_return_calls: HashSet<Location> =
644+
raw_no_return_calls.iter().map(Location::from).collect();
645+
646+
let raw_contextual_return_locs: &[BNArchitectureAndAddress] = unsafe {
647+
lifter_context_slice(
648+
flc_ref.contextualFunctionReturnLocations,
649+
flc_ref.contextualFunctionReturnCount,
650+
)
651+
};
652+
let raw_contextual_return_vals: &[bool] = unsafe {
653+
lifter_context_slice(
654+
flc_ref.contextualFunctionReturnValues,
655+
flc_ref.contextualFunctionReturnCount,
656+
)
657+
};
658+
let contextual_returns: HashMap<Location, bool> = raw_contextual_return_locs
659+
.iter()
660+
.map(Location::from)
661+
.zip(raw_contextual_return_vals.iter().copied())
662+
.collect();
663+
664+
let inlined_remapping: HashMap<Location, Location> = {
665+
let raw_inline_remap_locs: &[BNArchitectureAndAddress] = lifter_context_slice(
666+
flc_ref.inlinedRemappingKeys,
667+
flc_ref.inlinedRemappingEntryCount,
668+
);
669+
670+
let raw_inline_remap_dests: &[BNArchitectureAndAddress] = lifter_context_slice(
671+
flc_ref.inlinedRemappingValues,
672+
flc_ref.inlinedRemappingEntryCount,
673+
);
674+
675+
raw_inline_remap_locs
676+
.iter()
677+
.map(Location::from)
678+
.zip(raw_inline_remap_dests.iter().map(Location::from))
679+
.collect()
680+
};
599681

600-
FunctionLifterContext { handle }
682+
let mut user_indirect_branches: HashMap<Location, HashSet<Location>> = HashMap::new();
683+
let mut auto_indirect_branches: HashMap<Location, HashSet<Location>> = HashMap::new();
684+
for i in 0..flc_ref.indirectBranchesCount {
685+
let entry = unsafe { *flc_ref.indirectBranches.add(i) };
686+
let src = Location::new(
687+
Some(CoreArchitecture::from_raw(entry.sourceArch)),
688+
entry.sourceAddr,
689+
);
690+
let dest = Location::new(
691+
Some(CoreArchitecture::from_raw(entry.destArch)),
692+
entry.destAddr,
693+
);
694+
if entry.autoDefined {
695+
auto_indirect_branches.entry(src).or_default().insert(dest);
696+
} else {
697+
user_indirect_branches.entry(src).or_default().insert(dest);
698+
}
699+
}
700+
701+
FunctionLifterContext {
702+
handle,
703+
function: BNNewLowLevelILFunctionReference(function),
704+
platform,
705+
logger,
706+
blocks,
707+
no_return_calls,
708+
contextual_returns,
709+
inlined_remapping,
710+
user_indirect_branches,
711+
auto_indirect_branches,
712+
}
713+
}
714+
715+
pub fn prepare_block_translation(
716+
&self,
717+
func: &LowLevelILMutableFunction,
718+
arch: &CoreArchitecture,
719+
address: u64,
720+
) {
721+
unsafe {
722+
BNPrepareBlockTranslation(func.handle, arch.handle, address);
723+
}
601724
}
602725

603726
pub fn get_function_arch_context<A: ArchitectureWithFunctionContext>(
@@ -615,6 +738,14 @@ impl FunctionLifterContext {
615738
}
616739
}
617740

741+
impl Drop for FunctionLifterContext {
742+
fn drop(&mut self) {
743+
if !self.function.is_null() {
744+
unsafe { BNFreeLowLevelILFunction(self.function) };
745+
}
746+
}
747+
}
748+
618749
// TODO: WTF?!?!?!?
619750
pub struct CoreArchitectureList(*mut *mut BNArchitecture, usize);
620751

@@ -1622,12 +1753,12 @@ where
16221753
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
16231754
{
16241755
let custom_arch = unsafe { &*(ctxt as *mut A) };
1625-
let function = unsafe {
1756+
let llil = unsafe {
16261757
LowLevelILMutableFunction::from_raw_with_arch(function, Some(*custom_arch.as_ref()))
16271758
};
1628-
let mut context: FunctionLifterContext =
1629-
unsafe { FunctionLifterContext::from_raw(context) };
1630-
custom_arch.lift_function(function, &mut context)
1759+
1760+
let mut ctx = unsafe { FunctionLifterContext::from_raw(function, context) };
1761+
custom_arch.lift_function(llil, &mut ctx)
16311762
}
16321763

16331764
extern "C" fn cb_reg_name<A>(ctxt: *mut c_void, reg: u32) -> *mut c_char
@@ -2622,7 +2753,6 @@ where
26222753

26232754
unsafe {
26242755
let res = BNRegisterArchitecture(name.as_ptr(), &mut custom_arch as *mut _);
2625-
26262756
assert!(!res.is_null());
26272757

26282758
(*raw).arch.assume_init_mut()

rust/src/binary_view.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1807,26 +1807,36 @@ impl BinaryView {
18071807
address: u64,
18081808
platform: &Platform,
18091809
) -> Option<Ref<Function>> {
1810-
self.add_auto_function_ext(address, platform, None)
1810+
self.add_auto_function_ext(address, platform, None, false)
18111811
}
18121812

18131813
/// Add an auto function at the given `address` with the `platform` and function type.
18141814
///
1815+
/// The `auto_discovered` flag is used to prevent or allow this created function to be deleted if
1816+
/// it is never used (the function has no xrefs), if you are confident that this is a valid function
1817+
/// set this to `false`.
1818+
///
18151819
/// NOTE: If the view's default platform is not set, this will set it to `platform`.
18161820
pub fn add_auto_function_ext(
18171821
&self,
18181822
address: u64,
18191823
platform: &Platform,
18201824
func_type: Option<&Type>,
1825+
auto_discovered: bool,
18211826
) -> Option<Ref<Function>> {
18221827
unsafe {
18231828
let func_type = match func_type {
18241829
Some(func_type) => func_type.handle,
18251830
None => std::ptr::null_mut(),
18261831
};
18271832

1828-
let handle =
1829-
BNAddFunctionForAnalysis(self.handle, platform.handle, address, true, func_type);
1833+
let handle = BNAddFunctionForAnalysis(
1834+
self.handle,
1835+
platform.handle,
1836+
address,
1837+
auto_discovered,
1838+
func_type,
1839+
);
18301840

18311841
if handle.is_null() {
18321842
return None;

rust/src/function.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,8 @@ impl Function {
514514
let arch = arch.unwrap_or_else(|| self.arch());
515515
let mut result = 0;
516516
unsafe {
517-
BNGetFunctionBlockSortHint(self.handle, arch.handle, addr, &mut result).then_some(result)
517+
BNGetFunctionBlockSortHint(self.handle, arch.handle, addr, &mut result)
518+
.then_some(result)
518519
}
519520
}
520521

rust/src/logger.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,14 @@ impl Logger {
5757
Self::new_with_session(name, LOGGER_DEFAULT_SESSION_ID)
5858
}
5959

60+
pub fn ref_from_raw(handle: *mut BNLogger) -> Ref<Logger> {
61+
unsafe {
62+
Ref::new(Logger {
63+
handle: NonNull::new(handle).unwrap(),
64+
})
65+
}
66+
}
67+
6068
/// Create a logger scoped with the specific [`SessionId`], hiding the logs when the session
6169
/// is not active in the UI.
6270
///

rust/src/low_level_il.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414

1515
use std::borrow::Cow;
16+
use std::collections::HashSet;
1617
use std::fmt;
1718
use std::fmt::{Debug, Display};
1819
// TODO : provide some way to forbid emitting register reads for certain registers
@@ -316,3 +317,42 @@ pub enum VisitorAction {
316317
Sibling,
317318
Halt,
318319
}
320+
321+
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
322+
pub enum ILInstructionAttribute {
323+
ILAllowDeadStoreElimination,
324+
ILPreventDeadStoreElimination,
325+
MLILAssumePossibleUse,
326+
MLILUnknownSize,
327+
SrcInstructionUsesPointerAuth,
328+
ILPreventAliasAnalysis,
329+
ILIsCFGProtected,
330+
MLILPossiblyUnusedIntermediate,
331+
HLILFoldableExpr,
332+
HLILInvertableCondition,
333+
HLILEarlyReturnPossible,
334+
HLILSwitchRecoveryPossible,
335+
ILTransparentCopy,
336+
}
337+
338+
impl ILInstructionAttribute {
339+
pub fn value(&self) -> u32 {
340+
match self {
341+
Self::ILAllowDeadStoreElimination => 1,
342+
Self::ILPreventDeadStoreElimination => 2,
343+
Self::MLILAssumePossibleUse => 4,
344+
Self::MLILUnknownSize => 8,
345+
Self::SrcInstructionUsesPointerAuth => 16,
346+
Self::ILPreventAliasAnalysis => 32,
347+
Self::ILIsCFGProtected => 64,
348+
Self::MLILPossiblyUnusedIntermediate => 128,
349+
Self::HLILFoldableExpr => 256,
350+
Self::HLILInvertableCondition => 512,
351+
Self::HLILEarlyReturnPossible => 1024,
352+
Self::HLILSwitchRecoveryPossible => 2048,
353+
Self::ILTransparentCopy => 4096,
354+
}
355+
}
356+
}
357+
358+
pub type ILInstructionAttributeSet = HashSet<ILInstructionAttribute>;

rust/src/low_level_il/function.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,20 @@ where
209209
Some(unsafe { BasicBlock::ref_from_raw(block, LowLevelILBlock { function: self }) })
210210
}
211211
}
212+
213+
pub fn set_indirect_branches(&self, branches: &[Location]) {
214+
let mut bn_branches: Box<[BNArchitectureAndAddress]> = branches
215+
.iter()
216+
.map(|loc| BNArchitectureAndAddress {
217+
address: loc.addr,
218+
arch: loc.arch.unwrap_or_else(|| self.arch()).handle,
219+
})
220+
.collect();
221+
222+
unsafe {
223+
BNLowLevelILSetIndirectBranches(self.handle, bn_branches.as_mut_ptr(), branches.len());
224+
}
225+
}
212226
}
213227

214228
impl<M: FunctionMutability> LowLevelILFunction<M, NonSSA> {

0 commit comments

Comments
 (0)