diff options
| author | James Johnson <jjohnsonjj1251@gmail.com> | 2025-09-22 19:21:51 -0400 |
|---|---|---|
| committer | Mason Reed <mason@vector35.com> | 2025-10-02 16:52:02 -0400 |
| commit | 20eba1282ff32bb77880a903323cf157b86fea8c (patch) | |
| tree | 43c68712f5488cdedf609d7afe9d30af0475730a /rust | |
| parent | 9d2e83dad8f09208f93a9f09191c6690e2b85fa4 (diff) | |
[Rust] Improvements to LLIL flag and intrinsic operations
Diffstat (limited to 'rust')
| -rw-r--r-- | rust/src/architecture.rs | 14 | ||||
| -rw-r--r-- | rust/src/low_level_il/expression.rs | 5 | ||||
| -rw-r--r-- | rust/src/low_level_il/operation.rs | 96 | ||||
| -rw-r--r-- | rust/tests/low_level_il.rs | 43 |
4 files changed, 149 insertions, 9 deletions
diff --git a/rust/src/architecture.rs b/rust/src/architecture.rs index 0a6e4f82..c7fbe5f1 100644 --- a/rust/src/architecture.rs +++ b/rust/src/architecture.rs @@ -1310,7 +1310,7 @@ impl FlagGroup for CoreFlagGroup { } } -#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[derive(Copy, Clone, Eq, PartialEq)] pub struct CoreIntrinsic { pub arch: CoreArchitecture, pub id: IntrinsicId, @@ -1396,6 +1396,18 @@ impl Intrinsic for CoreIntrinsic { } } +impl Debug for CoreIntrinsic { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CoreIntrinsic") + .field("id", &self.id) + .field("name", &self.name()) + .field("class", &self.class()) + .field("inputs", &self.inputs()) + .field("outputs", &self.outputs()) + .finish() + } +} + // TODO: WTF?!?!?!? pub struct CoreArchitectureList(*mut *mut BNArchitecture, usize); diff --git a/rust/src/low_level_il/expression.rs b/rust/src/low_level_il/expression.rs index ecd850f9..de69e112 100644 --- a/rust/src/low_level_il/expression.rs +++ b/rust/src/low_level_il/expression.rs @@ -98,10 +98,7 @@ where F: FunctionForm, R: ExpressionResultType, { - pub(crate) fn new( - function: &'func LowLevelILFunction<M, F>, - index: LowLevelExpressionIndex, - ) -> Self { + pub fn new(function: &'func LowLevelILFunction<M, F>, index: LowLevelExpressionIndex) -> Self { // TODO: Validate expression here? Self { function, diff --git a/rust/src/low_level_il/operation.rs b/rust/src/low_level_il/operation.rs index e74a8240..2ddb84f2 100644 --- a/rust/src/low_level_il/operation.rs +++ b/rust/src/low_level_il/operation.rs @@ -88,6 +88,22 @@ where }; PossibleValueSet::from_owned_core_raw(raw_pvs) } + + /// Get the raw operand from the operand list. + /// + /// This has no type information associated with it. It's up to the caller to know what the correct type of the + /// underlying u64 should be. + /// + /// # Panic + /// `idx` must be less than 4. This is to protect against an out of bounds access. + /// + /// # Safety + /// Even if `idx` is valid, it may index to an unitialized or unused value. Make sure you index into an operand that + /// you know should be initialized properly. + pub unsafe fn get_operand(&self, idx: usize) -> u64 { + assert!(idx < 4); + self.op.operands[idx] + } } impl<M, O> Operation<'_, M, NonSSA, O> @@ -214,16 +230,69 @@ where // LLIL_INTRINSIC, LLIL_INTRINSIC_SSA pub struct Intrinsic; +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum IntrinsicOutput { + Reg(CoreRegister), + Flag(CoreFlag), +} + +impl From<CoreRegister> for IntrinsicOutput { + fn from(value: CoreRegister) -> Self { + Self::Reg(value) + } +} + +impl From<CoreFlag> for IntrinsicOutput { + fn from(value: CoreFlag) -> Self { + Self::Flag(value) + } +} + impl<M, F> Operation<'_, M, F, Intrinsic> where M: FunctionMutability, F: FunctionForm, { - // TODO: Support register and expression lists pub fn intrinsic(&self) -> Option<CoreIntrinsic> { let raw_id = self.op.operands[2] as u32; self.function.arch().intrinsic_from_id(IntrinsicId(raw_id)) } + + /// Get the output list. + pub fn outputs(&self) -> Vec<IntrinsicOutput> { + // Convert the operand to either a register or flag id. + let operand_to_output = |o: u64| { + if o & (1 << 32) != 0 { + self.function + .arch() + .flag_from_id(FlagId((o & 0xffffffff) as u32)) + .expect("Invalid core flag ID") + .into() + } else { + self.function + .arch() + .register_from_id(RegisterId((o & 0xffffffff) as u32)) + .expect("Invalid register ID") + .into() + } + }; + + self.get_operand_list(0) + .into_iter() + .map(operand_to_output) + .collect::<Vec<_>>() + } + + /// Get the input list for the intrinsic. + /// + /// This will just be a CallParamSsa expression. + #[inline] + pub fn inputs(&self) -> LowLevelILExpression<'_, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[3] as usize), + ) + } } impl<M, F> Debug for Operation<'_, M, F, Intrinsic> @@ -232,9 +301,15 @@ where F: FunctionForm, { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + use crate::architecture::Intrinsic; f.debug_struct("Intrinsic") .field("address", &self.address()) - .field("size", &self.intrinsic()) + .field( + "intrinsic", + &self.intrinsic().expect("Valid intrinsic").name(), + ) + .field("outputs", &self.outputs()) + .field("inputs", &self.inputs()) .finish() } } @@ -1013,13 +1088,28 @@ where // LLIL_FLAG, LLIL_FLAG_SSA pub struct Flag; +impl<M, F> Operation<'_, M, F, Flag> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn source_flag(&self) -> CoreFlag { + self.function + .arch() + .flag_from_id(FlagId(self.op.operands[0] as u32)) + .expect("Bad flag ID") + } +} + impl<M, F> Debug for Operation<'_, M, F, Flag> where M: FunctionMutability, F: FunctionForm, { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("Flag").finish() + f.debug_struct("Flag") + .field("source_flag", &self.source_flag()) + .finish() } } diff --git a/rust/tests/low_level_il.rs b/rust/tests/low_level_il.rs index db8cc549..84ae3247 100644 --- a/rust/tests/low_level_il.rs +++ b/rust/tests/low_level_il.rs @@ -1,4 +1,4 @@ -use binaryninja::architecture::Register; +use binaryninja::architecture::{ArchitectureExt, Intrinsic, Register}; use binaryninja::binary_view::BinaryViewExt; use binaryninja::headless::Session; use binaryninja::low_level_il::expression::{ @@ -7,6 +7,7 @@ use binaryninja::low_level_il::expression::{ use binaryninja::low_level_il::instruction::{ InstructionHandler, LowLevelILInstructionKind, LowLevelInstructionIndex, }; +use binaryninja::low_level_il::operation::IntrinsicOutput; use binaryninja::low_level_il::{LowLevelILRegisterKind, LowLevelILSSARegisterKind, VisitorAction}; use std::path::PathBuf; @@ -308,3 +309,43 @@ fn test_llil_ssa() { _ => panic!("Expected CallSsa"), } } + +#[test] +fn test_llil_intrinsic() { + let _session = Session::new().expect("Failed to initialize session"); + let out_dir = env!("OUT_DIR").parse::<PathBuf>().unwrap(); + let view = binaryninja::load(out_dir.join("atof.obj")).expect("Failed to create view"); + let image_base = view.original_image_base(); + let platform = view.default_platform().unwrap(); + let arch = platform.arch(); + + // Sample function: __crt_strtox::bit_scan_reverse + let sample_function = view + .function_at(&platform, image_base + 0x00037310) + .unwrap(); + let llil_function = sample_function.low_level_il().unwrap(); + + // 5 @ 0004731d (LLIL_INTRINSIC eax, eflags = __bsr_gprv_memv((LLIL_LOAD.d [(LLIL_ADD.d (LLIL_REG.d ebp) + (LLIL_CONST.d 8)) {arg1}].d))) + let instr_5 = llil_function + .instruction_from_index(LowLevelInstructionIndex(5)) + .expect("Valid instruction"); + assert_eq!(instr_5.address(), image_base + 0x0003731d); + println!("{:?}", instr_5); + println!("{:#?}", instr_5.kind()); + match instr_5.kind() { + LowLevelILInstructionKind::Intrinsic(op) => { + assert_eq!(op.intrinsic().unwrap().name(), "__bsr_gprv_memv"); + assert_eq!(op.outputs().len(), 2); + let reg_out_0 = arch.register_by_name("eax").unwrap(); + let reg_out_1 = arch.register_by_name("eflags").unwrap(); + assert_eq!( + op.outputs(), + vec![ + IntrinsicOutput::Reg(reg_out_0), + IntrinsicOutput::Reg(reg_out_1), + ] + ); + } + _ => panic!("Expected Intrinsic"), + } +} |
