Skip to content

Commit 26cbd99

Browse files
committed
Rust APIs for custom function lifters
1 parent 39201de commit 26cbd99

8 files changed

Lines changed: 274 additions & 16 deletions

File tree

rust/src/architecture.rs

Lines changed: 138 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,15 @@ use crate::{
2727
data_buffer::DataBuffer,
2828
disassembly::InstructionTextToken,
2929
ffi::INVALID_REGISTER,
30-
function::Function,
30+
function::{Function, Location, NativeBlock},
3131
platform::Platform,
3232
rc::*,
3333
relocation::CoreRelocationHandler,
3434
string::{IntoCStr, *},
3535
types::{NameAndType, Type},
3636
Endianness,
3737
};
38+
use std::collections::{HashMap, HashSet};
3839
use std::ops::Deref;
3940
use std::{
4041
borrow::Borrow,
@@ -48,7 +49,9 @@ use std::ptr::NonNull;
4849
use crate::function_recognizer::FunctionRecognizer;
4950
use crate::relocation::{CustomRelocationHandlerHandle, RelocationHandler};
5051

52+
use crate::basic_block::BasicBlock;
5153
use crate::confidence::Conf;
54+
use crate::logger::Logger;
5255
use crate::low_level_il::expression::ValueExpr;
5356
use crate::low_level_il::lifting::{
5457
get_default_flag_cond_llil, get_default_flag_write_llil, LowLevelILFlagWriteOp,
@@ -592,13 +595,133 @@ pub trait ArchitectureWithFunctionContext: Architecture {
592595

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

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

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

604727
pub fn get_function_arch_context<A: ArchitectureWithFunctionContext>(
@@ -616,6 +739,14 @@ impl FunctionLifterContext {
616739
}
617740
}
618741

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

@@ -1630,12 +1761,12 @@ where
16301761
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
16311762
{
16321763
let custom_arch = unsafe { &*(ctxt as *mut A) };
1633-
let function = unsafe {
1764+
let llil = unsafe {
16341765
LowLevelILMutableFunction::from_raw_with_arch(function, Some(*custom_arch.as_ref()))
16351766
};
1636-
let mut context: FunctionLifterContext =
1637-
unsafe { FunctionLifterContext::from_raw(context) };
1638-
custom_arch.lift_function(function, &mut context)
1767+
1768+
let mut ctx = unsafe { FunctionLifterContext::from_raw(function, context) };
1769+
custom_arch.lift_function(llil, &mut ctx)
16391770
}
16401771

16411772
extern "C" fn cb_reg_name<A>(ctxt: *mut c_void, reg: u32) -> *mut c_char
@@ -2630,7 +2761,6 @@ where
26302761

26312762
unsafe {
26322763
let res = BNRegisterArchitecture(name.as_ptr(), &mut custom_arch as *mut _);
2633-
26342764
assert!(!res.is_null());
26352765

26362766
(*raw).arch.assume_init_mut()

rust/src/binary_view.rs

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

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

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

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

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> {

rust/src/low_level_il/lifting.rs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,19 @@
1414

1515
use std::marker::PhantomData;
1616

17-
use binaryninjacore_sys::{BNAddLowLevelILLabelForAddress, BNLowLevelILOperation};
18-
use binaryninjacore_sys::{BNLowLevelILLabel, BNRegisterOrConstant};
17+
use binaryninjacore_sys::{
18+
BNAddLowLevelILLabelForAddress, BNLowLevelILClearIndirectBranches, BNLowLevelILLabel,
19+
BNLowLevelILOperation, BNRegisterOrConstant, BNSetLowLevelILExprAttributes,
20+
};
1921

2022
use super::*;
2123
use crate::architecture::{Architecture, FlagWriteId, RegisterId};
2224
use crate::architecture::{CoreRegister, Register as ArchReg};
2325
use crate::architecture::{
2426
Flag, FlagClass, FlagCondition, FlagGroup, FlagRole, FlagWrite, Intrinsic,
2527
};
26-
use crate::function::Location;
28+
use crate::basic_block::BasicBlock;
29+
use crate::function::{Location, NativeBlock};
2730

2831
pub trait LiftableLowLevelIL<'func> {
2932
type Result: ExpressionResultType;
@@ -1512,6 +1515,13 @@ impl LowLevelILMutableFunction {
15121515
}
15131516
}
15141517

1518+
pub fn set_current_source_block(&self, source: &BasicBlock<NativeBlock>) {
1519+
use binaryninjacore_sys::BNLowLevelILSetCurrentSourceBlock;
1520+
unsafe {
1521+
BNLowLevelILSetCurrentSourceBlock(self.handle, source.handle);
1522+
}
1523+
}
1524+
15151525
pub fn label_for_address<L: Into<Location>>(&self, loc: L) -> Option<LowLevelILLabel> {
15161526
use binaryninjacore_sys::BNGetLowLevelILLabelForAddress;
15171527

@@ -1561,6 +1571,25 @@ impl LowLevelILMutableFunction {
15611571
}
15621572
*label = new_label;
15631573
}
1574+
1575+
pub fn set_expr_attributes(
1576+
&self,
1577+
expr: LowLevelExpressionIndex,
1578+
value: &ILInstructionAttributeSet,
1579+
) {
1580+
let mut result = 0u32;
1581+
for flag in value {
1582+
result |= flag.value();
1583+
}
1584+
1585+
unsafe {
1586+
BNSetLowLevelILExprAttributes(self.handle, expr.0, result);
1587+
}
1588+
}
1589+
1590+
pub fn clear_indirect_branches(&self) {
1591+
unsafe { BNLowLevelILClearIndirectBranches(self.handle) };
1592+
}
15641593
}
15651594

15661595
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]

0 commit comments

Comments
 (0)