diff options
Diffstat (limited to 'rust/src/llil/lifting.rs')
| -rw-r--r-- | rust/src/llil/lifting.rs | 1292 |
1 files changed, 1292 insertions, 0 deletions
diff --git a/rust/src/llil/lifting.rs b/rust/src/llil/lifting.rs new file mode 100644 index 00000000..e50eaaf1 --- /dev/null +++ b/rust/src/llil/lifting.rs @@ -0,0 +1,1292 @@ +use std::marker::PhantomData; +use std::mem; + +use crate::architecture::Register as ArchReg; +use crate::architecture::{FlagWrite, Flag, FlagClass, FlagGroup, FlagRole, FlagCondition}; +use crate::architecture::Architecture; + + +use super::*; + +pub trait Liftable<'func, A: 'func + Architecture> { + type Result: ExpressionResultType; + + fn lift(il: &'func Function<A, Mutable, NonSSA<LiftedNonSSA>>, expr: Self) + -> Expression<'func, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result>; +} + +pub trait LiftableWithSize<'func, A: 'func + Architecture>: Liftable<'func, A, Result=ValueExpr> { + fn lift_with_size(il: &'func Function<A, Mutable, NonSSA<LiftedNonSSA>>, expr: Self, size: usize) + -> Expression<'func, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>; +} + +use binaryninjacore_sys::BNRegisterOrConstant; + +#[derive(Copy, Clone)] +pub enum RegisterOrConstant<R: ArchReg> { + Register(usize, Register<R>), + Constant(usize, u64), +} + +impl<R: ArchReg> RegisterOrConstant<R> { + pub(crate) fn into_api(self) -> BNRegisterOrConstant { + match self { + RegisterOrConstant::Register(_, r) => BNRegisterOrConstant { + constant: false, + reg: r.id(), + value: 0, + }, + RegisterOrConstant::Constant(_, value) => BNRegisterOrConstant { + constant: true, + reg: 0, + value: value, + } + } + } +} + +// TODO flesh way out +#[derive(Copy, Clone)] +pub enum FlagWriteOp<R: ArchReg> { + SetReg(usize, RegisterOrConstant<R>), + SetRegSplit(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + + Sub(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Add(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + + Load(usize, RegisterOrConstant<R>), + + Push(usize, RegisterOrConstant<R>), + Neg(usize, RegisterOrConstant<R>), + Not(usize, RegisterOrConstant<R>), + Sx(usize, RegisterOrConstant<R>), + Zx(usize, RegisterOrConstant<R>), + LowPart(usize, RegisterOrConstant<R>), + BoolToInt(usize, RegisterOrConstant<R>), + FloatToInt(usize, RegisterOrConstant<R>), + + Store(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + + And(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Or(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Xor(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Lsl(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Lsr(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Asr(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Rol(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Ror(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Mul(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + MuluDp(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + MulsDp(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Divu(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Divs(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Modu(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + Mods(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + DivuDp(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + DivsDp(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + ModuDp(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + ModsDp(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + + TestBit(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + AddOverflow(usize, RegisterOrConstant<R>, RegisterOrConstant<R>), + + Adc(usize, RegisterOrConstant<R>, RegisterOrConstant<R>, RegisterOrConstant<R>), + Sbb(usize, RegisterOrConstant<R>, RegisterOrConstant<R>, RegisterOrConstant<R>), + Rlc(usize, RegisterOrConstant<R>, RegisterOrConstant<R>, RegisterOrConstant<R>), + Rrc(usize, RegisterOrConstant<R>, RegisterOrConstant<R>, RegisterOrConstant<R>), + + Pop(usize), + + // TODO: floating point stuff, llil comparison ops that set flags, intrinsics +} + +impl<R: ArchReg> FlagWriteOp<R> { + pub(crate) fn from_op<A>(arch: &A, size: usize, op: BNLowLevelILOperation, operands: &[BNRegisterOrConstant]) + -> Option<Self> + where + A: Architecture<Register=R>, + R: ArchReg<InfoType=A::RegisterInfo>, + { + use binaryninjacore_sys::BNLowLevelILOperation::*; + use self::FlagWriteOp::*; + + fn build_op<A, R>(arch: &A, size: usize, operand: &BNRegisterOrConstant) -> RegisterOrConstant<R> + where + A: Architecture<Register=R>, + R: ArchReg<InfoType=A::RegisterInfo>, + { + if operand.constant { + RegisterOrConstant::Constant(size, operand.value) + } else { + let il_reg = if 0x8000_0000 & operand.reg == 0 { + Register::ArchReg(arch.register_from_id(operand.reg).unwrap()) + } else { + Register::Temp(operand.reg) + }; + + RegisterOrConstant::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 binaryninjacore_sys::BNLowLevelILOperation::*; + use self::FlagWriteOp::*; + + 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 api_operands(&self) -> (usize, [BNRegisterOrConstant; 5]) { + use self::FlagWriteOp::*; + + let mut operands: [BNRegisterOrConstant; 5] = unsafe { mem::zeroed() }; + + 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_api(); + 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_api(); + operands[1] = op1.into_api(); + 2 + } + + Adc(_, op0, op1, op2) | + Sbb(_, op0, op1, op2) | + Rlc(_, op0, op1, op2) | + Rrc(_, op0, op1, op2) => { + operands[0] = op0.into_api(); + operands[1] = op1.into_api(); + operands[2] = op2.into_api(); + 3 + } + }; + + (count, operands) + } +} + + +pub fn get_default_flag_write_llil<'func, A>(arch: &A, role: FlagRole, op: FlagWriteOp<A::Register>, il: &'func Lifter<A>) + -> LiftedExpr<'func, A> +where + A: 'func + Architecture +{ + let (size, operation) = op.size_and_op(); + let (count, operands) = op.api_operands(); + + let expr_idx = unsafe { + use binaryninjacore_sys::BNGetDefaultArchitectureFlagWriteLowLevelIL; + BNGetDefaultArchitectureFlagWriteLowLevelIL(arch.as_ref().0, operation, size, role, + operands.as_ptr() as *mut _, count, il.handle) + }; + + Expression { + function: il, + expr_idx: expr_idx, + _ty: PhantomData, + } +} + +pub fn get_default_flag_cond_llil<'func, A>(arch: &A, cond: FlagCondition, class: Option<A::FlagClass>, il: &'func Lifter<A>) + -> LiftedExpr<'func, A> +where + A: 'func + Architecture +{ + use binaryninjacore_sys::BNGetDefaultArchitectureFlagConditionLowLevelIL; + + let handle = arch.as_ref(); + let class_id = class.map(|c| c.id()).unwrap_or(0); + + unsafe { + let expr_idx = BNGetDefaultArchitectureFlagConditionLowLevelIL(handle.0, cond, class_id, il.handle); + + Expression { + function: il, + expr_idx: expr_idx, + _ty: PhantomData, + } + } +} + +macro_rules! prim_int_lifter { + ($x:ty) => { + impl<'a, A: 'a + Architecture> Liftable<'a, A> for $x { + type Result = ValueExpr; + + fn lift(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, val: Self) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result> + { + il.const_int(mem::size_of::<Self>(), val as i64 as u64) + } + } + + impl<'a, A: 'a + Architecture> LiftableWithSize<'a, A> for $x { + fn lift_with_size(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, val: Self, size: usize) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, 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 { + 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: ArchReg, A: 'a + Architecture> Liftable<'a, A> for Register<R> + where R: Liftable<'a, A, Result=ValueExpr> + Into<Register<R>> +{ + type Result = ValueExpr; + + fn lift(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, reg: Self) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result> + { + match reg { + Register::ArchReg(r) => R::lift(il, r), + Register::Temp(t) => il.reg(il.arch().default_integer_size(), Register::Temp(t)), + } + } +} + +impl<'a, R: ArchReg, A: 'a + Architecture> LiftableWithSize<'a, A> for Register<R> + where R: LiftableWithSize<'a, A> + Into<Register<R>> +{ + fn lift_with_size(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, reg: Self, size: usize) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> + { + match reg { + Register::ArchReg(r) => R::lift_with_size(il, r, size), + Register::Temp(t) => il.reg(size, Register::Temp(t)), + } + } +} + + +impl<'a, R: ArchReg, A: 'a + Architecture> Liftable<'a, A> for RegisterOrConstant<R> + where R: LiftableWithSize<'a, A, Result=ValueExpr> + Into<Register<R>> +{ + type Result = ValueExpr; + + fn lift(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, reg: Self) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result> + { + match reg { + RegisterOrConstant::Register(size, r) => Register::<R>::lift_with_size(il, r, size), + RegisterOrConstant::Constant(size, value) => u64::lift_with_size(il, value, size), + } + } +} + +impl<'a, R: ArchReg, A: 'a + Architecture> LiftableWithSize<'a, A> for RegisterOrConstant<R> + where R: LiftableWithSize<'a, A> + Into<Register<R>> +{ + fn lift_with_size(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, reg: Self, size: usize) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> + { + // TODO ensure requested size is compatible with size of this constant + match reg { + RegisterOrConstant::Register(_, r) => Register::<R>::lift_with_size(il, r, size), + RegisterOrConstant::Constant(_, value) => u64::lift_with_size(il, value, size), + } + } +} + + +impl<'a, A, R> Liftable<'a, A> for Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R> +where + A: 'a + Architecture, + R: ExpressionResultType, +{ + type Result = R; + + fn lift(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, expr: Self) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result> + { + debug_assert!(expr.function.handle == il.handle); + expr + } +} + +impl<'a, A: 'a + Architecture> LiftableWithSize<'a, A> for Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> { + fn lift_with_size(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, expr: Self, _size: usize) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result> + { + #[cfg(debug_assertions)] + { + if let Some(expr_size) = expr.info().size() { + if expr_size != _size { + warn!("il @ {:x} attempted to lift {} byte expression as {} bytes", + il.current_address(), expr_size, _size); + } + } + } + + Liftable::lift(il, expr) + } +} + + +impl<'func, A, R> Expression<'func, A, Mutable, NonSSA<LiftedNonSSA>, R> +where + A: 'func + Architecture, + R: ExpressionResultType, +{ + pub fn with_source_operand(self, op: u32) -> Self { + use binaryninjacore_sys::BNLowLevelILSetExprSourceOperand; + + unsafe { + BNLowLevelILSetExprSourceOperand(self.function.handle, self.expr_idx, op) + } + + self + } + + pub fn append(self) { + let il = self.function; + il.instruction(self); + } +} + + +use binaryninjacore_sys::BNLowLevelILOperation; +pub struct ExpressionBuilder<'func, A, R> +where + A: 'func + Architecture, + R: ExpressionResultType, +{ + function: &'func Function<A, Mutable, NonSSA<LiftedNonSSA>>, + op: BNLowLevelILOperation, + size: usize, + flags: u32, + op1: u64, + op2: u64, + op3: u64, + op4: u64, + _ty: PhantomData<R>, +} + +impl<'a, A, R> ExpressionBuilder<'a, A, R> +where + A: 'a + Architecture, + R: ExpressionResultType, +{ + pub fn with_flag_write(mut self, flag_write: A::FlagWrite) -> Self { + // TODO verify valid id + self.flags = flag_write.id(); + self + } + + pub fn into_expr(self) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R> { + self.into() + } + + pub fn with_source_operand(self, op: u32) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R> { + let expr = self.into_expr(); + expr.with_source_operand(op) + } + + pub fn append(self) { + let expr = self.into_expr(); + let il = expr.function; + + il.instruction(expr); + } +} + +impl<'a, A, R> Into<Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R>> for ExpressionBuilder<'a, A, R> +where + A: 'a + Architecture, + R: ExpressionResultType, +{ + fn into(self) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R> { + use binaryninjacore_sys::BNLowLevelILAddExpr; + + let expr_idx = unsafe { + BNLowLevelILAddExpr(self.function.handle, + self.op, self.size, self.flags, + self.op1, self.op2, self.op3, self.op4) + }; + + Expression { + function: self.function, + expr_idx: expr_idx, + _ty: PhantomData, + } + } +} + +impl<'a, A, R> Liftable<'a, A> for ExpressionBuilder<'a, A, R> +where + A: 'a + Architecture, + R: ExpressionResultType, +{ + type Result = R; + + fn lift(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, expr: Self) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result> + { + debug_assert!(expr.function.handle == il.handle); + + expr.into() + } +} + +impl<'a, A> LiftableWithSize<'a, A> for ExpressionBuilder<'a, A, ValueExpr> +where + A: 'a + Architecture, +{ + fn lift_with_size(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, expr: Self, _size: usize) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, 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) { + warn!("il @ {:x} attempted to lift {} byte expression builder as {} bytes", + il.current_address(), expr.size, _size); + } + } + + Liftable::lift(il, expr) + } +} + + +macro_rules! no_arg_lifter { + ($name:ident, $op:ident, $result:ty) => { + pub fn $name(&self) -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, $result> { + use binaryninjacore_sys::BNLowLevelILOperation::$op; + use binaryninjacore_sys::BNLowLevelILAddExpr; + + let expr_idx = unsafe { + BNLowLevelILAddExpr(self.handle, $op, + 0, 0, 0, 0, 0, 0) + }; + + Expression { + function: self, + expr_idx: expr_idx, + _ty: PhantomData, + } + } + } +} + +macro_rules! sized_no_arg_lifter { + ($name:ident, $op:ident, $result:ty) => { + pub fn $name(&self, size: usize) -> ExpressionBuilder<A, $result> + { + use binaryninjacore_sys::BNLowLevelILOperation::$op; + + ExpressionBuilder { + function: self, + op: $op, + size: size, + flags: 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) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, $result> + where + E: Liftable<'a, A, Result=ValueExpr> + { + use binaryninjacore_sys::BNLowLevelILOperation::$op; + use binaryninjacore_sys::BNLowLevelILAddExpr; + + let expr = E::lift(self, expr); + + let expr_idx = unsafe { + BNLowLevelILAddExpr(self.handle, $op, 0, 0, + expr.expr_idx as u64, 0, 0, 0) + }; + + Expression { + function: self, + expr_idx: expr_idx, + _ty: PhantomData, + } + } + } +} + +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, A, $result> + where + E: LiftableWithSize<'a, A> + { + use binaryninjacore_sys::BNLowLevelILOperation::$op; + + let expr = E::lift_with_size(self, expr, size); + + ExpressionBuilder { + function: self, + op: $op, + size: size, + flags: 0, + op1: expr.expr_idx 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, A, $result> + where + E: LiftableWithSize<'a, A> + { + use binaryninjacore_sys::BNLowLevelILOperation::$op; + + let expr = E::lift(self, expr); + + ExpressionBuilder { + function: self, + op: $op, + size: size, + flags: 0, + op1: expr.expr_idx 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, A, ValueExpr> + where + L: LiftableWithSize<'a, A>, + R: LiftableWithSize<'a, 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: size, + flags: 0, + op1: left.expr_idx as u64, + op2: right.expr_idx 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, A, ValueExpr> + where + L: LiftableWithSize<'a, A>, + R: LiftableWithSize<'a, A>, + C: LiftableWithSize<'a, 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, 1); // TODO 0? + + ExpressionBuilder { + function: self, + op: $op, + size: size, + flags: 0, + op1: left.expr_idx as u64, + op2: right.expr_idx as u64, + op3: carry.expr_idx as u64, + op4: 0, + _ty: PhantomData, + } + } + } +} + +impl<A> Function<A, Mutable, NonSSA<LiftedNonSSA>> +where + A: Architecture, +{ + pub fn expression<'a, E: Liftable<'a, A>>(&'a self, expr: E) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, E::Result> + { + E::lift(self, expr) + } + + pub fn instruction<'a, E: Liftable<'a, A>>(&'a self, expr: E) + { + let expr = self.expression(expr); + + unsafe { + use binaryninjacore_sys::BNLowLevelILAddInstruction; + BNLowLevelILAddInstruction(self.handle, expr.expr_idx); + } + } + + + pub unsafe fn replace_expression<'a, E: Liftable<'a, A>>( + &'a self, + replaced_expr_index: usize, + replacement: E) + { + unsafe { + use binaryninjacore_sys::BNReplaceLowLevelILExpr; + use binaryninjacore_sys::BNGetLowLevelILExprCount; + + if replaced_expr_index >= BNGetLowLevelILExprCount(self.handle) { + panic!("bad expr idx used: {} exceeds function bounds", replaced_expr_index); + } + + let expr = self.expression(replacement); + BNReplaceLowLevelILExpr(self.handle, replaced_expr_index, expr.expr_idx); + } + } + + pub fn const_int(&self, size: usize, val: u64) + -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> + { + use binaryninjacore_sys::BNLowLevelILOperation::LLIL_CONST; + use binaryninjacore_sys::BNLowLevelILAddExpr; + + let expr_idx = unsafe { + BNLowLevelILAddExpr(self.handle, LLIL_CONST, size, 0, + val, 0, 0, 0) + }; + + Expression { + function: self, + expr_idx: expr_idx, + _ty: PhantomData, + } + } + + pub fn const_ptr_sized(&self, size: usize, val: u64) + -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> + { + use binaryninjacore_sys::BNLowLevelILOperation::LLIL_CONST_PTR; + use binaryninjacore_sys::BNLowLevelILAddExpr; + + let expr_idx = unsafe { + BNLowLevelILAddExpr(self.handle, LLIL_CONST_PTR, size, 0, + val, 0, 0, 0) + }; + + Expression { + function: self, + expr_idx: expr_idx, + _ty: PhantomData, + } + } + + pub fn const_ptr(&self, val: u64) + -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> + { + self.const_ptr_sized(self.arch().address_size(), val) + } + + pub fn trap(&self, val: u64) + -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, VoidExpr> + { + use binaryninjacore_sys::BNLowLevelILOperation::LLIL_TRAP; + use binaryninjacore_sys::BNLowLevelILAddExpr; + + let expr_idx = unsafe { + BNLowLevelILAddExpr(self.handle, LLIL_TRAP, 0, 0, + val, 0, 0, 0) + }; + + Expression { + function: self, + expr_idx: expr_idx, + _ty: PhantomData, + } + } + + no_arg_lifter!(unimplemented, LLIL_UNIMPL, ValueExpr); + no_arg_lifter!(undefined, LLIL_UNDEF, VoidExpr); + 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!(ret, LLIL_RET, VoidExpr); + unsized_unary_op_lifter!(jump, LLIL_JUMP, VoidExpr); + // JumpTo TODO + + pub fn if_expr<'a: 'b, 'b, C>(&'a self, cond: C, t: &'b Label, f: &'b Label) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, VoidExpr> + where + C: Liftable<'b, A, Result=ValueExpr>, + { + use binaryninjacore_sys::BNLowLevelILIf; + + let cond = C::lift(self, cond); + + let expr_idx = unsafe { + BNLowLevelILIf(self.handle, cond.expr_idx as u64, + &t.0 as *const _ as *mut _, + &f.0 as *const _ as *mut _) + }; + + Expression { + function: self, + expr_idx: expr_idx, + _ty: PhantomData, + } + } + + pub fn goto<'a: 'b, 'b>(&'a self, l: &'b Label) + -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, VoidExpr> + { + use binaryninjacore_sys::BNLowLevelILGoto; + + let expr_idx = unsafe { + BNLowLevelILGoto(self.handle, &l.0 as *const _ as *mut _) + }; + + Expression { + function: self, + expr_idx: expr_idx, + _ty: PhantomData, + } + } + + pub fn reg<R: Into<Register<A::Register>>>(&self, size: usize, reg: R) + -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> + { + use binaryninjacore_sys::BNLowLevelILOperation::LLIL_REG; + use binaryninjacore_sys::BNLowLevelILAddExpr; + + // TODO verify valid id + let reg = match reg.into() { + Register::ArchReg(r) => r.id(), + Register::Temp(r) => 0x8000_0000 | r, + }; + + let expr_idx = unsafe { + BNLowLevelILAddExpr(self.handle, LLIL_REG, size, 0, + reg as u64, 0, 0, 0) + }; + + Expression { + function: self, + expr_idx: expr_idx, + _ty: PhantomData, + } + } + + pub fn set_reg<'a, R, E>(&'a self, size: usize, dest_reg: R, expr: E) + -> ExpressionBuilder<'a, A, VoidExpr> + where + R: Into<Register<A::Register>>, + E: LiftableWithSize<'a, A> + { + use binaryninjacore_sys::BNLowLevelILOperation::LLIL_SET_REG; + + // TODO verify valid id + let dest_reg = match dest_reg.into() { + Register::ArchReg(r) => r.id(), + Register::Temp(r) => 0x8000_0000 | r, + }; + + let expr = E::lift_with_size(self, expr, size); + + ExpressionBuilder { + function: self, + op: LLIL_SET_REG, + size: size, + flags: 0, + op1: dest_reg as u64, + op2: expr.expr_idx as u64, + op3: 0, + op4: 0, + _ty: PhantomData, + } + } + + pub fn set_reg_split<'a, H, L, E>(&'a self, size: usize, hi_reg: H, lo_reg: L, expr: E) + -> ExpressionBuilder<'a, A, VoidExpr> + where + H: Into<Register<A::Register>>, + L: Into<Register<A::Register>>, + E: LiftableWithSize<'a, A> + { + use binaryninjacore_sys::BNLowLevelILOperation::LLIL_SET_REG_SPLIT; + + // TODO verify valid id + let hi_reg = match hi_reg.into() { + Register::ArchReg(r) => r.id(), + Register::Temp(r) => 0x8000_0000 | r, + }; + + // TODO verify valid id + let lo_reg = match lo_reg.into() { + Register::ArchReg(r) => r.id(), + Register::Temp(r) => 0x8000_0000 | r, + }; + + let expr = E::lift_with_size(self, expr, size); + + ExpressionBuilder { + function: self, + op: LLIL_SET_REG_SPLIT, + size: size, + flags: 0, + op1: hi_reg as u64, + op2: lo_reg as u64, + op3: expr.expr_idx as u64, + op4: 0, + _ty: PhantomData, + } + } + + pub fn flag(&self, flag: A::Flag) + -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> + { + use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG; + use binaryninjacore_sys::BNLowLevelILAddExpr; + + // TODO verify valid id + let expr_idx = unsafe { + BNLowLevelILAddExpr(self.handle, LLIL_FLAG, 0, 0, + flag.id() as u64, 0, 0, 0) + }; + + Expression { + function: self, + expr_idx: expr_idx, + _ty: PhantomData, + } + } + + pub fn flag_cond(&self, cond: FlagCondition) + -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> + { + use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG_COND; + use binaryninjacore_sys::BNLowLevelILAddExpr; + + // TODO verify valid id + let expr_idx = unsafe { + BNLowLevelILAddExpr(self.handle, LLIL_FLAG_COND, 0, 0, + cond as u64, 0, 0, 0) + }; + + Expression { + function: self, + expr_idx: expr_idx, + _ty: PhantomData, + } + } + + pub fn flag_group(&self, group: A::FlagGroup) + -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> + { + use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG_GROUP; + use binaryninjacore_sys::BNLowLevelILAddExpr; + + // TODO verify valid id + let expr_idx = unsafe { + BNLowLevelILAddExpr(self.handle, LLIL_FLAG_GROUP, 0, 0, + group.id() as u64, 0, 0, 0) + }; + + Expression { + function: self, + expr_idx: expr_idx, + _ty: PhantomData, + } + } + + pub fn set_flag<'a, E>(&'a self, dest_flag: A::Flag, expr: E) + -> ExpressionBuilder<'a, A, VoidExpr> + where + E: LiftableWithSize<'a, 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, + flags: 0, + op1: dest_flag.id() as u64, + op2: expr.expr_idx as u64, + op3: 0, + op4: 0, + _ty: PhantomData, + } + } + + /* + * TODO + FlagBit(usize, Flag<A>, u64), + */ + + pub fn load<'a, E>(&'a self, size: usize, source_mem: E) + -> ExpressionBuilder<'a, A, ValueExpr> + where + E: Liftable<'a, A, Result=ValueExpr> + { + use binaryninjacore_sys::BNLowLevelILOperation::LLIL_LOAD; + + let expr = E::lift(self, source_mem); + + ExpressionBuilder { + function: self, + op: LLIL_LOAD, + size: size, + flags: 0, + op1: expr.expr_idx 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, A, VoidExpr> + where + D: Liftable<'a, A, Result=ValueExpr>, + V: LiftableWithSize<'a, 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: size, + flags: 0, + op1: dest_mem.expr_idx as u64, + op2: value.expr_idx as u64, + op3: 0, + op4: 0, + _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<A::FlagWrite>), + DivuDp(usize, Expr, Expr, Expr, Option<A::FlagWrite>), + ModsDp(usize, Expr, Expr, Expr, Option<A::FlagWrite>), + ModuDp(usize, Expr, Expr, Expr, Option<A::FlagWrite>), + */ + + // 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); + + pub fn current_address(&self) -> u64 { + use binaryninjacore_sys::BNLowLevelILGetCurrentAddress; + unsafe { + BNLowLevelILGetCurrentAddress(self.handle) + } + } + + pub fn set_current_address<L: Into<Location>>(&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.0, loc.addr); } + } + + pub fn label_for_address<L: Into<Location>>(&self, loc: L) -> Option<&Label> { + use binaryninjacore_sys::BNGetLowLevelILLabelForAddress; + + let loc: Location = loc.into(); + let arch = loc.arch.unwrap_or_else(|| *self.arch().as_ref()); + + let res = unsafe { + BNGetLowLevelILLabelForAddress(self.handle, arch.0, loc.addr) + }; + + if res.is_null() { + None + } else { + Some(unsafe { &*(res as *mut Label) }) + } + } + + pub fn mark_label(&self, label: &mut Label) { + use binaryninjacore_sys::BNLowLevelILMarkLabel; + + unsafe { + BNLowLevelILMarkLabel(self.handle, &mut label.0 as *mut _); + } + } +} + +use binaryninjacore_sys::BNLowLevelILLabel; + +#[repr(C)] +pub struct Label(BNLowLevelILLabel); +impl Label { + pub fn new() -> Self { + use binaryninjacore_sys::BNLowLevelILInitLabel; + + unsafe { + let mut res = Label(mem::uninitialized()); + BNLowLevelILInitLabel(&mut res.0 as *mut _); + res + } + } +} + + |
