diff options
| author | Ryan Snyder <ryan@vector35.com> | 2021-01-21 18:27:48 +0000 |
|---|---|---|
| committer | KyleMiles <krm504@nyu.edu> | 2021-01-21 19:06:55 +0000 |
| commit | d3140edec185f47235b9e4642bdd56d6c585a341 (patch) | |
| tree | a61859c29e4e3539daea2b761bb1439d942beaf4 /rust/src/llil | |
| parent | c0ddbf0c76d3f1bb7a2b2024f749afc8b9482575 (diff) | |
This is a combination of 23 commits, the work of Ryan Snyder:
Initial fresh repo
Add support for recent calling convention API updates and folds the binaryninjacore-sys crate directly into this one.
Add support for auto function analysis suppression
Finish moving binaryninjacore-sys back into this crate
Update for Symbol/Segment core API changes
Update for Symbol API cleanup
api: advance submodule reference, support Token changes
arch/lifting: support for flags in custom architectures
arch/lifting: support default flag write behaviors, handle more ops
build: enable headless binary support on MacOS via evil hack
bv: add BinaryView wrapper support, remove wrong comment
api: update to latest binja dev branch support
deps: bump dep versions
rust: bump to 2018 edition
api: bump to avoid cargo submodule brokenness
build: improve binaryninja path detection; enable linux linkhack
bv: stub for bv load settings
arch: fix flag related crash, minor llil update
api: update for recent changes
macos: disable linkhack briefly
Diffstat (limited to 'rust/src/llil')
| -rw-r--r-- | rust/src/llil/block.rs | 91 | ||||
| -rw-r--r-- | rust/src/llil/expression.rs | 816 | ||||
| -rw-r--r-- | rust/src/llil/function.rs | 198 | ||||
| -rw-r--r-- | rust/src/llil/instruction.rs | 175 | ||||
| -rw-r--r-- | rust/src/llil/lifting.rs | 1292 | ||||
| -rw-r--r-- | rust/src/llil/mod.rs | 80 | ||||
| -rw-r--r-- | rust/src/llil/operation.rs | 765 |
7 files changed, 3417 insertions, 0 deletions
diff --git a/rust/src/llil/block.rs b/rust/src/llil/block.rs new file mode 100644 index 00000000..543ab670 --- /dev/null +++ b/rust/src/llil/block.rs @@ -0,0 +1,91 @@ +use std::ops::Range; + +use crate::architecture::Architecture; +use crate::basicblock::{BasicBlock, BlockContext}; + +use super::*; + +pub struct BlockIter<'func, A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + function: &'func Function<A, M, F>, + range: Range<u64>, +} + +impl<'func, A, M, F> Iterator for BlockIter<'func, A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + type Item = Instruction<'func, A, M, F>; + + fn next(&mut self) -> Option<Self::Item> { + self.range.next().map(|i| Instruction { + function: self.function, + instr_idx: i as usize, + }) + } +} + + + +pub struct Block<'func, A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub(crate) function: &'func Function<A, M, F>, +} + +impl<'func, A, M, F> fmt::Debug for Block<'func, A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "llil_bb {:?}", self.function) + } +} + +impl<'func, A, M, F> BlockContext for Block<'func, A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + type Iter = BlockIter<'func, A, M, F>; + type Instruction = Instruction<'func, A, M, F>; + + fn start(&self, block: &BasicBlock<Self>) -> Instruction<'func, A, M, F> { + Instruction { + function: self.function, + instr_idx: block.raw_start() as usize, + } + } + + fn iter(&self, block: &BasicBlock<Self>) -> BlockIter<'func, A, M, F> { + BlockIter { + function: self.function, + range: block.raw_start() .. block.raw_end(), + } + } +} + +impl<'func, A, M, F> Clone for Block<'func, A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + fn clone(&self) -> Self { + Block { function: self.function } + } +} + + diff --git a/rust/src/llil/expression.rs b/rust/src/llil/expression.rs new file mode 100644 index 00000000..1e1edda4 --- /dev/null +++ b/rust/src/llil/expression.rs @@ -0,0 +1,816 @@ +use binaryninjacore_sys::BNGetLowLevelILByIndex; +use binaryninjacore_sys::BNLowLevelILInstruction; + +use std::marker::PhantomData; +use std::fmt; + +use super::*; +use super::operation; +use super::operation::Operation; + +use crate::architecture::Architecture; +use crate::architecture::RegisterInfo; + +// used as a marker for Expressions that can produce a value +#[derive(Copy, Clone, Debug)] +pub struct ValueExpr; + +// used as a marker for Expressions that can not produce a value +#[derive(Copy, Clone, Debug)] +pub struct VoidExpr; + +pub trait ExpressionResultType: 'static {} +impl ExpressionResultType for ValueExpr {} +impl ExpressionResultType for VoidExpr {} + +pub struct Expression<'func, A, M, F, R> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, + R: ExpressionResultType, +{ + pub(crate) function: &'func Function<A, M, F>, + pub(crate) expr_idx: usize, + + // tag the 'return' type of this expression + pub(crate) _ty: PhantomData<R>, +} + +impl<'func, A, M, F, R> Expression<'func, A, M, F, R> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, + R: ExpressionResultType, +{ + pub fn index(&self) -> usize { + self.expr_idx + } +} + +impl<'func, A, M, V> fmt::Debug for Expression<'func, A, M, NonSSA<V>, ValueExpr> +where + A: 'func + Architecture, + M: FunctionMutability, + V: NonSSAVariant, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let op_info = self.info(); + write!(f, "<expr {}: {:?}>", self.expr_idx, op_info) + } +} + +fn common_info<'func, A, M, F>(function: &'func Function<A, M, F>, op: BNLowLevelILInstruction) + -> ExprInfo<'func, A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + use binaryninjacore_sys::BNLowLevelILOperation::*; + + match op.operation { + LLIL_CONST => ExprInfo::Const(Operation::new(function, op)), + LLIL_CONST_PTR => ExprInfo::ConstPtr(Operation::new(function, op)), + + LLIL_ADD => ExprInfo::Add(Operation::new(function, op)), + LLIL_ADC => ExprInfo::Adc(Operation::new(function, op)), + LLIL_SUB => ExprInfo::Sub(Operation::new(function, op)), + LLIL_SBB => ExprInfo::Sbb(Operation::new(function, op)), + LLIL_AND => ExprInfo::And(Operation::new(function, op)), + LLIL_OR => ExprInfo::Or (Operation::new(function, op)), + LLIL_XOR => ExprInfo::Xor(Operation::new(function, op)), + LLIL_LSL => ExprInfo::Lsl(Operation::new(function, op)), + LLIL_LSR => ExprInfo::Lsr(Operation::new(function, op)), + LLIL_ASR => ExprInfo::Asr(Operation::new(function, op)), + LLIL_ROL => ExprInfo::Rol(Operation::new(function, op)), + LLIL_RLC => ExprInfo::Rlc(Operation::new(function, op)), + LLIL_ROR => ExprInfo::Ror(Operation::new(function, op)), + LLIL_RRC => ExprInfo::Rrc(Operation::new(function, op)), + LLIL_MUL => ExprInfo::Mul(Operation::new(function, op)), + + LLIL_MULU_DP => ExprInfo::MuluDp(Operation::new(function, op)), + LLIL_MULS_DP => ExprInfo::MulsDp(Operation::new(function, op)), + + LLIL_DIVU => ExprInfo::Divu(Operation::new(function, op)), + LLIL_DIVS => ExprInfo::Divs(Operation::new(function, op)), + + LLIL_DIVU_DP => ExprInfo::DivuDp(Operation::new(function, op)), + LLIL_DIVS_DP => ExprInfo::DivsDp(Operation::new(function, op)), + + LLIL_MODU => ExprInfo::Modu(Operation::new(function, op)), + LLIL_MODS => ExprInfo::Mods(Operation::new(function, op)), + + LLIL_MODU_DP => ExprInfo::ModuDp(Operation::new(function, op)), + LLIL_MODS_DP => ExprInfo::ModsDp(Operation::new(function, op)), + + LLIL_NEG => ExprInfo::Neg(Operation::new(function, op)), + LLIL_NOT => ExprInfo::Not(Operation::new(function, op)), + + LLIL_SX => ExprInfo::Sx(Operation::new(function, op)), + LLIL_ZX => ExprInfo::Zx(Operation::new(function, op)), + LLIL_LOW_PART => ExprInfo::LowPart(Operation::new(function, op)), + + LLIL_CMP_E => ExprInfo::CmpE(Operation::new(function, op)), + LLIL_CMP_NE => ExprInfo::CmpNe(Operation::new(function, op)), + LLIL_CMP_SLT => ExprInfo::CmpSlt(Operation::new(function, op)), + LLIL_CMP_ULT => ExprInfo::CmpUlt(Operation::new(function, op)), + LLIL_CMP_SLE => ExprInfo::CmpSle(Operation::new(function, op)), + LLIL_CMP_ULE => ExprInfo::CmpUle(Operation::new(function, op)), + LLIL_CMP_SGE => ExprInfo::CmpSge(Operation::new(function, op)), + LLIL_CMP_UGE => ExprInfo::CmpUge(Operation::new(function, op)), + LLIL_CMP_SGT => ExprInfo::CmpSgt(Operation::new(function, op)), + LLIL_CMP_UGT => ExprInfo::CmpUgt(Operation::new(function, op)), + + LLIL_BOOL_TO_INT => ExprInfo::BoolToInt(Operation::new(function, op)), + + LLIL_UNIMPL => ExprInfo::Unimpl(Operation::new(function, op)), + LLIL_UNIMPL_MEM => ExprInfo::UnimplMem(Operation::new(function, op)), + + // TODO TEST_BIT ADD_OVERFLOW + _ => { + #[cfg(debug_assertions)] + { + error!("Got unexpected operation {:?} in value expr at 0x{:x}", + op.operation, op.address); + } + + ExprInfo::Undef(Operation::new(function, op)) + } + } +} + +use super::VisitorAction; + +macro_rules! visit { + ($f:expr, $($e:expr),*) => { + if let VisitorAction::Halt = $f($($e,)*) { + return VisitorAction::Halt; + } + } +} + +fn common_visit<'func, A, M, F, CB>(info: &ExprInfo<'func, A, M, F>, f: &mut CB) + -> VisitorAction +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, + CB: FnMut(&Expression<'func, A, M, F, ValueExpr>) -> VisitorAction, +{ + use self::ExprInfo::*; + + match *info { + CmpE(ref op) | CmpNe(ref op) | + CmpSlt(ref op) | CmpUlt(ref op) | + CmpSle(ref op) | CmpUle(ref op) | + CmpSge(ref op) | CmpUge(ref op) | + CmpSgt(ref op) | CmpUgt(ref op) => { + visit!(f, &op.left()); + visit!(f, &op.right()); + } + + Adc(ref op) | + Sbb(ref op) | + Rlc(ref op) | + Rrc(ref op) => { + visit!(f, &op.left()); + visit!(f, &op.right()); + visit!(f, &op.carry()); + } + + Add(ref op) | + Sub(ref op) | + And(ref op) | + Or (ref op) | + Xor(ref op) | + Lsl(ref op) | + Lsr(ref op) | + Asr(ref op) | + Rol(ref op) | + Ror(ref op) | + Mul(ref op) | + MulsDp(ref op) | + MuluDp(ref op) | + Divu(ref op) | + Divs(ref op) | + Modu(ref op) | + Mods(ref op) => { + visit!(f, &op.left()); + visit!(f, &op.right()); + } + + DivuDp(ref op) | + DivsDp(ref op) | + ModuDp(ref op) | + ModsDp(ref op) => { + visit!(f, &op.high()); + visit!(f, &op.low()); + visit!(f, &op.right()); + } + + Neg(ref op) | + Not(ref op) | + Sx(ref op) | + Zx(ref op) | + LowPart(ref op) | + BoolToInt(ref op) => { + visit!(f, &op.operand()); + } + + UnimplMem(ref op) => { + visit!(f, &op.mem_expr()); + } + + _ => {} + }; + + VisitorAction::Sibling +} + +impl<'func, A, M, V> Expression<'func, A, M, NonSSA<V>, ValueExpr> +where + A: 'func + Architecture, + M: FunctionMutability, + V: NonSSAVariant, +{ + pub(crate) unsafe fn info_from_op(&self, op: BNLowLevelILInstruction) -> ExprInfo<'func, A, M, NonSSA<V>> { + use binaryninjacore_sys::BNLowLevelILOperation::*; + + match op.operation { + LLIL_LOAD => ExprInfo::Load(Operation::new(self.function, op)), + LLIL_POP => ExprInfo::Pop(Operation::new(self.function, op)), + LLIL_REG => ExprInfo::Reg(Operation::new(self.function, op)), + LLIL_FLAG => ExprInfo::Flag(Operation::new(self.function, op)), + LLIL_FLAG_BIT => ExprInfo::FlagBit(Operation::new(self.function, op)), + LLIL_FLAG_COND => ExprInfo::FlagCond(Operation::new(self.function, op)), // TODO lifted only + LLIL_FLAG_GROUP => ExprInfo::FlagGroup(Operation::new(self.function, op)), // TODO lifted only + _ => common_info(self.function, op), + } + } + + pub fn info(&self) -> ExprInfo<'func, A, M, NonSSA<V>> { + unsafe { + let op = BNGetLowLevelILByIndex(self.function.handle, self.expr_idx); + self.info_from_op(op) + } + } + + pub fn visit_tree<F>(&self, f: &mut F) -> VisitorAction + where + F: FnMut(&Self, &ExprInfo<'func, A, M, NonSSA<V>>) -> VisitorAction, + { + use self::ExprInfo::*; + + let info = self.info(); + + match f(self, &info) { + VisitorAction::Descend => {}, + action => return action, + }; + + match info { + Load(ref op) => visit!(Self::visit_tree, &op.source_mem_expr(), f), + _ => { + let mut fb = |e: &Self| e.visit_tree(f); + visit!(common_visit, &info, &mut fb); + } + }; + + VisitorAction::Sibling + } +} + +impl<'func, A, M> Expression<'func, A, M, SSA, ValueExpr> +where + A: 'func + Architecture, + M: FunctionMutability, +{ + pub(crate) unsafe fn info_from_op(&self, op: BNLowLevelILInstruction) -> ExprInfo<'func, A, M, SSA> { + use binaryninjacore_sys::BNLowLevelILOperation::*; + + match op.operation { + LLIL_LOAD_SSA => ExprInfo::Load(Operation::new(self.function, op)), + LLIL_REG_SSA | + LLIL_REG_SSA_PARTIAL => ExprInfo::Reg(Operation::new(self.function, op)), + LLIL_FLAG_SSA => ExprInfo::Flag(Operation::new(self.function, op)), + LLIL_FLAG_BIT_SSA => ExprInfo::FlagBit(Operation::new(self.function, op)), + _ => common_info(self.function, op), + } + } + + pub fn info(&self) -> ExprInfo<'func, A, M, SSA> { + unsafe { + let op = BNGetLowLevelILByIndex(self.function.handle, self.expr_idx); + self.info_from_op(op) + } + } + + pub fn visit_tree<F>(&self, f: &mut F) -> VisitorAction + where + F: FnMut(&Self, &ExprInfo<'func, A, M, SSA>) -> VisitorAction, + { + use self::ExprInfo::*; + + let info = self.info(); + + match f(self, &info) { + VisitorAction::Descend => {}, + action => return action, + }; + + match info { + // TODO ssa + Load(ref _op) => {} //visit!(Self::visit_tree, &op.source_mem_expr(), f), + _ => { + let mut fb = |e: &Self| e.visit_tree(f); + visit!(common_visit, &info, &mut fb); + } + }; + + VisitorAction::Sibling + } +} + +impl<'func, A, F> Expression<'func, A, Finalized, F, ValueExpr> +where + A: 'func + Architecture, + F: FunctionForm, +{ + // TODO possible values +} + + + +pub enum ExprInfo<'func, A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + Load(Operation<'func, A, M, F, operation::Load>), + Pop(Operation<'func, A, M, F, operation::Pop>), + Reg(Operation<'func, A, M, F, operation::Reg>), + Const(Operation<'func, A, M, F, operation::Const>), + ConstPtr(Operation<'func, A, M, F, operation::Const>), + Flag(Operation<'func, A, M, F, operation::Flag>), + FlagBit(Operation<'func, A, M, F, operation::FlagBit>), + + Add(Operation<'func, A, M, F, operation::BinaryOp>), + Adc(Operation<'func, A, M, F, operation::BinaryOpCarry>), + Sub(Operation<'func, A, M, F, operation::BinaryOp>), + Sbb(Operation<'func, A, M, F, operation::BinaryOpCarry>), + And(Operation<'func, A, M, F, operation::BinaryOp>), + Or (Operation<'func, A, M, F, operation::BinaryOp>), + Xor(Operation<'func, A, M, F, operation::BinaryOp>), + Lsl(Operation<'func, A, M, F, operation::BinaryOp>), + Lsr(Operation<'func, A, M, F, operation::BinaryOp>), + Asr(Operation<'func, A, M, F, operation::BinaryOp>), + Rol(Operation<'func, A, M, F, operation::BinaryOp>), + Rlc(Operation<'func, A, M, F, operation::BinaryOpCarry>), + Ror(Operation<'func, A, M, F, operation::BinaryOp>), + Rrc(Operation<'func, A, M, F, operation::BinaryOpCarry>), + Mul(Operation<'func, A, M, F, operation::BinaryOp>), + + MulsDp(Operation<'func, A, M, F, operation::BinaryOp>), + MuluDp(Operation<'func, A, M, F, operation::BinaryOp>), + + Divu(Operation<'func, A, M, F, operation::BinaryOp>), + Divs(Operation<'func, A, M, F, operation::BinaryOp>), + + DivuDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>), + DivsDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>), + + Modu(Operation<'func, A, M, F, operation::BinaryOp>), + Mods(Operation<'func, A, M, F, operation::BinaryOp>), + + ModuDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>), + ModsDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>), + + Neg(Operation<'func, A, M, F, operation::UnaryOp>), + Not(Operation<'func, A, M, F, operation::UnaryOp>), + Sx(Operation<'func, A, M, F, operation::UnaryOp>), + Zx(Operation<'func, A, M, F, operation::UnaryOp>), + LowPart(Operation<'func, A, M, F, operation::UnaryOp>), + + FlagCond(Operation<'func, A, M, F, operation::FlagCond>), + FlagGroup(Operation<'func, A, M, F, operation::FlagGroup>), + + CmpE(Operation<'func, A, M, F, operation::Condition>), + CmpNe(Operation<'func, A, M, F, operation::Condition>), + CmpSlt(Operation<'func, A, M, F, operation::Condition>), + CmpUlt(Operation<'func, A, M, F, operation::Condition>), + CmpSle(Operation<'func, A, M, F, operation::Condition>), + CmpUle(Operation<'func, A, M, F, operation::Condition>), + CmpSge(Operation<'func, A, M, F, operation::Condition>), + CmpUge(Operation<'func, A, M, F, operation::Condition>), + CmpSgt(Operation<'func, A, M, F, operation::Condition>), + CmpUgt(Operation<'func, A, M, F, operation::Condition>), + + //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO + + BoolToInt(Operation<'func, A, M, F, operation::UnaryOp>), + + // TODO ADD_OVERFLOW + + Unimpl(Operation<'func, A, M, F, operation::NoArgs>), + UnimplMem(Operation<'func, A, M, F, operation::UnimplMem>), + + Undef(Operation<'func, A, M, F, operation::NoArgs>), +} + +impl<'func, A, M, F> ExprInfo<'func, A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + /// Returns the size of the result of this expression + /// + /// If the expression is malformed or is `Unimpl` there + /// is no meaningful size associated with the result. + pub fn size(&self) -> Option<usize> { + use self::ExprInfo::*; + + match *self { + Undef(..) | + Unimpl(..) => None, + + FlagCond(..) | FlagGroup(..) | + CmpE(..) | CmpNe(..) | + CmpSlt(..) | CmpUlt(..) | + CmpSle(..) | CmpUle(..) | + CmpSge(..) | CmpUge(..) | + CmpSgt(..) | CmpUgt(..) => Some(0), + + _ => Some(self.raw_struct().size), + + //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO + } + } + + pub fn address(&self) -> u64 { + self.raw_struct().address + } + + /// Determines if the expressions represent the same operation + /// + /// It does not examine the operands for equality. + pub fn is_same_op_as(&self, other: &Self) -> bool { + use self::ExprInfo::*; + + match (self, other) { + (&Reg(..), &Reg(..)) => true, + _ => self.raw_struct().operation == other.raw_struct().operation, + } + } + + pub fn as_cmp_op(&self) -> Option<&Operation<'func, A, M, F, operation::Condition>> { + use self::ExprInfo::*; + + match *self { + CmpE (ref op) | CmpNe (ref op) | + CmpSlt(ref op) | CmpUlt(ref op) | + CmpSle(ref op) | CmpUle(ref op) | + CmpSge(ref op) | CmpUge(ref op) | + CmpSgt(ref op) | CmpUgt(ref op) => Some(op), + _ => None, + } + } + + pub fn as_binary_op(&self) -> Option<&Operation<'func, A, M, F, operation::BinaryOp>> { + use self::ExprInfo::*; + + match *self { + Add(ref op) | + Sub(ref op) | + And(ref op) | + Or (ref op) | + Xor(ref op) | + Lsl(ref op) | + Lsr(ref op) | + Asr(ref op) | + Rol(ref op) | + Ror(ref op) | + Mul(ref op) | + MulsDp(ref op) | + MuluDp(ref op) | + Divu(ref op) | + Divs(ref op) | + Modu(ref op) | + Mods(ref op) => Some(op), + _ => None, + } + } + + pub fn as_binary_op_carry(&self) -> Option<&Operation<'func, A, M, F, operation::BinaryOpCarry>> { + use self::ExprInfo::*; + + match *self { + Adc(ref op) | + Sbb(ref op) | + Rlc(ref op) | + Rrc(ref op) => Some(op), + _ => None, + } + } + + pub fn as_double_prec_div_op(&self) -> Option<&Operation<'func, A, M, F, operation::DoublePrecDivOp>> { + use self::ExprInfo::*; + + match *self { + DivuDp(ref op) | + DivsDp(ref op) | + ModuDp(ref op) | + ModsDp(ref op) => Some(op), + _ => None, + } + } + + pub fn as_unary_op(&self) -> Option<&Operation<'func, A, M, F, operation::UnaryOp>> { + use self::ExprInfo::*; + + match *self { + Neg(ref op) | + Not(ref op) | + Sx(ref op) | + Zx(ref op) | + LowPart(ref op) | + BoolToInt(ref op) => Some(op), + _ => None, + } + } + + pub(crate) fn raw_struct(&self) -> &BNLowLevelILInstruction { + use self::ExprInfo::*; + + match *self { + Undef(ref op) => &op.op, + + Unimpl(ref op) => &op.op, + + FlagCond(ref op) => &op.op, + FlagGroup(ref op) => &op.op, + + CmpE (ref op) | CmpNe (ref op) | + CmpSlt(ref op) | CmpUlt(ref op) | + CmpSle(ref op) | CmpUle(ref op) | + CmpSge(ref op) | CmpUge(ref op) | + CmpSgt(ref op) | CmpUgt(ref op) => &op.op, + + Load(ref op) => &op.op, + + Pop(ref op) => &op.op, + + Reg(ref op) => &op.op, + + Flag(ref op) => &op.op, + + FlagBit(ref op) => &op.op, + + Const(ref op) | + ConstPtr(ref op) => &op.op, + + Adc(ref op) | + Sbb(ref op) | + Rlc(ref op) | + Rrc(ref op) => &op.op, + + Add(ref op) | + Sub(ref op) | + And(ref op) | + Or (ref op) | + Xor(ref op) | + Lsl(ref op) | + Lsr(ref op) | + Asr(ref op) | + Rol(ref op) | + Ror(ref op) | + Mul(ref op) | + MulsDp(ref op) | + MuluDp(ref op) | + Divu(ref op) | + Divs(ref op) | + Modu(ref op) | + Mods(ref op) => &op.op, + + DivuDp(ref op) | + DivsDp(ref op) | + ModuDp(ref op) | + ModsDp(ref op) => &op.op, + + Neg(ref op) | + Not(ref op) | + Sx(ref op) | + Zx(ref op) | + LowPart(ref op) | + BoolToInt(ref op) => &op.op, + + UnimplMem(ref op) => &op.op, + + //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO + } + } +} + +impl<'func, A> ExprInfo<'func, A, Mutable, NonSSA<LiftedNonSSA>> +where + A: 'func + Architecture, +{ + + pub fn flag_write(&self) -> Option<A::FlagWrite> { + use self::ExprInfo::*; + + match *self { + Undef(ref op) => None, + + Unimpl(ref op) => None, + + FlagCond(ref op) => None, + FlagGroup(ref op) => None, + + CmpE (ref op) | CmpNe (ref op) | + CmpSlt(ref op) | CmpUlt(ref op) | + CmpSle(ref op) | CmpUle(ref op) | + CmpSge(ref op) | CmpUge(ref op) | + CmpSgt(ref op) | CmpUgt(ref op) => None, + + Load(ref op) => op.flag_write(), + + Pop(ref op) => op.flag_write(), + + Reg(ref op) => op.flag_write(), + + Flag(ref op) => op.flag_write(), + + FlagBit(ref op) => op.flag_write(), + + Const(ref op) | + ConstPtr(ref op) => op.flag_write(), + + Adc(ref op) | + Sbb(ref op) | + Rlc(ref op) | + Rrc(ref op) => op.flag_write(), + + Add(ref op) | + Sub(ref op) | + And(ref op) | + Or (ref op) | + Xor(ref op) | + Lsl(ref op) | + Lsr(ref op) | + Asr(ref op) | + Rol(ref op) | + Ror(ref op) | + Mul(ref op) | + MulsDp(ref op) | + MuluDp(ref op) | + Divu(ref op) | + Divs(ref op) | + Modu(ref op) | + Mods(ref op) => op.flag_write(), + + DivuDp(ref op) | + DivsDp(ref op) | + ModuDp(ref op) | + ModsDp(ref op) => op.flag_write(), + + Neg(ref op) | + Not(ref op) | + Sx(ref op) | + Zx(ref op) | + LowPart(ref op) | + BoolToInt(ref op) => op.flag_write(), + + UnimplMem(ref op) => op.flag_write(), + + //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO + } + } +} + +impl<'func, A, M, V> fmt::Debug for ExprInfo<'func, A, M, NonSSA<V>> +where + A: 'func + Architecture, + M: FunctionMutability, + V: NonSSAVariant, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::ExprInfo::*; + + match *self { + Undef(..) => f.write_str("undefined"), + + Unimpl(..) => f.write_str("unimplemented"), + + FlagCond(..) => f.write_str("some_flag_cond"), + FlagGroup(..) => f.write_str("some_flag_group"), + + CmpE(ref op) | CmpNe(ref op) | + CmpSlt(ref op) | CmpUlt(ref op) | + CmpSle(ref op) | CmpUle(ref op) | + CmpSge(ref op) | CmpUge(ref op) | + CmpSgt(ref op) | CmpUgt(ref op) => { + let left = op.left(); + let right = op.right(); + + write!(f, "{:?}({}, {:?}, {:?})", op.op.operation, op.size(), left, right) + } + + Load(ref op) => { + let source = op.source_mem_expr(); + let size = op.size(); + + write!(f, "[{:?}].{}", source, size) + } + + Pop(ref op) => write!(f, "pop.{}", op.size()), + + Reg(ref op) => { + let reg = op.source_reg(); + let size = op.size(); + + let size = match reg { + Register::Temp(_) => Some(size), + Register::ArchReg(ref r) if r.info().size() != size => Some(size), + _ => None + }; + + match size { + Some(s) => write!(f, "{:?}.{}", reg, s), + _ => write!(f, "{:?}", reg), + } + } + + Flag(ref _op) => write!(f, "flag"), // TODO + + FlagBit(ref _op) => write!(f, "flag_bit"), // TODO + + Const(ref op) | + ConstPtr(ref op) => write!(f, "0x{:x}", op.value()), + + Adc(ref op) | + Sbb(ref op) | + Rlc(ref op) | + Rrc(ref op) => { + let left = op.left(); + let right = op.right(); + let carry = op.carry(); + + write!(f, "{:?}({}, {:?}, {:?}, carry: {:?})", + op.op.operation, op.size(), left, right, carry) + } + + Add(ref op) | + Sub(ref op) | + And(ref op) | + Or (ref op) | + Xor(ref op) | + Lsl(ref op) | + Lsr(ref op) | + Asr(ref op) | + Rol(ref op) | + Ror(ref op) | + Mul(ref op) | + MulsDp(ref op) | + MuluDp(ref op) | + Divu(ref op) | + Divs(ref op) | + Modu(ref op) | + Mods(ref op) => { + let left = op.left(); + let right = op.right(); + + write!(f, "{:?}({}, {:?}, {:?})", + op.op.operation, op.size(), left, right) + } + + DivuDp(ref op) | + DivsDp(ref op) | + ModuDp(ref op) | + ModsDp(ref op) => { + let high = op.high(); + let low = op.low(); + let right = op.right(); + + write!(f, "{:?}({}, {:?}:{:?},{:?})", + op.op.operation, op.size(), high, low, right) + } + + Neg(ref op) | + Not(ref op) | + Sx(ref op) | + Zx(ref op) | + LowPart(ref op) | + BoolToInt(ref op) => { + write!(f, "{:?}({}, {:?})", op.op.operation, op.size(), op.operand()) + } + + UnimplMem(ref op) => write!(f, "unimplemented_mem({:?})", op.mem_expr()), + + //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO + } + } +} diff --git a/rust/src/llil/function.rs b/rust/src/llil/function.rs new file mode 100644 index 00000000..ac50e72c --- /dev/null +++ b/rust/src/llil/function.rs @@ -0,0 +1,198 @@ +use binaryninjacore_sys::BNLowLevelILFunction; +use binaryninjacore_sys::BNNewLowLevelILFunctionReference; +use binaryninjacore_sys::BNFreeLowLevelILFunction; + +use std::borrow::Borrow; +use std::marker::PhantomData; + +use crate::basicblock::BasicBlock; +use crate::rc::*; + +use super::*; + +#[derive(Copy, Clone, Debug)] +pub struct Mutable; +#[derive(Copy, Clone, Debug)] +pub struct Finalized; + +pub trait FunctionMutability: 'static {} +impl FunctionMutability for Mutable {} +impl FunctionMutability for Finalized {} + + +#[derive(Copy, Clone, Debug)] +pub struct LiftedNonSSA; +#[derive(Copy, Clone, Debug)] +pub struct RegularNonSSA; + +pub trait NonSSAVariant: 'static {} +impl NonSSAVariant for LiftedNonSSA {} +impl NonSSAVariant for RegularNonSSA {} + +#[derive(Copy, Clone, Debug)] +pub struct SSA; +#[derive(Copy, Clone, Debug)] +pub struct NonSSA<V: NonSSAVariant>(V); + +pub trait FunctionForm: 'static {} +impl FunctionForm for SSA {} +impl<V: NonSSAVariant> FunctionForm for NonSSA<V> {} + + +pub struct Function<A: Architecture, M: FunctionMutability, F: FunctionForm> { + pub(crate) borrower: A::Handle, + pub(crate) handle: *mut BNLowLevelILFunction, + _arch: PhantomData<*mut A>, + _mutability: PhantomData<M>, + _form: PhantomData<F>, +} + +unsafe impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Send for Function<A, M, F> {} +unsafe impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Sync for Function<A, M, F> {} + +impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Eq for Function<A, M, F> {} +impl<A: Architecture, M: FunctionMutability, F: FunctionForm> PartialEq for Function<A, M, F> { + fn eq(&self, rhs: &Self) -> bool { + self.handle == rhs.handle + } +} + +use std::hash::{Hash, Hasher}; +impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Hash for Function<A, M, F> { + fn hash<H: Hasher>(&self, state: &mut H) { + self.handle.hash(state); + } +} + +impl<'func, A, M, F> Function<A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm +{ + pub(crate) unsafe fn from_raw(borrower: A::Handle, handle: *mut BNLowLevelILFunction) -> Self { + debug_assert!(!handle.is_null()); + + Self { + borrower, + handle, + _arch: PhantomData, + _mutability: PhantomData, + _form: PhantomData, + } + } + + pub(crate) fn arch(&self) -> &A { + self.borrower.borrow() + } + + pub fn instruction_at<L: Into<Location>>(&self, loc: L) -> Option<Instruction<A, M, F>> { + use binaryninjacore_sys::BNLowLevelILGetInstructionStart; + use binaryninjacore_sys::BNGetLowLevelILInstructionCount; + + let loc: Location = loc.into(); + let arch_handle = loc.arch.unwrap_or_else(|| *self.arch().as_ref()); + + unsafe { + let instr_idx = BNLowLevelILGetInstructionStart(self.handle, arch_handle.0, loc.addr); + + if instr_idx >= BNGetLowLevelILInstructionCount(self.handle) { + None + } else { + Some(Instruction { + function: self, + instr_idx: instr_idx, + }) + } + } + } + + pub fn instruction_from_idx(&self, instr_idx: usize) -> Instruction<A, M, F> { + unsafe { + use binaryninjacore_sys::BNGetLowLevelILInstructionCount; + if instr_idx >= BNGetLowLevelILInstructionCount(self.handle) { + panic!("instruction index {} out of bounds", instr_idx); + } + + Instruction { + function: self, + instr_idx: instr_idx, + } + } + } + + pub fn instruction_count(&self) -> usize { + unsafe { + use binaryninjacore_sys::BNGetLowLevelILInstructionCount; + BNGetLowLevelILInstructionCount(self.handle) + } + } + +} + +// LLIL basic blocks are not available until the function object +// is finalized, so ensure we can't try requesting basic blocks +// during lifting +impl<'func, A, F> Function<A, Finalized, F> +where + A: 'func + Architecture, + F: FunctionForm +{ + pub fn basic_blocks(&self) -> Array<BasicBlock<LowLevelBlock<A, Finalized, F>>> { + use binaryninjacore_sys::BNGetLowLevelILBasicBlockList; + + unsafe { + let mut count = 0; + let blocks = BNGetLowLevelILBasicBlockList(self.handle, &mut count); + let context = LowLevelBlock { function: self }; + + Array::new(blocks, count, context) + } + } +} + +impl<'func, A, M, F> ToOwned for Function<A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm +{ + type Owned = Ref<Self>; + + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } + } +} + + +unsafe impl<'func, A, M, F> RefCountable for Function<A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm +{ + unsafe fn inc_ref(handle: &Self) -> Ref<Self> { + Ref::new(Self { + borrower: handle.borrower.clone(), + handle: BNNewLowLevelILFunctionReference(handle.handle), + _arch: PhantomData, + _mutability: PhantomData, + _form: PhantomData, + }) + } + + unsafe fn dec_ref(handle: &Self) { + BNFreeLowLevelILFunction(handle.handle); + } +} + +impl<'func, A, M, F> fmt::Debug for Function<A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "<llil func handle {:p}>", self.handle) + } +} diff --git a/rust/src/llil/instruction.rs b/rust/src/llil/instruction.rs new file mode 100644 index 00000000..4d1554df --- /dev/null +++ b/rust/src/llil/instruction.rs @@ -0,0 +1,175 @@ +use binaryninjacore_sys::BNGetLowLevelILByIndex; +use binaryninjacore_sys::BNGetLowLevelILIndexForInstruction; +use binaryninjacore_sys::BNLowLevelILInstruction; + +use std::marker::PhantomData; + +use super::*; +use super::operation; +use super::operation::Operation; + +use crate::architecture::Architecture; + +pub struct Instruction<'func, A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub(crate) function: &'func Function<A, M, F>, + pub(crate) instr_idx: usize, +} + +fn common_info<'func, A, M, F>(function: &'func Function<A, M, F>, op: BNLowLevelILInstruction) + -> Option<InstrInfo<'func, A, M, F>> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + use binaryninjacore_sys::BNLowLevelILOperation::*; + + match op.operation { + LLIL_NOP => InstrInfo::Nop(Operation::new(function, op)).into(), + LLIL_JUMP => InstrInfo::Jump(Operation::new(function, op)).into(), + LLIL_JUMP_TO => InstrInfo::JumpTo(Operation::new(function, op)).into(), + LLIL_RET => InstrInfo::Ret(Operation::new(function, op)).into(), + LLIL_NORET => InstrInfo::NoRet(Operation::new(function, op)).into(), + LLIL_IF => InstrInfo::If(Operation::new(function, op)).into(), + LLIL_GOTO => InstrInfo::Goto(Operation::new(function, op)).into(), + LLIL_BP => InstrInfo::Bp(Operation::new(function, op)).into(), + LLIL_TRAP => InstrInfo::Trap(Operation::new(function, op)).into(), + LLIL_UNDEF => InstrInfo::Undef(Operation::new(function, op)).into(), + _ => None, + } +} + +use super::VisitorAction; + +macro_rules! visit { + ($f:expr, $($e:expr),*) => { + if let VisitorAction::Halt = $f($($e,)*) { + return VisitorAction::Halt; + } + } +} + +fn common_visit<'func, A, M, F, CB>(info: &InstrInfo<'func, A, M, F>, f: &mut CB) + -> VisitorAction +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, + CB: FnMut(&Expression<'func, A, M, F, ValueExpr>) -> VisitorAction, +{ + use self::InstrInfo::*; + + match *info { + Jump(ref op) => visit!(f, &op.target()), + JumpTo(ref op) => visit!(f, &op.target()), + Ret(ref op) => visit!(f, &op.target()), + If(ref op) => visit!(f, &op.condition()), + Value(ref e, _) => visit!(f, e), + _ => {}, + }; + + VisitorAction::Sibling +} + +impl<'func, A, M, V> Instruction<'func, A, M, NonSSA<V>> +where + A: 'func + Architecture, + M: FunctionMutability, + V: NonSSAVariant, +{ + pub fn info(&self) -> InstrInfo<'func, A, M, NonSSA<V>> { + use binaryninjacore_sys::BNLowLevelILOperation::*; + + let expr_idx = unsafe { BNGetLowLevelILIndexForInstruction(self.function.handle, self.instr_idx) }; + let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, expr_idx) }; + + match op.operation { + LLIL_SET_REG => InstrInfo::SetReg(Operation::new(self.function, op)), + LLIL_SET_REG_SPLIT => InstrInfo::SetRegSplit(Operation::new(self.function, op)), + LLIL_SET_FLAG => InstrInfo::SetFlag(Operation::new(self.function, op)), + LLIL_STORE => InstrInfo::Store(Operation::new(self.function, op)), + LLIL_PUSH => InstrInfo::Push(Operation::new(self.function, op)), + LLIL_CALL | + LLIL_CALL_STACK_ADJUST => InstrInfo::Call(Operation::new(self.function, op)), + LLIL_SYSCALL => InstrInfo::Syscall(Operation::new(self.function, op)), + _ => { + common_info(self.function, op).unwrap_or_else(|| { + // Hopefully this is a bare value. If it isn't (expression + // from wrong function form or similar) it won't really cause + // any problems as it'll come back as undefined when queried. + let expr = Expression { + function: self.function, + expr_idx: expr_idx, + _ty: PhantomData, + }; + + let info = unsafe { expr.info_from_op(op) }; + + InstrInfo::Value(expr, info) + }) + } + } + } + + pub fn visit_tree<F>(&self, f: &mut F) -> VisitorAction + where + F: FnMut(&Expression<'func, A, M, NonSSA<V>, ValueExpr>, &ExprInfo<'func, A, M, NonSSA<V>>) -> VisitorAction, + { + use self::InstrInfo::*; + let info = self.info(); + + let fb = &mut |e: &Expression<'func, A, M, NonSSA<V>, ValueExpr>| e.visit_tree(f); + + match info { + SetReg(ref op) => visit!(fb, &op.source_expr()), + SetRegSplit(ref op) => visit!(fb, &op.source_expr()), + SetFlag(ref op) => visit!(fb, &op.source_expr()), + Store(ref op) => { + visit!(fb, &op.dest_mem_expr()); + visit!(fb, &op.source_expr()); + } + Push(ref op) => visit!(fb, &op.operand()), + Call(ref op) => visit!(fb, &op.target()), + _ => visit!(common_visit, &info, fb), + } + + VisitorAction::Sibling + } +} + +pub enum InstrInfo<'func, A, M, F> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + Nop(Operation<'func, A, M, F, operation::NoArgs>), + SetReg(Operation<'func, A, M, F, operation::SetReg>), + SetRegSplit(Operation<'func, A, M, F, operation::SetRegSplit>), + SetFlag(Operation<'func, A, M, F, operation::SetFlag>), + Store(Operation<'func, A, M, F, operation::Store>), + Push(Operation<'func, A, M, F, operation::UnaryOp>), // TODO needs a real op + + Jump(Operation<'func, A, M, F, operation::Jump>), + JumpTo(Operation<'func, A, M, F, operation::JumpTo>), + + Call(Operation<'func, A, M, F, operation::Call>), + + Ret(Operation<'func, A, M, F, operation::Ret>), + NoRet(Operation<'func, A, M, F, operation::NoArgs>), + + If(Operation<'func, A, M, F, operation::If>), + Goto(Operation<'func, A, M, F, operation::Goto>), + + Syscall(Operation<'func, A, M, F, operation::Syscall>), + Bp(Operation<'func, A, M, F, operation::NoArgs>), + Trap(Operation<'func, A, M, F, operation::Trap>), + Undef(Operation<'func, A, M, F, operation::NoArgs>), + + Value(Expression<'func, A, M, F, ValueExpr>, ExprInfo<'func, A, M, F>), +} 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 + } + } +} + + diff --git a/rust/src/llil/mod.rs b/rust/src/llil/mod.rs new file mode 100644 index 00000000..c3b1b350 --- /dev/null +++ b/rust/src/llil/mod.rs @@ -0,0 +1,80 @@ +use std::fmt; + +// TODO provide some way to forbid emitting register reads for certain registers +// also writing for certain registers (e.g. zero register must prohibit il.set_reg and il.reg +// (replace with nop or const(0) respectively) +// requirements on load/store memory address sizes? +// can reg/set_reg be used with sizes that differ from what is in BNRegisterInfo? + +use crate::architecture::Register as ArchReg; +use crate::architecture::Architecture; +use crate::function::Location; + +mod function; +mod instruction; +mod expression; +mod lifting; +mod block; +pub mod operation; + +pub use self::function::*; +pub use self::instruction::*; +pub use self::expression::*; +pub use self::lifting::{Liftable, LiftableWithSize, Label, ExpressionBuilder, FlagWriteOp, RegisterOrConstant}; +pub use self::lifting::get_default_flag_write_llil; +pub use self::lifting::get_default_flag_cond_llil; + +pub use self::block::Block as LowLevelBlock; +pub use self::block::BlockIter as LowLevelBlockIter; + +pub type Lifter<Arch> = Function<Arch, Mutable, NonSSA<LiftedNonSSA>>; +pub type LiftedFunction<Arch> = Function<Arch, Finalized, NonSSA<LiftedNonSSA>>; +pub type LiftedExpr<'a, Arch> = Expression<'a, Arch, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>; +pub type RegularFunction<Arch> = Function<Arch, Finalized, NonSSA<RegularNonSSA>>; +pub type SSAFunction<Arch> = Function<Arch, Finalized, SSA>; + +#[derive(Copy, Clone)] +pub enum Register<R: ArchReg> { + ArchReg(R), + Temp(u32), +} + +impl<R: ArchReg> Register<R> { + fn id(&self) -> u32 { + match *self { + Register::ArchReg(ref r) => r.id(), + Register::Temp(id) => 0x8000_0000 | id, + } + } +} + +impl<R: ArchReg> fmt::Debug for Register<R> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Register::ArchReg(ref r) => write!(f, "{}", r.name().as_ref()), + Register::Temp(id) => write!(f, "temp{}", id), + } + } +} + +#[derive(Copy, Clone, Debug)] +pub enum SSARegister<R: ArchReg> { + Full(Register<R>, u32), // no such thing as partial access to a temp register, I think + Partial(R, u32, R), // partial accesses only possible for arch registers, I think +} + +impl<R: ArchReg> SSARegister<R> { + pub fn version(&self) -> u32 { + match *self { + SSARegister::Full(_, ver) | + SSARegister::Partial(_, ver, _) => ver + } + } +} + +pub enum VisitorAction { + Descend, + Sibling, + Halt, +} + diff --git a/rust/src/llil/operation.rs b/rust/src/llil/operation.rs new file mode 100644 index 00000000..81d72d57 --- /dev/null +++ b/rust/src/llil/operation.rs @@ -0,0 +1,765 @@ +use binaryninjacore_sys::BNLowLevelILInstruction; + +use std::marker::PhantomData; +use std::mem; + +use super::*; + +pub struct Operation<'func, A, M, F, O> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, + O: OperationArguments, +{ + pub(crate) function: &'func Function<A, M, F>, + pub(crate) op: BNLowLevelILInstruction, + _args: PhantomData<O>, +} + +impl<'func, A, M, F, O> Operation<'func, A, M, F, O> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, + O: OperationArguments, +{ + pub(crate) fn new(function: &'func Function<A, M, F>, op: BNLowLevelILInstruction) -> Self { + Self { + function: function, + op: op, + _args: PhantomData, + } + } + + pub fn address(&self) -> u64 { + self.op.address + } +} + +impl<'func, A, M, O> Operation<'func, A, M, NonSSA<LiftedNonSSA>, O> +where + A: 'func + Architecture, + M: FunctionMutability, + O: OperationArguments, +{ + pub fn flag_write(&self) -> Option<A::FlagWrite> { + match self.op.flags { + 0 => None, + id => self.function.arch().flag_write_from_id(id) + } + } +} + +// LLIL_NOP, LLIL_NORET, LLIL_BP, LLIL_UNDEF, LLIL_UNIMPL +pub struct NoArgs; + + + +// LLIL_POP +pub struct Pop; + +impl<'func, A, M, F> Operation<'func, A, M, F, Pop> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } +} + + + + +// LLIL_SYSCALL, LLIL_SYSCALL_SSA +pub struct Syscall; + + + + +// LLIL_SET_REG, LLIL_SET_REG_SSA, LLIL_SET_REG_PARTIAL_SSA +pub struct SetReg; + +impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, SetReg> +where + A: 'func + Architecture, + M: FunctionMutability, + V: NonSSAVariant, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn dest_reg(&self) -> Register<A::Register> { + let raw_id = self.op.operands[0] as u32; + + if raw_id >= 0x8000_0000 { + Register::Temp(raw_id & 0x7fff_ffff) + } else { + self.function.arch().register_from_id(raw_id) + .map(Register::ArchReg) + .unwrap_or_else(|| { + error!("got garbage register from LLIL_SET_REG @ 0x{:x}", + self.op.address); + + Register::Temp(0) + }) + } + } + + pub fn source_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[1] as usize, + _ty: PhantomData, + } + } +} + + +// LLIL_SET_REG_SPLIT, LLIL_SET_REG_SPLIT_SSA +pub struct SetRegSplit; + +impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, SetRegSplit> +where + A: 'func + Architecture, + M: FunctionMutability, + V: NonSSAVariant, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn dest_reg_high(&self) -> Register<A::Register> { + let raw_id = self.op.operands[0] as u32; + + if raw_id >= 0x8000_0000 { + Register::Temp(raw_id & 0x7fff_ffff) + } else { + self.function.arch().register_from_id(raw_id) + .map(Register::ArchReg) + .unwrap_or_else(|| { + error!("got garbage register from LLIL_SET_REG_SPLIT @ 0x{:x}", + self.op.address); + + Register::Temp(0) + }) + } + } + + pub fn dest_reg_low(&self) -> Register<A::Register> { + let raw_id = self.op.operands[1] as u32; + + if raw_id >= 0x8000_0000 { + Register::Temp(raw_id & 0x7fff_ffff) + } else { + self.function.arch().register_from_id(raw_id) + .map(Register::ArchReg) + .unwrap_or_else(|| { + error!("got garbage register from LLIL_SET_REG_SPLIT @ 0x{:x}", + self.op.address); + + Register::Temp(0) + }) + } + } + + pub fn source_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[2] as usize, + _ty: PhantomData, + } + } +} + + + + +// LLIL_SET_FLAG, LLIL_SET_FLAG_SSA +pub struct SetFlag; + +impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, SetFlag> +where + A: 'func + Architecture, + M: FunctionMutability, + V: NonSSAVariant, +{ + pub fn source_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[1] as usize, + _ty: PhantomData, + } + } +} + + +// LLIL_LOAD, LLIL_LOAD_SSA +pub struct Load; + +impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, Load> +where + A: 'func + Architecture, + M: FunctionMutability, + V: NonSSAVariant, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn source_mem_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } +} + + + +// LLIL_STORE, LLIL_STORE_SSA +pub struct Store; + +impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, Store> +where + A: 'func + Architecture, + M: FunctionMutability, + V: NonSSAVariant, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn dest_mem_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } + + pub fn source_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[1] as usize, + _ty: PhantomData, + } + } +} + + + +// LLIL_REG, LLIL_REG_SSA, LLIL_REG_SSA_PARTIAL +pub struct Reg; + +impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, Reg> +where + A: 'func + Architecture, + M: FunctionMutability, + V: NonSSAVariant, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn source_reg(&self) -> Register<A::Register> { + let raw_id = self.op.operands[0] as u32; + + if raw_id >= 0x8000_0000 { + Register::Temp(raw_id & 0x7fff_ffff) + } else { + self.function.arch().register_from_id(raw_id) + .map(Register::ArchReg) + .unwrap_or_else(|| { + error!("got garbage register from LLIL_REG @ 0x{:x}", + self.op.address); + + Register::Temp(0) + }) + } + } +} + + + +// LLIL_FLAG, LLIL_FLAG_SSA +pub struct Flag; + + + + +// LLIL_FLAG_BIT, LLIL_FLAG_BIT_SSA +pub struct FlagBit; + + + +// LLIL_JUMP +pub struct Jump; + +impl<'func, A, M, F> Operation<'func, A, M, F, Jump> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn target(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } +} + + + +// LLIL_JUMP_TO +pub struct JumpTo; + +impl<'func, A, M, F> Operation<'func, A, M, F, JumpTo> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn target(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } + // TODO target list +} + + + +// LLIL_CALL, LLIL_CALL_SSA +pub struct Call; + +impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, Call> +where + A: 'func + Architecture, + M: FunctionMutability, + V: NonSSAVariant, +{ + pub fn target(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } + + pub fn stack_adjust(&self) -> Option<u64> { + use binaryninjacore_sys::BNLowLevelILOperation::LLIL_CALL_STACK_ADJUST; + + if self.op.operation == LLIL_CALL_STACK_ADJUST { + Some(self.op.operands[1]) + } else { + None + } + } +} + + + +// LLIL_RET +pub struct Ret; + +impl<'func, A, M, F> Operation<'func, A, M, F, Ret> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn target(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } +} + + + +// LLIL_IF +pub struct If; + +impl<'func, A, M, F> Operation<'func, A, M, F, If> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn condition(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } + + pub fn true_target(&self) -> Instruction<'func, A, M, F> { + Instruction { + function: self.function, + instr_idx: self.op.operands[1] as usize, + } + } + + pub fn false_target(&self) -> Instruction<'func, A, M, F> { + Instruction { + function: self.function, + instr_idx: self.op.operands[2] as usize, + } + } +} + + + +// LLIL_GOTO +pub struct Goto; + +impl<'func, A, M, F> Operation<'func, A, M, F, Goto> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn target(&self) -> Instruction<'func, A, M, F> { + Instruction { + function: self.function, + instr_idx: self.op.operands[0] as usize, + } + } +} + + + +// LLIL_FLAG_COND +pub struct FlagCond; + + + +// LLIL_FLAG_GROUP +pub struct FlagGroup; + +impl<'func, A, M> Operation<'func, A, M, NonSSA<LiftedNonSSA>, FlagGroup> +where + A: 'func + Architecture, + M: FunctionMutability, +{ + pub fn flag_group(&self) -> A::FlagGroup { + let id = self.op.operands[0] as u32; + self.function.arch().flag_group_from_id(id).unwrap() + } +} + + + +// LLIL_TRAP +pub struct Trap; + +impl<'func, A, M, F> Operation<'func, A, M, F, Trap> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn vector(&self) -> u64 { + self.op.operands[0] + } +} + + + +// LLIL_REG_PHI +pub struct RegPhi; + + + +// LLIL_FLAG_PHI +pub struct FlagPhi; + + + +// LLIL_MEM_PHI +pub struct MemPhi; + + + +// LLIL_CONST, LLIL_CONST_PTR +pub struct Const; + +impl<'func, A, M, F> Operation<'func, A, M, F, Const> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn value(&self) -> u64 { + #[cfg(debug_assertions)] + { + let raw = self.op.operands[0] as i64; + + let is_safe = match raw.overflowing_shr(self.op.size as u32 * 8) { + (_, true) => true, + (res, false) => [-1, 0].contains(&res), + }; + + if !is_safe { + error!("il expr @ {:x} contains constant 0x{:x} as {} byte value (doesn't fit!)", + self.op.address, self.op.operands[0], self.op.size); + } + } + + let mut mask = -1i64 as u64; + + if self.op.size < mem::size_of::<u64>() { + mask <<= self.op.size * 8; + mask = !mask; + } + + self.op.operands[0] & mask + } +} + + + +// LLIL_ADD, LLIL_SUB, LLIL_AND, LLIL_OR +// LLIL_XOR, LLIL_LSL, LLIL_LSR, LLIL_ASR +// LLIL_ROL, LLIL_ROR, LLIL_MUL, LLIL_MULU_DP, +// LLIL_MULS_DP, LLIL_DIVU, LLIL_DIVS, LLIL_MODU, +// LLIL_MODS +pub struct BinaryOp; + +impl<'func, A, M, F> Operation<'func, A, M, F, BinaryOp> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn left(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } + + pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[1] as usize, + _ty: PhantomData, + } + } +} + + + +// LLIL_ADC, LLIL_SBB, LLIL_RLC, LLIL_RRC +pub struct BinaryOpCarry; + +impl<'func, A, M, F> Operation<'func, A, M, F, BinaryOpCarry> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn left(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } + + pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[1] as usize, + _ty: PhantomData, + } + } + + pub fn carry(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[2] as usize, + _ty: PhantomData, + } + } +} + + + +// LLIL_DIVS_DP, LLIL_DIVU_DP, LLIL_MODU_DP, LLIL_MODS_DP +pub struct DoublePrecDivOp; + +impl<'func, A, M, F> Operation<'func, A, M, F, DoublePrecDivOp> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn high(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } + + pub fn low(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[1] as usize, + _ty: PhantomData, + } + } + + pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[2] as usize, + _ty: PhantomData, + } + } +} + + + +// LLIL_PUSH, LLIL_NEG, LLIL_NOT, LLIL_SX, +// LLIL_ZX, LLIL_LOW_PART, LLIL_BOOL_TO_INT, LLIL_UNIMPL_MEM +pub struct UnaryOp; + +impl<'func, A, M, F> Operation<'func, A, M, F, UnaryOp> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn operand(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } +} + + + +// LLIL_CMP_X +pub struct Condition; + +impl<'func, A, M, F> Operation<'func, A, M, F, Condition> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn left(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } + + pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[1] as usize, + _ty: PhantomData, + } + } +} + + +// LLIL_UNIMPL_MEM +pub struct UnimplMem; + +impl<'func, A, M, F> Operation<'func, A, M, F, UnimplMem> +where + A: 'func + Architecture, + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn mem_expr(&self) -> Expression<'func, A, M, F, ValueExpr> { + Expression { + function: self.function, + expr_idx: self.op.operands[0] as usize, + _ty: PhantomData, + } + } +} + +// TODO TEST_BIT + +pub trait OperationArguments: 'static {} + +impl OperationArguments for NoArgs {} +impl OperationArguments for Pop {} +impl OperationArguments for Syscall {} +impl OperationArguments for SetReg {} +impl OperationArguments for SetRegSplit {} +impl OperationArguments for SetFlag {} +impl OperationArguments for Load {} +impl OperationArguments for Store {} +impl OperationArguments for Reg {} +impl OperationArguments for Flag {} +impl OperationArguments for FlagBit {} +impl OperationArguments for Jump {} +impl OperationArguments for JumpTo {} +impl OperationArguments for Call {} +impl OperationArguments for Ret {} +impl OperationArguments for If {} +impl OperationArguments for Goto {} +impl OperationArguments for FlagCond {} +impl OperationArguments for FlagGroup {} +impl OperationArguments for Trap {} +impl OperationArguments for RegPhi {} +impl OperationArguments for FlagPhi {} +impl OperationArguments for MemPhi {} +impl OperationArguments for Const {} +impl OperationArguments for BinaryOp {} +impl OperationArguments for BinaryOpCarry {} +impl OperationArguments for DoublePrecDivOp {} +impl OperationArguments for UnaryOp {} +impl OperationArguments for Condition {} +impl OperationArguments for UnimplMem {} |
