// Copyright 2021-2026 Vector 35 Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use std::marker::PhantomData; use binaryninjacore_sys::{BNAddLowLevelILLabelForAddress, BNLowLevelILOperation}; use binaryninjacore_sys::{BNLowLevelILLabel, BNRegisterOrConstant}; use super::*; use crate::architecture::{Architecture, FlagWriteId, RegisterId}; use crate::architecture::{CoreRegister, Register as ArchReg}; use crate::architecture::{ Flag, FlagClass, FlagCondition, FlagGroup, FlagRole, FlagWrite, Intrinsic, }; use crate::function::Location; pub trait LiftableLowLevelIL<'func> { type Result: ExpressionResultType; fn lift( il: &'func LowLevelILMutableFunction, expr: Self, ) -> LowLevelILMutableExpression<'func, Self::Result>; } pub trait LiftableLowLevelILWithSize<'func>: LiftableLowLevelIL<'func, Result = ValueExpr> { fn lift_with_size( il: &'func LowLevelILMutableFunction, expr: Self, size: usize, ) -> LowLevelILMutableExpression<'func, ValueExpr>; } #[derive(Copy, Clone)] pub enum LowLevelILRegisterOrConstant { Register(usize, LowLevelILRegisterKind), Constant(usize, u64), } impl From> for BNRegisterOrConstant { fn from(value: LowLevelILRegisterOrConstant) -> Self { match value { LowLevelILRegisterOrConstant::Register(_, r) => Self { constant: false, reg: r.id().0, value: 0, }, LowLevelILRegisterOrConstant::Constant(_, value) => Self { constant: true, reg: 0, value, }, } } } // TODO flesh way out #[derive(Copy, Clone)] pub enum LowLevelILFlagWriteOp { SetReg(usize, LowLevelILRegisterOrConstant), SetRegSplit( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), Sub( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), Add( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), Load(usize, LowLevelILRegisterOrConstant), Push(usize, LowLevelILRegisterOrConstant), Neg(usize, LowLevelILRegisterOrConstant), Not(usize, LowLevelILRegisterOrConstant), Sx(usize, LowLevelILRegisterOrConstant), Zx(usize, LowLevelILRegisterOrConstant), LowPart(usize, LowLevelILRegisterOrConstant), BoolToInt(usize, LowLevelILRegisterOrConstant), FloatToInt(usize, LowLevelILRegisterOrConstant), Store( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), And( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), Or( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), Xor( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), Lsl( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), Lsr( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), Asr( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), Rol( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), Ror( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), Mul( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), MuluDp( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), MulsDp( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), Divu( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), Divs( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), Modu( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), Mods( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), DivuDp( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), DivsDp( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), ModuDp( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), ModsDp( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), TestBit( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), AddOverflow( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), Adc( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), Sbb( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), Rlc( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), Rrc( usize, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, LowLevelILRegisterOrConstant, ), Pop(usize), // TODO: floating point stuff, llil comparison ops that set flags, intrinsics } impl LowLevelILFlagWriteOp { pub(crate) fn from_op( arch: &A, size: usize, op: BNLowLevelILOperation, operands: &[BNRegisterOrConstant], ) -> Option where A: Architecture, R: ArchReg, { use self::LowLevelILFlagWriteOp::*; use binaryninjacore_sys::BNLowLevelILOperation::*; fn build_op( arch: &A, size: usize, operand: &BNRegisterOrConstant, ) -> LowLevelILRegisterOrConstant where A: Architecture, R: ArchReg, { if operand.constant { LowLevelILRegisterOrConstant::Constant(size, operand.value) } else { let raw_id = RegisterId(operand.reg); let il_reg = LowLevelILRegisterKind::from_raw(arch, raw_id).expect("Bad register ID"); LowLevelILRegisterOrConstant::Register(size, il_reg) } } macro_rules! op { ($x:ident, $($ops:expr),*) => { ( $x(size, $( build_op(arch, size, &operands[$ops]), )* ) ) }; } Some(match (operands.len(), op) { (1, LLIL_SET_REG) => op!(SetReg, 0), (2, LLIL_SET_REG_SPLIT) => op!(SetRegSplit, 0, 1), (2, LLIL_SUB) => op!(Sub, 0, 1), (2, LLIL_ADD) => op!(Add, 0, 1), (1, LLIL_LOAD) => op!(Load, 0), (1, LLIL_PUSH) => op!(Push, 0), (1, LLIL_NEG) => op!(Neg, 0), (1, LLIL_NOT) => op!(Not, 0), (1, LLIL_SX) => op!(Sx, 0), (1, LLIL_ZX) => op!(Zx, 0), (1, LLIL_LOW_PART) => op!(LowPart, 0), (1, LLIL_BOOL_TO_INT) => op!(BoolToInt, 0), (1, LLIL_FLOAT_TO_INT) => op!(FloatToInt, 0), (2, LLIL_STORE) => op!(Store, 0, 1), (2, LLIL_AND) => op!(And, 0, 1), (2, LLIL_OR) => op!(Or, 0, 1), (2, LLIL_XOR) => op!(Xor, 0, 1), (2, LLIL_LSL) => op!(Lsl, 0, 1), (2, LLIL_LSR) => op!(Lsr, 0, 1), (2, LLIL_ASR) => op!(Asr, 0, 1), (2, LLIL_ROL) => op!(Rol, 0, 1), (2, LLIL_ROR) => op!(Ror, 0, 1), (2, LLIL_MUL) => op!(Mul, 0, 1), (2, LLIL_MULU_DP) => op!(MuluDp, 0, 1), (2, LLIL_MULS_DP) => op!(MulsDp, 0, 1), (2, LLIL_DIVU) => op!(Divu, 0, 1), (2, LLIL_DIVS) => op!(Divs, 0, 1), (2, LLIL_MODU) => op!(Modu, 0, 1), (2, LLIL_MODS) => op!(Mods, 0, 1), (2, LLIL_DIVU_DP) => op!(DivuDp, 0, 1), (2, LLIL_DIVS_DP) => op!(DivsDp, 0, 1), (2, LLIL_MODU_DP) => op!(ModuDp, 0, 1), (2, LLIL_MODS_DP) => op!(ModsDp, 0, 1), (2, LLIL_TEST_BIT) => op!(TestBit, 0, 1), (2, LLIL_ADD_OVERFLOW) => op!(AddOverflow, 0, 1), (3, LLIL_ADC) => op!(Adc, 0, 1, 2), (3, LLIL_SBB) => op!(Sbb, 0, 1, 2), (3, LLIL_RLC) => op!(Rlc, 0, 1, 2), (3, LLIL_RRC) => op!(Rrc, 0, 1, 2), (0, LLIL_POP) => op!(Pop,), _ => return None, }) } pub(crate) fn size_and_op(&self) -> (usize, BNLowLevelILOperation) { use self::LowLevelILFlagWriteOp::*; use binaryninjacore_sys::BNLowLevelILOperation::*; match *self { SetReg(size, ..) => (size, LLIL_SET_REG), SetRegSplit(size, ..) => (size, LLIL_SET_REG_SPLIT), Sub(size, ..) => (size, LLIL_SUB), Add(size, ..) => (size, LLIL_ADD), Load(size, ..) => (size, LLIL_LOAD), Push(size, ..) => (size, LLIL_PUSH), Neg(size, ..) => (size, LLIL_NEG), Not(size, ..) => (size, LLIL_NOT), Sx(size, ..) => (size, LLIL_SX), Zx(size, ..) => (size, LLIL_ZX), LowPart(size, ..) => (size, LLIL_LOW_PART), BoolToInt(size, ..) => (size, LLIL_BOOL_TO_INT), FloatToInt(size, ..) => (size, LLIL_FLOAT_TO_INT), Store(size, ..) => (size, LLIL_STORE), And(size, ..) => (size, LLIL_AND), Or(size, ..) => (size, LLIL_OR), Xor(size, ..) => (size, LLIL_XOR), Lsl(size, ..) => (size, LLIL_LSL), Lsr(size, ..) => (size, LLIL_LSR), Asr(size, ..) => (size, LLIL_ASR), Rol(size, ..) => (size, LLIL_ROL), Ror(size, ..) => (size, LLIL_ROR), Mul(size, ..) => (size, LLIL_MUL), MuluDp(size, ..) => (size, LLIL_MULU_DP), MulsDp(size, ..) => (size, LLIL_MULS_DP), Divu(size, ..) => (size, LLIL_DIVU), Divs(size, ..) => (size, LLIL_DIVS), Modu(size, ..) => (size, LLIL_MODU), Mods(size, ..) => (size, LLIL_MODS), DivuDp(size, ..) => (size, LLIL_DIVU_DP), DivsDp(size, ..) => (size, LLIL_DIVS_DP), ModuDp(size, ..) => (size, LLIL_MODU_DP), ModsDp(size, ..) => (size, LLIL_MODS_DP), TestBit(size, ..) => (size, LLIL_TEST_BIT), AddOverflow(size, ..) => (size, LLIL_ADD_OVERFLOW), Adc(size, ..) => (size, LLIL_ADC), Sbb(size, ..) => (size, LLIL_SBB), Rlc(size, ..) => (size, LLIL_RLC), Rrc(size, ..) => (size, LLIL_RRC), Pop(size) => (size, LLIL_POP), } } pub(crate) fn raw_operands(&self) -> (usize, [BNRegisterOrConstant; 5]) { use self::LowLevelILFlagWriteOp::*; let mut operands: [BNRegisterOrConstant; 5] = [BNRegisterOrConstant::default(); 5]; let count = match *self { Pop(_) => 0, SetReg(_, op0) | Load(_, op0) | Push(_, op0) | Neg(_, op0) | Not(_, op0) | Sx(_, op0) | Zx(_, op0) | LowPart(_, op0) | BoolToInt(_, op0) | FloatToInt(_, op0) => { operands[0] = op0.into(); 1 } SetRegSplit(_, op0, op1) | Sub(_, op0, op1) | Add(_, op0, op1) | Store(_, op0, op1) | And(_, op0, op1) | Or(_, op0, op1) | Xor(_, op0, op1) | Lsl(_, op0, op1) | Lsr(_, op0, op1) | Asr(_, op0, op1) | Rol(_, op0, op1) | Ror(_, op0, op1) | Mul(_, op0, op1) | MuluDp(_, op0, op1) | MulsDp(_, op0, op1) | Divu(_, op0, op1) | Divs(_, op0, op1) | Modu(_, op0, op1) | Mods(_, op0, op1) | DivuDp(_, op0, op1) | DivsDp(_, op0, op1) | ModuDp(_, op0, op1) | ModsDp(_, op0, op1) | TestBit(_, op0, op1) | AddOverflow(_, op0, op1) => { operands[0] = op0.into(); operands[1] = op1.into(); 2 } Adc(_, op0, op1, op2) | Sbb(_, op0, op1, op2) | Rlc(_, op0, op1, op2) | Rrc(_, op0, op1, op2) => { operands[0] = op0.into(); operands[1] = op1.into(); operands[2] = op2.into(); 3 } }; (count, operands) } } pub fn get_default_flag_write_llil<'func, A>( arch: &A, role: FlagRole, op: LowLevelILFlagWriteOp, il: &'func LowLevelILMutableFunction, ) -> LowLevelILMutableExpression<'func, ValueExpr> where A: 'func + Architecture, { let (size, operation) = op.size_and_op(); let (count, operands) = op.raw_operands(); let expr_idx = unsafe { use binaryninjacore_sys::BNGetDefaultArchitectureFlagWriteLowLevelIL; BNGetDefaultArchitectureFlagWriteLowLevelIL( arch.as_ref().handle, operation, size, role, operands.as_ptr() as *mut _, count, il.handle, ) }; LowLevelILExpression::new(il, LowLevelExpressionIndex(expr_idx)) } pub fn get_default_flag_cond_llil<'func, A>( arch: &A, cond: FlagCondition, class: Option, il: &'func LowLevelILMutableFunction, ) -> LowLevelILMutableExpression<'func, ValueExpr> where A: 'func + Architecture, { use binaryninjacore_sys::BNGetDefaultArchitectureFlagConditionLowLevelIL; let class_id = class.map(|c| c.id().0).unwrap_or(0); unsafe { let expr_idx = BNGetDefaultArchitectureFlagConditionLowLevelIL( arch.as_ref().handle, cond, class_id, il.handle, ); LowLevelILExpression::new(il, LowLevelExpressionIndex(expr_idx)) } } macro_rules! prim_int_lifter { ($x:ty) => { impl<'a> LiftableLowLevelIL<'a> for $x { type Result = ValueExpr; fn lift(il: &'a LowLevelILMutableFunction, val: Self) -> LowLevelILMutableExpression<'a, Self::Result> { il.const_int(std::mem::size_of::(), val as i64 as u64) } } impl<'a> LiftableLowLevelILWithSize<'a> for $x { fn lift_with_size(il: &'a LowLevelILMutableFunction, val: Self, size: usize) -> LowLevelILMutableExpression<'a, ValueExpr> { let raw = val as i64; #[cfg(debug_assertions)] { let is_safe = match raw.overflowing_shr(size as u32 * 8) { (_, true) => true, (res, false) => [-1, 0].contains(&res), }; if !is_safe { tracing::error!("il @ {:x} attempted to lift constant 0x{:x} as {} byte expr (won't fit!)", il.current_address(), val, size); } } il.const_int(size, raw as u64) } } } } prim_int_lifter!(i8); prim_int_lifter!(i16); prim_int_lifter!(i32); prim_int_lifter!(i64); prim_int_lifter!(u8); prim_int_lifter!(u16); prim_int_lifter!(u32); prim_int_lifter!(u64); impl<'a, R> LiftableLowLevelIL<'a> for LowLevelILRegisterKind where R: LiftableLowLevelIL<'a, Result = ValueExpr> + Into> + ArchReg, { type Result = ValueExpr; fn lift( il: &'a LowLevelILMutableFunction, reg: Self, ) -> LowLevelILMutableExpression<'a, Self::Result> { match reg { LowLevelILRegisterKind::Arch(r) => R::lift(il, r), LowLevelILRegisterKind::Temp(t) => il.reg( il.arch().default_integer_size(), LowLevelILRegisterKind::Temp::(t), ), } } } impl<'a, R> LiftableLowLevelILWithSize<'a> for LowLevelILRegisterKind where R: LiftableLowLevelILWithSize<'a> + Into> + ArchReg, { fn lift_with_size( il: &'a LowLevelILMutableFunction, reg: Self, size: usize, ) -> LowLevelILMutableExpression<'a, ValueExpr> { match reg { LowLevelILRegisterKind::Arch(r) => R::lift_with_size(il, r, size), LowLevelILRegisterKind::Temp(t) => il.reg(size, LowLevelILRegisterKind::::Temp(t)), } } } impl<'a, R> LiftableLowLevelIL<'a> for LowLevelILRegisterOrConstant where R: LiftableLowLevelILWithSize<'a, Result = ValueExpr> + Into> + ArchReg, { type Result = ValueExpr; fn lift( il: &'a LowLevelILMutableFunction, reg: Self, ) -> LowLevelILMutableExpression<'a, Self::Result> { match reg { LowLevelILRegisterOrConstant::Register(size, r) => { LowLevelILRegisterKind::::lift_with_size(il, r, size) } LowLevelILRegisterOrConstant::Constant(size, value) => { u64::lift_with_size(il, value, size) } } } } impl<'a, R> LiftableLowLevelILWithSize<'a> for LowLevelILRegisterOrConstant where R: LiftableLowLevelILWithSize<'a> + Into> + ArchReg, { fn lift_with_size( il: &'a LowLevelILMutableFunction, reg: Self, size: usize, ) -> LowLevelILMutableExpression<'a, ValueExpr> { // TODO ensure requested size is compatible with size of this constant match reg { LowLevelILRegisterOrConstant::Register(_, r) => { LowLevelILRegisterKind::::lift_with_size(il, r, size) } LowLevelILRegisterOrConstant::Constant(_, value) => { u64::lift_with_size(il, value, size) } } } } impl<'a, R> LiftableLowLevelIL<'a> for LowLevelILExpression<'a, Mutable, NonSSA, R> where R: ExpressionResultType, { type Result = R; fn lift( il: &'a LowLevelILMutableFunction, expr: Self, ) -> LowLevelILMutableExpression<'a, Self::Result> { debug_assert!(expr.function.handle == il.handle); expr } } impl<'a> LiftableLowLevelILWithSize<'a> for LowLevelILExpression<'a, Mutable, NonSSA, ValueExpr> { fn lift_with_size( il: &'a LowLevelILMutableFunction, expr: Self, _size: usize, ) -> LowLevelILMutableExpression<'a, Self::Result> { #[cfg(debug_assertions)] { use crate::low_level_il::ExpressionHandler; if let Some(expr_size) = expr.kind().size() { if expr_size != _size { tracing::warn!( "il @ {:x} attempted to lift {} byte expression as {} bytes", il.current_address(), expr_size, _size ); } } } LiftableLowLevelIL::lift(il, expr) } } impl LowLevelILExpression<'_, Mutable, NonSSA, R> where R: ExpressionResultType, { pub fn with_source_operand(self, op: u32) -> Self { use binaryninjacore_sys::BNLowLevelILSetExprSourceOperand; unsafe { BNLowLevelILSetExprSourceOperand(self.function.handle, self.index.0, op) } self } pub fn append(self) { self.function.add_instruction(self); } } pub struct ExpressionBuilder<'func, R> where R: ExpressionResultType, { function: &'func LowLevelILFunction, op: BNLowLevelILOperation, size: usize, flag_write: FlagWriteId, op1: u64, op2: u64, op3: u64, op4: u64, _ty: PhantomData, } impl<'a, R> ExpressionBuilder<'a, R> where R: ExpressionResultType, { pub fn from_expr(expr: LowLevelILExpression<'a, Mutable, NonSSA, R>) -> Self { use binaryninjacore_sys::BNGetLowLevelILByIndex; let instr = unsafe { BNGetLowLevelILByIndex(expr.function.handle, expr.index.0) }; ExpressionBuilder { function: expr.function, op: instr.operation, size: instr.size, flag_write: FlagWriteId(instr.flags), op1: instr.operands[0], op2: instr.operands[1], op3: instr.operands[2], op4: instr.operands[3], _ty: PhantomData, } } pub fn with_flag_write(mut self, flag_write: impl FlagWrite) -> Self { // TODO verify valid id self.flag_write = flag_write.id(); self } pub fn build(self) -> LowLevelILExpression<'a, Mutable, NonSSA, R> { use binaryninjacore_sys::BNLowLevelILAddExpr; let expr_idx = unsafe { BNLowLevelILAddExpr( self.function.handle, self.op, self.size, self.flag_write.0, self.op1, self.op2, self.op3, self.op4, ) }; LowLevelILExpression::new(self.function, LowLevelExpressionIndex(expr_idx)) } pub fn with_source_operand(self, op: u32) -> LowLevelILExpression<'a, Mutable, NonSSA, R> { self.build().with_source_operand(op) } pub fn append(self) { let expr = self.build(); expr.function.add_instruction(expr); } } impl<'a, R> LiftableLowLevelIL<'a> for ExpressionBuilder<'a, R> where R: ExpressionResultType, { type Result = R; fn lift( il: &'a LowLevelILMutableFunction, expr: Self, ) -> LowLevelILMutableExpression<'a, Self::Result> { debug_assert!(expr.function.handle == il.handle); expr.build() } } impl<'a> LiftableLowLevelILWithSize<'a> for ExpressionBuilder<'a, ValueExpr> { fn lift_with_size( il: &'a LowLevelILMutableFunction, expr: Self, _size: usize, ) -> LowLevelILMutableExpression<'a, ValueExpr> { #[cfg(debug_assertions)] { use binaryninjacore_sys::BNLowLevelILOperation::{LLIL_UNIMPL, LLIL_UNIMPL_MEM}; if expr.size != _size && ![LLIL_UNIMPL, LLIL_UNIMPL_MEM].contains(&expr.op) { tracing::warn!( "il @ {:x} attempted to lift {} byte expression builder as {} bytes", il.current_address(), expr.size, _size ); } } LiftableLowLevelIL::lift(il, expr) } } macro_rules! no_arg_lifter { ($name:ident, $op:ident, $result:ty) => { pub fn $name(&self) -> LowLevelILExpression<'_, Mutable, NonSSA, $result> { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::$op; let expr_idx = unsafe { BNLowLevelILAddExpr(self.handle, $op, 0, 0, 0, 0, 0, 0) }; LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx)) } }; } macro_rules! sized_no_arg_lifter { ($name:ident, $op:ident, $result:ty) => { pub fn $name(&self, size: usize) -> ExpressionBuilder<'_, $result> { use binaryninjacore_sys::BNLowLevelILOperation::$op; ExpressionBuilder { function: self, op: $op, size, flag_write: FlagWriteId(0), op1: 0, op2: 0, op3: 0, op4: 0, _ty: PhantomData, } } }; } macro_rules! unsized_unary_op_lifter { ($name:ident, $op:ident, $result:ty) => { pub fn $name<'a, E>(&'a self, expr: E) -> LowLevelILExpression<'a, Mutable, NonSSA, $result> where E: LiftableLowLevelIL<'a, Result = ValueExpr>, { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::$op; let expr = E::lift(self, expr); let expr_idx = unsafe { BNLowLevelILAddExpr(self.handle, $op, 0, 0, expr.index.0 as u64, 0, 0, 0) }; LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx)) } }; } macro_rules! sized_unary_op_lifter { ($name:ident, $op:ident, $result:ty) => { pub fn $name<'a, E>(&'a self, size: usize, expr: E) -> ExpressionBuilder<'a, $result> where E: LiftableLowLevelILWithSize<'a>, { use binaryninjacore_sys::BNLowLevelILOperation::$op; let expr = E::lift_with_size(self, expr, size); ExpressionBuilder { function: self, op: $op, size, flag_write: FlagWriteId(0), op1: expr.index.0 as u64, op2: 0, op3: 0, op4: 0, _ty: PhantomData, } } }; } macro_rules! size_changing_unary_op_lifter { ($name:ident, $op:ident, $result:ty) => { pub fn $name<'a, E>(&'a self, size: usize, expr: E) -> ExpressionBuilder<'a, $result> where E: LiftableLowLevelILWithSize<'a>, { use binaryninjacore_sys::BNLowLevelILOperation::$op; let expr = E::lift(self, expr); ExpressionBuilder { function: self, op: $op, size, flag_write: FlagWriteId(0), op1: expr.index.0 as u64, op2: 0, op3: 0, op4: 0, _ty: PhantomData, } } }; } macro_rules! binary_op_lifter { ($name:ident, $op:ident) => { pub fn $name<'a, L, R>( &'a self, size: usize, left: L, right: R, ) -> ExpressionBuilder<'a, ValueExpr> where L: LiftableLowLevelILWithSize<'a>, R: LiftableLowLevelILWithSize<'a>, { use binaryninjacore_sys::BNLowLevelILOperation::$op; let left = L::lift_with_size(self, left, size); let right = R::lift_with_size(self, right, size); ExpressionBuilder { function: self, op: $op, size, flag_write: FlagWriteId(0), op1: left.index.0 as u64, op2: right.index.0 as u64, op3: 0, op4: 0, _ty: PhantomData, } } }; } macro_rules! binary_op_carry_lifter { ($name:ident, $op:ident) => { pub fn $name<'a, L, R, C>( &'a self, size: usize, left: L, right: R, carry: C, ) -> ExpressionBuilder<'a, ValueExpr> where L: LiftableLowLevelILWithSize<'a>, R: LiftableLowLevelILWithSize<'a>, C: LiftableLowLevelILWithSize<'a>, { use binaryninjacore_sys::BNLowLevelILOperation::$op; let left = L::lift_with_size(self, left, size); let right = R::lift_with_size(self, right, size); let carry = C::lift_with_size(self, carry, 0); ExpressionBuilder { function: self, op: $op, size, flag_write: FlagWriteId(0), op1: left.index.0 as u64, op2: right.index.0 as u64, op3: carry.index.0 as u64, op4: 0, _ty: PhantomData, } } }; } impl LowLevelILMutableFunction { pub const NO_INPUTS: [ExpressionBuilder<'static, ValueExpr>; 0] = []; pub const NO_OUTPUTS: [LowLevelILRegisterKind; 0] = []; pub fn expression<'a, E: LiftableLowLevelIL<'a>>( &'a self, expr: E, ) -> LowLevelILExpression<'a, Mutable, NonSSA, E::Result> { E::lift(self, expr) } pub fn add_instruction<'a, E: LiftableLowLevelIL<'a>>(&'a self, expr: E) { let expr = self.expression(expr); unsafe { use binaryninjacore_sys::BNLowLevelILAddInstruction; BNLowLevelILAddInstruction(self.handle, expr.index.0); } } pub unsafe fn replace_expression<'a, E: LiftableLowLevelIL<'a>>( &'a self, replaced_expr_index: LowLevelExpressionIndex, replacement: E, ) -> bool { use binaryninjacore_sys::BNReplaceLowLevelILExpr; if replaced_expr_index.0 >= self.expression_count() { // Invalid expression index, cant replace expression. return false; } let expr = self.expression(replacement); BNReplaceLowLevelILExpr(self.handle, replaced_expr_index.0, expr.index.0); true } pub fn const_int(&self, size: usize, val: u64) -> LowLevelILMutableExpression<'_, ValueExpr> { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::LLIL_CONST; let expr_idx = unsafe { BNLowLevelILAddExpr(self.handle, LLIL_CONST, size, 0, val, 0, 0, 0) }; LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx)) } pub fn const_ptr_sized( &self, size: usize, val: u64, ) -> LowLevelILMutableExpression<'_, ValueExpr> { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::LLIL_CONST_PTR; let expr_idx = unsafe { BNLowLevelILAddExpr(self.handle, LLIL_CONST_PTR, size, 0, val, 0, 0, 0) }; LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx)) } pub fn const_ptr(&self, val: u64) -> LowLevelILMutableExpression<'_, ValueExpr> { self.const_ptr_sized(self.arch().address_size(), val) } pub fn trap(&self, val: u64) -> LowLevelILExpression<'_, Mutable, NonSSA, VoidExpr> { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::LLIL_TRAP; let expr_idx = unsafe { BNLowLevelILAddExpr(self.handle, LLIL_TRAP, 0, 0, val, 0, 0, 0) }; LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx)) } no_arg_lifter!(unimplemented, LLIL_UNIMPL, ValueExpr); no_arg_lifter!(undefined, LLIL_UNDEF, ValueExpr); no_arg_lifter!(nop, LLIL_NOP, VoidExpr); no_arg_lifter!(no_ret, LLIL_NORET, VoidExpr); no_arg_lifter!(syscall, LLIL_SYSCALL, VoidExpr); no_arg_lifter!(bp, LLIL_BP, VoidExpr); unsized_unary_op_lifter!(call, LLIL_CALL, VoidExpr); unsized_unary_op_lifter!(tailcall, LLIL_TAILCALL, VoidExpr); unsized_unary_op_lifter!(ret, LLIL_RET, VoidExpr); unsized_unary_op_lifter!(jump, LLIL_JUMP, VoidExpr); // TODO: LLIL_JUMP_TO pub fn if_expr<'a: 'b, 'b, C>( &'a self, cond: C, true_label: &'b mut LowLevelILLabel, false_label: &'b mut LowLevelILLabel, ) -> LowLevelILExpression<'a, Mutable, NonSSA, VoidExpr> where C: LiftableLowLevelIL<'b, Result = ValueExpr>, { use binaryninjacore_sys::BNLowLevelILIf; let cond = C::lift(self, cond); let mut raw_true_label = BNLowLevelILLabel::from(*true_label); let mut raw_false_label = BNLowLevelILLabel::from(*false_label); let expr_idx = unsafe { BNLowLevelILIf( self.handle, cond.index.0 as u64, &mut raw_true_label, &mut raw_false_label, ) }; // Update the labels after they have been resolved. let mut new_true_label = LowLevelILLabel::from(raw_true_label); let mut new_false_label = LowLevelILLabel::from(raw_false_label); if let Some(location) = true_label.location { new_true_label.location = Some(location); self.update_label_map_for_label(&new_true_label); } if let Some(location) = false_label.location { new_false_label.location = Some(location); self.update_label_map_for_label(&new_false_label); } *true_label = new_true_label; *false_label = new_false_label; LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx)) } // TODO: Wtf are these lifetimes?? pub fn goto<'a: 'b, 'b>( &'a self, label: &'b mut LowLevelILLabel, ) -> LowLevelILExpression<'a, Mutable, NonSSA, VoidExpr> { use binaryninjacore_sys::BNLowLevelILGoto; let mut raw_label = BNLowLevelILLabel::from(*label); let expr_idx = unsafe { BNLowLevelILGoto(self.handle, &mut raw_label) }; // Update the labels after they have been resolved. let mut new_label = LowLevelILLabel::from(raw_label); if let Some(location) = label.location { new_label.location = Some(location); self.update_label_map_for_label(&new_label); } *label = new_label; LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx)) } pub fn reg>>( &self, size: usize, reg: LR, ) -> LowLevelILMutableExpression<'_, ValueExpr> { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::LLIL_REG; // TODO verify valid id let reg = reg.into().id(); let expr_idx = unsafe { BNLowLevelILAddExpr(self.handle, LLIL_REG, size, 0, reg.0 as u64, 0, 0, 0) }; LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx)) } pub fn reg_split>>( &self, size: usize, hi_reg: LR, lo_reg: LR, ) -> LowLevelILMutableExpression<'_, ValueExpr> { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::LLIL_REG_SPLIT; // TODO verify valid id let hi_reg = hi_reg.into().id(); let lo_reg = lo_reg.into().id(); let expr_idx = unsafe { BNLowLevelILAddExpr( self.handle, LLIL_REG_SPLIT, size, 0, hi_reg.0 as u64, lo_reg.0 as u64, 0, 0, ) }; LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx)) } pub fn set_reg<'a, R, LR, E>( &'a self, size: usize, dest_reg: LR, expr: E, ) -> ExpressionBuilder<'a, VoidExpr> where R: ArchReg, LR: Into>, E: LiftableLowLevelILWithSize<'a>, { use binaryninjacore_sys::BNLowLevelILOperation::LLIL_SET_REG; // TODO verify valid id let dest_reg = dest_reg.into().id(); let expr = E::lift_with_size(self, expr, size); ExpressionBuilder { function: self, op: LLIL_SET_REG, size, // TODO: Make these optional? flag_write: FlagWriteId(0), op1: dest_reg.0 as u64, op2: expr.index.0 as u64, op3: 0, op4: 0, _ty: PhantomData, } } pub fn set_reg_split<'a, R, LR, E>( &'a self, size: usize, hi_reg: LR, lo_reg: LR, expr: E, ) -> ExpressionBuilder<'a, VoidExpr> where R: ArchReg, LR: Into>, E: LiftableLowLevelILWithSize<'a>, { use binaryninjacore_sys::BNLowLevelILOperation::LLIL_SET_REG_SPLIT; // TODO verify valid id let hi_reg = hi_reg.into().id(); let lo_reg = lo_reg.into().id(); let expr = E::lift_with_size(self, expr, size); ExpressionBuilder { function: self, op: LLIL_SET_REG_SPLIT, size, flag_write: FlagWriteId(0), op1: hi_reg.0 as u64, op2: lo_reg.0 as u64, op3: expr.index.0 as u64, op4: 0, _ty: PhantomData, } } pub fn flag(&self, flag: impl Flag) -> LowLevelILMutableExpression<'_, ValueExpr> { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG; // TODO verify valid id let expr_idx = unsafe { BNLowLevelILAddExpr(self.handle, LLIL_FLAG, 0, 0, flag.id().0 as u64, 0, 0, 0) }; LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx)) } pub fn flag_cond(&self, cond: FlagCondition) -> LowLevelILMutableExpression<'_, ValueExpr> { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG_COND; // TODO verify valid id let expr_idx = unsafe { BNLowLevelILAddExpr(self.handle, LLIL_FLAG_COND, 0, 0, cond as u64, 0, 0, 0) }; LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx)) } pub fn flag_group(&self, group: impl FlagGroup) -> LowLevelILMutableExpression<'_, ValueExpr> { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG_GROUP; // TODO verify valid id let expr_idx = unsafe { BNLowLevelILAddExpr( self.handle, LLIL_FLAG_GROUP, 0, 0, group.id().0 as u64, 0, 0, 0, ) }; LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx)) } pub fn set_flag<'a, E>( &'a self, dest_flag: impl Flag, expr: E, ) -> ExpressionBuilder<'a, VoidExpr> where E: LiftableLowLevelILWithSize<'a>, { use binaryninjacore_sys::BNLowLevelILOperation::LLIL_SET_FLAG; // TODO verify valid id let expr = E::lift_with_size(self, expr, 0); ExpressionBuilder { function: self, op: LLIL_SET_FLAG, size: 0, flag_write: FlagWriteId(0), op1: dest_flag.id().0 as u64, op2: expr.index.0 as u64, op3: 0, op4: 0, _ty: PhantomData, } } /* * TODO FlagBit(usize, Flag, u64), */ pub fn load<'a, E>(&'a self, size: usize, source_mem: E) -> ExpressionBuilder<'a, ValueExpr> where E: LiftableLowLevelIL<'a, Result = ValueExpr>, { use binaryninjacore_sys::BNLowLevelILOperation::LLIL_LOAD; let expr = E::lift(self, source_mem); ExpressionBuilder { function: self, op: LLIL_LOAD, size, flag_write: FlagWriteId(0), op1: expr.index.0 as u64, op2: 0, op3: 0, op4: 0, _ty: PhantomData, } } pub fn store<'a, D, V>( &'a self, size: usize, dest_mem: D, value: V, ) -> ExpressionBuilder<'a, VoidExpr> where D: LiftableLowLevelIL<'a, Result = ValueExpr>, V: LiftableLowLevelILWithSize<'a>, { use binaryninjacore_sys::BNLowLevelILOperation::LLIL_STORE; let dest_mem = D::lift(self, dest_mem); let value = V::lift_with_size(self, value, size); ExpressionBuilder { function: self, op: LLIL_STORE, size, flag_write: FlagWriteId(0), op1: dest_mem.index.0 as u64, op2: value.index.0 as u64, op3: 0, op4: 0, _ty: PhantomData, } } // TODO: Reposition arguments. pub fn intrinsic<'a, R, O, P>( &'a self, outputs: impl IntoIterator, intrinsic: impl Intrinsic, inputs: impl IntoIterator, ) -> ExpressionBuilder<'a, VoidExpr> where R: ArchReg, O: Into>, P: LiftableLowLevelIL<'a, Result = ValueExpr>, { use binaryninjacore_sys::BNLowLevelILOperation::{LLIL_CALL_PARAM, LLIL_INTRINSIC}; use binaryninjacore_sys::{BNLowLevelILAddExpr, BNLowLevelILAddOperandList}; let mut outputs: Vec = outputs .into_iter() .map(|output| output.into().id().0 as u64) .collect(); let output_expr_idx = unsafe { BNLowLevelILAddOperandList(self.handle, outputs.as_mut_ptr(), outputs.len()) }; let mut inputs: Vec = inputs .into_iter() .map(|input| { let input = P::lift(self, input); input.index.0 as u64 }) .collect(); let input_list_expr_idx = unsafe { BNLowLevelILAddOperandList(self.handle, inputs.as_mut_ptr(), inputs.len()) }; let input_expr_idx = unsafe { BNLowLevelILAddExpr( self.handle, LLIL_CALL_PARAM, 0, 0, inputs.len() as u64, input_list_expr_idx as u64, 0, 0, ) }; ExpressionBuilder { function: self, op: LLIL_INTRINSIC, size: 0, flag_write: FlagWriteId(0), op1: outputs.len() as u64, op2: output_expr_idx as u64, op3: intrinsic.id().0 as u64, op4: input_expr_idx as u64, _ty: PhantomData, } } sized_unary_op_lifter!(push, LLIL_PUSH, VoidExpr); sized_no_arg_lifter!(pop, LLIL_POP, ValueExpr); size_changing_unary_op_lifter!(unimplemented_mem, LLIL_UNIMPL_MEM, ValueExpr); sized_unary_op_lifter!(neg, LLIL_NEG, ValueExpr); sized_unary_op_lifter!(not, LLIL_NOT, ValueExpr); size_changing_unary_op_lifter!(sx, LLIL_SX, ValueExpr); size_changing_unary_op_lifter!(zx, LLIL_ZX, ValueExpr); size_changing_unary_op_lifter!(low_part, LLIL_LOW_PART, ValueExpr); binary_op_lifter!(add, LLIL_ADD); binary_op_lifter!(add_overflow, LLIL_ADD_OVERFLOW); binary_op_lifter!(sub, LLIL_SUB); binary_op_lifter!(and, LLIL_AND); binary_op_lifter!(or, LLIL_OR); binary_op_lifter!(xor, LLIL_XOR); binary_op_lifter!(lsl, LLIL_LSL); binary_op_lifter!(lsr, LLIL_LSR); binary_op_lifter!(asr, LLIL_ASR); binary_op_lifter!(rol, LLIL_ROL); binary_op_lifter!(rlc, LLIL_RLC); binary_op_lifter!(ror, LLIL_ROR); binary_op_lifter!(rrc, LLIL_RRC); binary_op_lifter!(mul, LLIL_MUL); binary_op_lifter!(muls_dp, LLIL_MULS_DP); binary_op_lifter!(mulu_dp, LLIL_MULU_DP); binary_op_lifter!(divs, LLIL_DIVS); binary_op_lifter!(divu, LLIL_DIVU); binary_op_lifter!(mods, LLIL_MODS); binary_op_lifter!(modu, LLIL_MODU); binary_op_carry_lifter!(adc, LLIL_ADC); binary_op_carry_lifter!(sbb, LLIL_SBB); /* DivsDp(usize, Expr, Expr, Expr, Option), DivuDp(usize, Expr, Expr, Expr, Option), ModsDp(usize, Expr, Expr, Expr, Option), ModuDp(usize, Expr, Expr, Expr, Option), */ // FlagCond(u32), // TODO binary_op_lifter!(cmp_e, LLIL_CMP_E); binary_op_lifter!(cmp_ne, LLIL_CMP_NE); binary_op_lifter!(cmp_slt, LLIL_CMP_SLT); binary_op_lifter!(cmp_ult, LLIL_CMP_ULT); binary_op_lifter!(cmp_sle, LLIL_CMP_SLE); binary_op_lifter!(cmp_ule, LLIL_CMP_ULE); binary_op_lifter!(cmp_sge, LLIL_CMP_SGE); binary_op_lifter!(cmp_uge, LLIL_CMP_UGE); binary_op_lifter!(cmp_sgt, LLIL_CMP_SGT); binary_op_lifter!(cmp_ugt, LLIL_CMP_UGT); binary_op_lifter!(test_bit, LLIL_TEST_BIT); // TODO no flags size_changing_unary_op_lifter!(bool_to_int, LLIL_BOOL_TO_INT, ValueExpr); binary_op_lifter!(fadd, LLIL_FADD); binary_op_lifter!(fsub, LLIL_FSUB); binary_op_lifter!(fmul, LLIL_FMUL); binary_op_lifter!(fdiv, LLIL_FDIV); sized_unary_op_lifter!(fsqrt, LLIL_FSQRT, ValueExpr); sized_unary_op_lifter!(fneg, LLIL_FNEG, ValueExpr); sized_unary_op_lifter!(fabs, LLIL_FABS, ValueExpr); sized_unary_op_lifter!(float_to_int, LLIL_FLOAT_TO_INT, ValueExpr); sized_unary_op_lifter!(int_to_float, LLIL_INT_TO_FLOAT, ValueExpr); sized_unary_op_lifter!(float_conv, LLIL_FLOAT_CONV, ValueExpr); sized_unary_op_lifter!(round_to_int, LLIL_ROUND_TO_INT, ValueExpr); sized_unary_op_lifter!(floor, LLIL_FLOOR, ValueExpr); sized_unary_op_lifter!(ceil, LLIL_CEIL, ValueExpr); sized_unary_op_lifter!(ftrunc, LLIL_FTRUNC, ValueExpr); binary_op_lifter!(fcmp_e, LLIL_FCMP_E); binary_op_lifter!(fcmp_ne, LLIL_FCMP_NE); binary_op_lifter!(fcmp_lt, LLIL_FCMP_LT); binary_op_lifter!(fcmp_le, LLIL_FCMP_LE); binary_op_lifter!(fcmp_ge, LLIL_FCMP_GE); binary_op_lifter!(fcmp_gt, LLIL_FCMP_GT); binary_op_lifter!(fcmp_o, LLIL_FCMP_O); binary_op_lifter!(fcmp_uo, LLIL_FCMP_UO); pub fn current_address(&self) -> u64 { use binaryninjacore_sys::BNLowLevelILGetCurrentAddress; unsafe { BNLowLevelILGetCurrentAddress(self.handle) } } pub fn set_current_address>(&self, loc: L) { use binaryninjacore_sys::BNLowLevelILSetCurrentAddress; let loc: Location = loc.into(); let arch = loc.arch.unwrap_or_else(|| *self.arch().as_ref()); unsafe { BNLowLevelILSetCurrentAddress(self.handle, arch.handle, loc.addr); } } pub fn label_for_address>(&self, loc: L) -> Option { use binaryninjacore_sys::BNGetLowLevelILLabelForAddress; let loc: Location = loc.into(); let arch = loc.arch.unwrap_or_else(|| *self.arch().as_ref()); let raw_label = unsafe { BNGetLowLevelILLabelForAddress(self.handle, arch.handle, loc.addr) }; match raw_label.is_null() { false => { let mut label = unsafe { LowLevelILLabel::from(*raw_label) }; // Set the location so that calls to [Self::update_label_map_for_label] will update the label map. label.location = Some(loc); Some(label) } true => None, } } /// Call this after updating the label through an il operation or via [`Self::mark_label`]. fn update_label_map_for_label(&self, label: &LowLevelILLabel) { use binaryninjacore_sys::BNGetLowLevelILLabelForAddress; // Only need to update the label if there is an associated address. if let Some(loc) = label.location { let arch = loc.arch.unwrap_or_else(|| *self.arch().as_ref()); // Add the label into the label map unsafe { BNAddLowLevelILLabelForAddress(self.handle, arch.handle, loc.addr) }; // Retrieve a pointer to the label in the map let raw_label = unsafe { BNGetLowLevelILLabelForAddress(self.handle, arch.handle, loc.addr) }; // We should always have a valid label here assert!(!raw_label.is_null(), "Failed to add label for address!"); // Update the label in the map with `label` unsafe { *raw_label = label.into() }; } } pub fn mark_label(&self, label: &mut LowLevelILLabel) { use binaryninjacore_sys::BNLowLevelILMarkLabel; let mut raw_label = BNLowLevelILLabel::from(*label); unsafe { BNLowLevelILMarkLabel(self.handle, &mut raw_label) }; let mut new_label = LowLevelILLabel::from(raw_label); if let Some(location) = label.location { new_label.location = Some(location); self.update_label_map_for_label(&new_label); } *label = new_label; } } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct LowLevelILLabel { /// Used to update the label map if the label is associated with a location. pub location: Option, pub resolved: bool, // TODO: This expr_ref is not actually a valid one sometimes... // TODO: We should make these non public and only accessible if resolved is true. pub expr_ref: LowLevelExpressionIndex, // TODO: If this is 7 this label is not valid. pub operand: usize, } impl LowLevelILLabel { pub fn new() -> Self { use binaryninjacore_sys::BNLowLevelILInitLabel; let mut raw_label = BNLowLevelILLabel::default(); unsafe { BNLowLevelILInitLabel(&mut raw_label) }; raw_label.into() } } impl From for LowLevelILLabel { fn from(value: BNLowLevelILLabel) -> Self { Self { location: None, resolved: value.resolved, expr_ref: LowLevelExpressionIndex(value.ref_), operand: value.operand, } } } impl From for BNLowLevelILLabel { fn from(value: LowLevelILLabel) -> Self { Self { resolved: value.resolved, ref_: value.expr_ref.0, operand: value.operand, } } } impl From<&LowLevelILLabel> for BNLowLevelILLabel { fn from(value: &LowLevelILLabel) -> Self { Self::from(*value) } } impl Default for LowLevelILLabel { fn default() -> Self { Self::new() } }