use super::lift::*; use super::operation::*; use super::{MediumLevelILBlock, MediumLevelILFunction}; use crate::architecture::{CoreIntrinsic, FlagId, IntrinsicId, RegisterId}; use crate::basic_block::BasicBlock; use crate::confidence::Conf; use crate::disassembly::InstructionTextToken; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref}; use crate::types::Type; use crate::variable::{ConstantData, PossibleValueSet, RegisterValue, SSAVariable, Variable}; use crate::{DataFlowQueryOption, ILBranchDependence}; use binaryninjacore_sys::*; use std::collections::BTreeMap; use std::fmt; use std::fmt::{Debug, Display, Formatter}; #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct MediumLevelInstructionIndex(pub usize); impl MediumLevelInstructionIndex { pub fn next(&self) -> Self { Self(self.0 + 1) } } impl From for MediumLevelInstructionIndex { fn from(index: usize) -> Self { Self(index) } } impl From for MediumLevelInstructionIndex { fn from(index: u64) -> Self { Self(index as usize) } } impl Display for MediumLevelInstructionIndex { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.write_fmt(format_args!("{}", self.0)) } } #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct MediumLevelExpressionIndex(pub usize); impl MediumLevelExpressionIndex { pub fn next(&self) -> Self { Self(self.0 + 1) } } impl From for MediumLevelExpressionIndex { fn from(index: usize) -> Self { Self(index) } } impl From for MediumLevelExpressionIndex { fn from(index: u64) -> Self { Self(index as usize) } } impl Display for MediumLevelExpressionIndex { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.write_fmt(format_args!("{}", self.0)) } } #[derive(Clone)] pub struct MediumLevelILInstruction { pub function: Ref, pub address: u64, pub instr_index: MediumLevelInstructionIndex, pub expr_index: MediumLevelExpressionIndex, pub size: usize, pub kind: MediumLevelILInstructionKind, } impl MediumLevelILInstruction { pub(crate) fn from_instr_index( function: Ref, instr_index: MediumLevelInstructionIndex, ) -> Self { // Get the associated expression index for the top-level instruction. let expr_index_raw = unsafe { BNGetMediumLevelILIndexForInstruction(function.handle, instr_index.0) }; Self::new( function, instr_index, MediumLevelExpressionIndex(expr_index_raw), ) } pub(crate) fn from_expr_index( function: Ref, expr_index: MediumLevelExpressionIndex, ) -> Self { // Get the associated top-level instruction index for the expression. let instr_index_raw = unsafe { BNGetMediumLevelILInstructionForExpr(function.handle, expr_index.0) }; Self::new( function, MediumLevelInstructionIndex(instr_index_raw), expr_index, ) } pub(crate) fn new( function: Ref, instr_index: MediumLevelInstructionIndex, expr_index: MediumLevelExpressionIndex, ) -> Self { // TODO: If op.sourceOperation == BN_INVALID_OPERAND && op.operation == MLIL_NOP return None let op = unsafe { BNGetMediumLevelILByIndex(function.handle, expr_index.0) }; use BNMediumLevelILOperation::*; use MediumLevelILInstructionKind as Op; let kind = match op.operation { MLIL_NOP => Op::Nop, MLIL_NORET => Op::Noret, MLIL_BP => Op::Bp, MLIL_UNDEF => Op::Undef, MLIL_ASSERT | MLIL_ASSERT_SSA | MLIL_FORCE_VER | MLIL_FORCE_VER_SSA => Op::Undef, MLIL_UNIMPL => Op::Unimpl, MLIL_IF => Op::If(MediumLevelILOperationIf { condition: MediumLevelExpressionIndex::from(op.operands[0]), dest_true: MediumLevelInstructionIndex(op.operands[1] as usize), dest_false: MediumLevelInstructionIndex(op.operands[2] as usize), }), MLIL_FLOAT_CONST => Op::FloatConst(FloatConst { constant: get_float(op.operands[0], op.size), }), MLIL_CONST => Op::Const(Constant { constant: op.operands[0], }), MLIL_CONST_PTR => Op::ConstPtr(Constant { constant: op.operands[0], }), MLIL_IMPORT => Op::Import(Constant { constant: op.operands[0], }), MLIL_EXTERN_PTR => Op::ExternPtr(ExternPtr { constant: op.operands[0], offset: op.operands[1], }), MLIL_CONST_DATA => Op::ConstData(ConstData { constant_data_kind: op.operands[0] as u32, constant_data_value: op.operands[1] as i64, size: op.size, }), MLIL_JUMP => Op::Jump(Jump { dest: MediumLevelExpressionIndex::from(op.operands[0]), }), MLIL_RET_HINT => Op::RetHint(Jump { dest: MediumLevelExpressionIndex::from(op.operands[0]), }), MLIL_STORE_SSA => Op::StoreSsa(StoreSsa { dest: MediumLevelExpressionIndex::from(op.operands[0]), dest_memory: op.operands[1], src_memory: op.operands[2], src: MediumLevelExpressionIndex::from(op.operands[3]), }), MLIL_STORE_STRUCT_SSA => Op::StoreStructSsa(StoreStructSsa { dest: MediumLevelExpressionIndex::from(op.operands[0]), offset: op.operands[1], dest_memory: op.operands[2], src_memory: op.operands[3], src: MediumLevelExpressionIndex::from(op.operands[4]), }), MLIL_STORE_STRUCT => Op::StoreStruct(StoreStruct { dest: MediumLevelExpressionIndex::from(op.operands[0]), offset: op.operands[1], src: MediumLevelExpressionIndex::from(op.operands[2]), }), MLIL_STORE => Op::Store(Store { dest: MediumLevelExpressionIndex::from(op.operands[0]), src: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_JUMP_TO => Op::JumpTo(JumpTo { dest: MediumLevelExpressionIndex::from(op.operands[0]), num_operands: op.operands[1] as usize, first_operand: op.operands[2] as usize, }), MLIL_GOTO => Op::Goto(Goto { dest: MediumLevelInstructionIndex(op.operands[0] as usize), }), MLIL_FREE_VAR_SLOT => Op::FreeVarSlot(FreeVarSlot { dest: get_var(op.operands[0]), }), MLIL_SET_VAR_FIELD => Op::SetVarField(SetVarField { dest: get_var(op.operands[0]), offset: op.operands[1], src: MediumLevelExpressionIndex::from(op.operands[2]), }), MLIL_SET_VAR => Op::SetVar(SetVar { dest: get_var(op.operands[0]), src: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_FREE_VAR_SLOT_SSA => Op::FreeVarSlotSsa(FreeVarSlotSsa { dest: get_var_ssa(op.operands[0], op.operands[1] as usize), prev: get_var_ssa(op.operands[0], op.operands[2] as usize), }), MLIL_SET_VAR_SSA_FIELD => Op::SetVarSsaField(SetVarSsaField { dest: get_var_ssa(op.operands[0], op.operands[1] as usize), prev: get_var_ssa(op.operands[0], op.operands[2] as usize), offset: op.operands[3], src: MediumLevelExpressionIndex::from(op.operands[4]), }), MLIL_SET_VAR_ALIASED_FIELD => Op::SetVarAliasedField(SetVarSsaField { dest: get_var_ssa(op.operands[0], op.operands[1] as usize), prev: get_var_ssa(op.operands[0], op.operands[2] as usize), offset: op.operands[3], src: MediumLevelExpressionIndex::from(op.operands[4]), }), MLIL_SET_VAR_ALIASED => Op::SetVarAliased(SetVarAliased { dest: get_var_ssa(op.operands[0], op.operands[1] as usize), prev: get_var_ssa(op.operands[0], op.operands[2] as usize), src: MediumLevelExpressionIndex::from(op.operands[3]), }), MLIL_SET_VAR_SSA => Op::SetVarSsa(SetVarSsa { dest: get_var_ssa(op.operands[0], op.operands[1] as usize), src: MediumLevelExpressionIndex::from(op.operands[2]), }), MLIL_VAR_PHI => Op::VarPhi(VarPhi { dest: get_var_ssa(op.operands[0], op.operands[1] as usize), num_operands: op.operands[2] as usize, first_operand: op.operands[3] as usize, }), MLIL_MEM_PHI => Op::MemPhi(MemPhi { dest_memory: op.operands[0], num_operands: op.operands[1] as usize, first_operand: op.operands[2] as usize, }), MLIL_VAR_SPLIT => Op::VarSplit(VarSplit { high: get_var(op.operands[0]), low: get_var(op.operands[1]), }), MLIL_SET_VAR_SPLIT => Op::SetVarSplit(SetVarSplit { high: get_var(op.operands[0]), low: get_var(op.operands[1]), src: MediumLevelExpressionIndex::from(op.operands[2]), }), MLIL_VAR_SPLIT_SSA => Op::VarSplitSsa(VarSplitSsa { high: get_var_ssa(op.operands[0], op.operands[1] as usize), low: get_var_ssa(op.operands[2], op.operands[3] as usize), }), MLIL_SET_VAR_SPLIT_SSA => Op::SetVarSplitSsa(SetVarSplitSsa { high: get_var_ssa(op.operands[0], op.operands[1] as usize), low: get_var_ssa(op.operands[2], op.operands[3] as usize), src: MediumLevelExpressionIndex::from(op.operands[4]), }), MLIL_ADD => Op::Add(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_SUB => Op::Sub(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_AND => Op::And(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_OR => Op::Or(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_XOR => Op::Xor(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_LSL => Op::Lsl(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_LSR => Op::Lsr(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_ASR => Op::Asr(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_ROL => Op::Rol(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_ROR => Op::Ror(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_MUL => Op::Mul(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_MULU_DP => Op::MuluDp(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_MULS_DP => Op::MulsDp(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_DIVU => Op::Divu(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_DIVU_DP => Op::DivuDp(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_DIVS => Op::Divs(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_DIVS_DP => Op::DivsDp(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_MODU => Op::Modu(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_MODU_DP => Op::ModuDp(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_MODS => Op::Mods(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_MODS_DP => Op::ModsDp(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_CMP_E => Op::CmpE(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_CMP_NE => Op::CmpNe(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_CMP_SLT => Op::CmpSlt(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_CMP_ULT => Op::CmpUlt(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_CMP_SLE => Op::CmpSle(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_CMP_ULE => Op::CmpUle(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_CMP_SGE => Op::CmpSge(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_CMP_UGE => Op::CmpUge(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_CMP_SGT => Op::CmpSgt(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_CMP_UGT => Op::CmpUgt(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_TEST_BIT => Op::TestBit(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_ADD_OVERFLOW => Op::AddOverflow(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_FCMP_E => Op::FcmpE(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_FCMP_NE => Op::FcmpNe(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_FCMP_LT => Op::FcmpLt(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_FCMP_LE => Op::FcmpLe(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_FCMP_GE => Op::FcmpGe(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_FCMP_GT => Op::FcmpGt(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_FCMP_O => Op::FcmpO(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_FCMP_UO => Op::FcmpUo(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_FADD => Op::Fadd(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_FSUB => Op::Fsub(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_FMUL => Op::Fmul(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_FDIV => Op::Fdiv(BinaryOp { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), }), MLIL_ADC => Op::Adc(BinaryOpCarry { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), carry: MediumLevelExpressionIndex::from(op.operands[2]), }), MLIL_SBB => Op::Sbb(BinaryOpCarry { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), carry: MediumLevelExpressionIndex::from(op.operands[2]), }), MLIL_RLC => Op::Rlc(BinaryOpCarry { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), carry: MediumLevelExpressionIndex::from(op.operands[2]), }), MLIL_RRC => Op::Rrc(BinaryOpCarry { left: MediumLevelExpressionIndex::from(op.operands[0]), right: MediumLevelExpressionIndex::from(op.operands[1]), carry: MediumLevelExpressionIndex::from(op.operands[2]), }), MLIL_CALL => Op::Call(Call { num_outputs: op.operands[0] as usize, first_output: op.operands[1] as usize, dest: MediumLevelExpressionIndex::from(op.operands[2]), num_params: op.operands[3] as usize, first_param: op.operands[4] as usize, }), MLIL_CALL_PARAM => Op::CallParam(CallParam { first_param: op.operands[0] as usize, num_params: op.operands[1] as usize, }), MLIL_CALL_OUTPUT_SSA => Op::CallOutputSsa(CallOutputSsa { dest_memory: op.operands[0], num_outputs: op.operands[1] as usize, first_output: op.operands[2] as usize, }), MLIL_CALL_PARAM_SSA => Op::CallParamSsa(CallParamSsa { src_memory: op.operands[0], num_params: op.operands[1] as usize, first_param: op.operands[2] as usize, }), MLIL_TAILCALL => Op::Tailcall(Call { num_outputs: op.operands[0] as usize, first_output: op.operands[1] as usize, dest: MediumLevelExpressionIndex::from(op.operands[2]), num_params: op.operands[3] as usize, first_param: op.operands[4] as usize, }), MLIL_SYSCALL => Op::Syscall(Syscall { num_outputs: op.operands[0] as usize, first_output: op.operands[1] as usize, num_params: op.operands[2] as usize, first_param: op.operands[3] as usize, }), MLIL_INTRINSIC => Op::Intrinsic(Intrinsic { num_outputs: op.operands[0] as usize, first_output: op.operands[1] as usize, intrinsic: op.operands[2] as u32, num_params: op.operands[3] as usize, first_param: op.operands[4] as usize, }), MLIL_INTRINSIC_SSA => Op::IntrinsicSsa(IntrinsicSsa { num_outputs: op.operands[0] as usize, first_output: op.operands[1] as usize, intrinsic: op.operands[2] as u32, num_params: op.operands[3] as usize, first_param: op.operands[4] as usize, }), MLIL_MEMORY_INTRINSIC_SSA => Op::MemoryIntrinsicSsa(MemoryIntrinsicSsa { output: MediumLevelExpressionIndex::from(op.operands[0]), intrinsic: op.operands[1] as u32, num_params: op.operands[2] as usize, first_param: op.operands[3] as usize, src_memory: op.operands[4], }), MLIL_MEMORY_INTRINSIC_OUTPUT_SSA => { Op::MemoryIntrinsicOutputSsa(MemoryIntrinsicOutputSsa { dest_memory: op.operands[0], first_output: op.operands[1] as usize, num_outputs: op.operands[2] as usize, }) } MLIL_CALL_SSA => Op::CallSsa(CallSsa { output: MediumLevelExpressionIndex::from(op.operands[0]), dest: MediumLevelExpressionIndex::from(op.operands[1]), num_params: op.operands[2] as usize, first_param: op.operands[3] as usize, src_memory: op.operands[4], }), MLIL_TAILCALL_SSA => Op::TailcallSsa(CallSsa { output: MediumLevelExpressionIndex::from(op.operands[0]), dest: MediumLevelExpressionIndex::from(op.operands[1]), num_params: op.operands[2] as usize, first_param: op.operands[3] as usize, src_memory: op.operands[4], }), MLIL_CALL_UNTYPED_SSA => Op::CallUntypedSsa(CallUntypedSsa { output: MediumLevelExpressionIndex::from(op.operands[0]), dest: MediumLevelExpressionIndex::from(op.operands[1]), params: MediumLevelExpressionIndex::from(op.operands[2]), stack: MediumLevelExpressionIndex::from(op.operands[3]), }), MLIL_TAILCALL_UNTYPED_SSA => Op::TailcallUntypedSsa(CallUntypedSsa { output: MediumLevelExpressionIndex::from(op.operands[0]), dest: MediumLevelExpressionIndex::from(op.operands[1]), params: MediumLevelExpressionIndex::from(op.operands[2]), stack: MediumLevelExpressionIndex::from(op.operands[3]), }), MLIL_SYSCALL_SSA => Op::SyscallSsa(SyscallSsa { output: MediumLevelExpressionIndex::from(op.operands[0]), num_params: op.operands[1] as usize, first_param: op.operands[2] as usize, src_memory: op.operands[3], }), MLIL_SYSCALL_UNTYPED_SSA => Op::SyscallUntypedSsa(SyscallUntypedSsa { output: MediumLevelExpressionIndex::from(op.operands[0]), params: MediumLevelExpressionIndex::from(op.operands[1]), stack: MediumLevelExpressionIndex::from(op.operands[2]), }), MLIL_CALL_UNTYPED => Op::CallUntyped(CallUntyped { num_outputs: op.operands[0] as usize, first_output: op.operands[1] as usize, dest: MediumLevelExpressionIndex::from(op.operands[2]), params: MediumLevelExpressionIndex::from(op.operands[3]), stack: MediumLevelExpressionIndex::from(op.operands[4]), }), MLIL_TAILCALL_UNTYPED => Op::TailcallUntyped(CallUntyped { num_outputs: op.operands[0] as usize, first_output: op.operands[1] as usize, dest: MediumLevelExpressionIndex::from(op.operands[2]), params: MediumLevelExpressionIndex::from(op.operands[3]), stack: MediumLevelExpressionIndex::from(op.operands[4]), }), MLIL_SYSCALL_UNTYPED => Op::SyscallUntyped(SyscallUntyped { num_outputs: op.operands[0] as usize, first_output: op.operands[1] as usize, params: MediumLevelExpressionIndex::from(op.operands[2]), stack: MediumLevelExpressionIndex::from(op.operands[3]), }), MLIL_NEG => Op::Neg(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_NOT => Op::Not(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_BSWAP => Op::Bswap(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_POPCNT => Op::Popcnt(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_CLZ => Op::Clz(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_CTZ => Op::Ctz(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_RBIT => Op::Rbit(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_CLS => Op::Cls(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_SX => Op::Sx(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_ZX => Op::Zx(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_LOW_PART => Op::LowPart(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_BOOL_TO_INT => Op::BoolToInt(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_UNIMPL_MEM => Op::UnimplMem(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_FSQRT => Op::Fsqrt(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_FNEG => Op::Fneg(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_FABS => Op::Fabs(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_FLOAT_TO_INT => Op::FloatToInt(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_INT_TO_FLOAT => Op::IntToFloat(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_FLOAT_CONV => Op::FloatConv(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_ROUND_TO_INT => Op::RoundToInt(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_FLOOR => Op::Floor(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_CEIL => Op::Ceil(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_FTRUNC => Op::Ftrunc(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_LOAD => Op::Load(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_LOAD_STRUCT => Op::LoadStruct(LoadStruct { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), offset: op.operands[1], }), MLIL_LOAD_STRUCT_SSA => Op::LoadStructSsa(LoadStructSsa { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), offset: op.operands[1], src_memory: op.operands[2], }), MLIL_LOAD_SSA => Op::LoadSsa(LoadSsa { src: MediumLevelExpressionIndex::from(op.operands[0]), src_memory: op.operands[1], }), MLIL_RET => Op::Ret(Ret { num_operands: op.operands[0] as usize, first_operand: op.operands[1] as usize, }), MLIL_SEPARATE_PARAM_LIST => Op::SeparateParamList(SeparateParamList { num_params: op.operands[0] as usize, first_param: op.operands[1] as usize, }), MLIL_SHARED_PARAM_SLOT => Op::SharedParamSlot(SharedParamSlot { num_params: op.operands[0] as usize, first_param: op.operands[1] as usize, }), MLIL_VAR => Op::Var(Var { src: get_var(op.operands[0]), }), MLIL_VAR_OUTPUT => Op::VarOutput(VarOutput { dest: get_var(op.operands[0]), }), MLIL_VAR_OUTPUT_FIELD => Op::VarOutputField(VarOutputField { dest: get_var(op.operands[0]), offset: op.operands[1], }), MLIL_STORE_OUTPUT => Op::StoreOutput(StoreOutput { dest: MediumLevelExpressionIndex::from(op.operands[0]), }), MLIL_ADDRESS_OF => Op::AddressOf(Var { src: get_var(op.operands[0]), }), MLIL_PASS_BY_REF => Op::PassByRef(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_RETURN_BY_REF => Op::ReturnByRef(UnaryOp { src: MediumLevelExpressionIndex::from(op.operands[0] as usize), }), MLIL_VAR_FIELD => Op::VarField(Field { src: get_var(op.operands[0]), offset: op.operands[1], }), MLIL_ADDRESS_OF_FIELD => Op::AddressOfField(Field { src: get_var(op.operands[0]), offset: op.operands[1], }), MLIL_VAR_SSA => Op::VarSsa(VarSsa { src: get_var_ssa(op.operands[0], op.operands[1] as usize), }), MLIL_VAR_ALIASED => Op::VarAliased(VarSsa { src: get_var_ssa(op.operands[0], op.operands[1] as usize), }), MLIL_VAR_SSA_FIELD => Op::VarSsaField(VarSsaField { src: get_var_ssa(op.operands[0], op.operands[1] as usize), offset: op.operands[2], }), MLIL_VAR_ALIASED_FIELD => Op::VarAliasedField(VarSsaField { src: get_var_ssa(op.operands[0], op.operands[1] as usize), offset: op.operands[2], }), MLIL_VAR_OUTPUT_SSA => Op::VarOutputSsa(VarOutputSsa { dest: get_var_ssa(op.operands[0], op.operands[1] as usize), }), MLIL_VAR_OUTPUT_SSA_FIELD => Op::VarOutputSsaField(VarOutputSsaField { dest: get_var_ssa(op.operands[0], op.operands[1] as usize), prev: get_var_ssa(op.operands[0], op.operands[2] as usize), offset: op.operands[3], }), MLIL_VAR_OUTPUT_ALIASED => Op::VarOutputAliased(VarOutputAliased { dest: get_var_ssa(op.operands[0], op.operands[1] as usize), prev: get_var_ssa(op.operands[0], op.operands[2] as usize), }), MLIL_VAR_OUTPUT_ALIASED_FIELD => Op::VarOutputAliasedField(VarOutputAliasedField { dest: get_var_ssa(op.operands[0], op.operands[1] as usize), prev: get_var_ssa(op.operands[0], op.operands[2] as usize), offset: op.operands[3], }), MLIL_TRAP => Op::Trap(Trap { vector: op.operands[0], }), MLIL_BLOCK_TO_EXPAND => Op::BlockToExpand(BlockToExpand { num_operands: op.operands[0] as usize, first_operand: op.operands[1] as usize, }), }; Self { function, address: op.address, instr_index, expr_index, size: op.size, kind, } } fn get_operand_list(&self, operand_idx: usize) -> Vec { let mut count = 0; let raw_list_ptr = unsafe { BNMediumLevelILGetOperandList( self.function.handle, self.expr_index.0, operand_idx, &mut count, ) }; assert!(!raw_list_ptr.is_null()); let list = unsafe { std::slice::from_raw_parts(raw_list_ptr, count).to_vec() }; unsafe { BNMediumLevelILFreeOperandList(raw_list_ptr) }; list } fn get_var_list(&self, operand_idx: usize) -> Vec { self.get_operand_list(operand_idx) .into_iter() .map(Variable::from_identifier) .collect() } fn get_ssa_var_list(&self, operand_idx: usize) -> Vec { self.get_operand_list(operand_idx) .chunks(2) .map(|chunk| (Variable::from_identifier(chunk[0]), chunk[1] as usize)) .map(|(var, version)| SSAVariable::new(var, version)) .collect() } fn get_expr_list(&self, operand_idx: usize) -> Vec { self.get_operand_list(operand_idx) .into_iter() .map(|val| MediumLevelExpressionIndex(val as usize)) .filter_map(|idx| self.function.instruction_from_expr_index(idx)) .collect() } fn get_target_map(&self, operand_idx: usize) -> BTreeMap { self.get_operand_list(operand_idx) .chunks(2) // TODO: This filter is kinda redundant. .filter_map(|chunk| chunk.get(0..2)) .map(|chunk| (chunk[0], MediumLevelInstructionIndex(chunk[1] as usize))) .collect() } pub fn lift(&self) -> MediumLevelILLiftedInstruction { use MediumLevelILInstructionKind::*; use MediumLevelILLiftedInstructionKind as Lifted; let kind = match self.kind { Nop => Lifted::Nop, Noret => Lifted::Noret, Bp => Lifted::Bp, Undef => Lifted::Undef, Unimpl => Lifted::Unimpl, NotYetImplemented => Lifted::NotYetImplemented, If(op) => Lifted::If(LiftedIf { condition: self.lift_operand(op.condition), dest_true: op.dest_true, dest_false: op.dest_false, }), FloatConst(op) => Lifted::FloatConst(op), Const(op) => Lifted::Const(op), ConstPtr(op) => Lifted::ConstPtr(op), Import(op) => Lifted::Import(op), ExternPtr(op) => Lifted::ExternPtr(op), ConstData(op) => Lifted::ConstData(LiftedConstData { constant_data: ConstantData::new( self.function.function(), RegisterValue { // TODO: Replace with a From for RegisterValueType. // TODO: We might also want to change the type of `op.constant_data_kind` // TODO: To RegisterValueType and do the conversion when creating instruction. state: unsafe { std::mem::transmute::(op.constant_data_kind) }, value: op.constant_data_value, offset: 0, size: op.size, }, ), }), Jump(op) => Lifted::Jump(LiftedJump { dest: self.lift_operand(op.dest), }), RetHint(op) => Lifted::RetHint(LiftedJump { dest: self.lift_operand(op.dest), }), StoreSsa(op) => Lifted::StoreSsa(LiftedStoreSsa { dest: self.lift_operand(op.dest), dest_memory: op.dest_memory, src_memory: op.src_memory, src: self.lift_operand(op.src), }), StoreStructSsa(op) => Lifted::StoreStructSsa(LiftedStoreStructSsa { dest: self.lift_operand(op.dest), offset: op.offset, dest_memory: op.dest_memory, src_memory: op.src_memory, src: self.lift_operand(op.src), }), StoreStruct(op) => Lifted::StoreStruct(LiftedStoreStruct { dest: self.lift_operand(op.dest), offset: op.offset, src: self.lift_operand(op.src), }), Store(op) => Lifted::Store(LiftedStore { dest: self.lift_operand(op.dest), src: self.lift_operand(op.src), }), JumpTo(op) => Lifted::JumpTo(LiftedJumpTo { dest: self.lift_operand(op.dest), targets: self.get_target_map(1), }), Goto(op) => Lifted::Goto(op), FreeVarSlot(op) => Lifted::FreeVarSlot(op), SetVarField(op) => Lifted::SetVarField(LiftedSetVarField { dest: op.dest, offset: op.offset, src: self.lift_operand(op.src), }), SetVar(op) => Lifted::SetVar(LiftedSetVar { dest: op.dest, src: self.lift_operand(op.src), }), FreeVarSlotSsa(op) => Lifted::FreeVarSlotSsa(op), SetVarSsaField(op) => Lifted::SetVarSsaField(LiftedSetVarSsaField { dest: op.dest, prev: op.prev, offset: op.offset, src: self.lift_operand(op.src), }), SetVarAliasedField(op) => Lifted::SetVarAliasedField(LiftedSetVarSsaField { dest: op.dest, prev: op.prev, offset: op.offset, src: self.lift_operand(op.src), }), SetVarAliased(op) => Lifted::SetVarAliased(LiftedSetVarAliased { dest: op.dest, prev: op.prev, src: self.lift_operand(op.src), }), SetVarSsa(op) => Lifted::SetVarSsa(LiftedSetVarSsa { dest: op.dest, src: self.lift_operand(op.src), }), VarPhi(op) => Lifted::VarPhi(LiftedVarPhi { dest: op.dest, src: self.get_ssa_var_list(2), }), MemPhi(op) => Lifted::MemPhi(LiftedMemPhi { dest_memory: op.dest_memory, // TODO: Make a stronger type for this. src_memory: self.get_operand_list(1), }), VarSplit(op) => Lifted::VarSplit(op), SetVarSplit(op) => Lifted::SetVarSplit(LiftedSetVarSplit { high: op.high, low: op.low, src: self.lift_operand(op.src), }), VarSplitSsa(op) => Lifted::VarSplitSsa(op), SetVarSplitSsa(op) => Lifted::SetVarSplitSsa(LiftedSetVarSplitSsa { high: op.high, low: op.low, src: self.lift_operand(op.src), }), Add(op) => Lifted::Add(self.lift_binary_op(op)), Sub(op) => Lifted::Sub(self.lift_binary_op(op)), And(op) => Lifted::And(self.lift_binary_op(op)), Or(op) => Lifted::Or(self.lift_binary_op(op)), Xor(op) => Lifted::Xor(self.lift_binary_op(op)), Lsl(op) => Lifted::Lsl(self.lift_binary_op(op)), Lsr(op) => Lifted::Lsr(self.lift_binary_op(op)), Asr(op) => Lifted::Asr(self.lift_binary_op(op)), Rol(op) => Lifted::Rol(self.lift_binary_op(op)), Ror(op) => Lifted::Ror(self.lift_binary_op(op)), Mul(op) => Lifted::Mul(self.lift_binary_op(op)), MuluDp(op) => Lifted::MuluDp(self.lift_binary_op(op)), MulsDp(op) => Lifted::MulsDp(self.lift_binary_op(op)), Divu(op) => Lifted::Divu(self.lift_binary_op(op)), DivuDp(op) => Lifted::DivuDp(self.lift_binary_op(op)), Divs(op) => Lifted::Divs(self.lift_binary_op(op)), DivsDp(op) => Lifted::DivsDp(self.lift_binary_op(op)), Modu(op) => Lifted::Modu(self.lift_binary_op(op)), ModuDp(op) => Lifted::ModuDp(self.lift_binary_op(op)), Mods(op) => Lifted::Mods(self.lift_binary_op(op)), ModsDp(op) => Lifted::ModsDp(self.lift_binary_op(op)), CmpE(op) => Lifted::CmpE(self.lift_binary_op(op)), CmpNe(op) => Lifted::CmpNe(self.lift_binary_op(op)), CmpSlt(op) => Lifted::CmpSlt(self.lift_binary_op(op)), CmpUlt(op) => Lifted::CmpUlt(self.lift_binary_op(op)), CmpSle(op) => Lifted::CmpSle(self.lift_binary_op(op)), CmpUle(op) => Lifted::CmpUle(self.lift_binary_op(op)), CmpSge(op) => Lifted::CmpSge(self.lift_binary_op(op)), CmpUge(op) => Lifted::CmpUge(self.lift_binary_op(op)), CmpSgt(op) => Lifted::CmpSgt(self.lift_binary_op(op)), CmpUgt(op) => Lifted::CmpUgt(self.lift_binary_op(op)), TestBit(op) => Lifted::TestBit(self.lift_binary_op(op)), AddOverflow(op) => Lifted::AddOverflow(self.lift_binary_op(op)), FcmpE(op) => Lifted::FcmpE(self.lift_binary_op(op)), FcmpNe(op) => Lifted::FcmpNe(self.lift_binary_op(op)), FcmpLt(op) => Lifted::FcmpLt(self.lift_binary_op(op)), FcmpLe(op) => Lifted::FcmpLe(self.lift_binary_op(op)), FcmpGe(op) => Lifted::FcmpGe(self.lift_binary_op(op)), FcmpGt(op) => Lifted::FcmpGt(self.lift_binary_op(op)), FcmpO(op) => Lifted::FcmpO(self.lift_binary_op(op)), FcmpUo(op) => Lifted::FcmpUo(self.lift_binary_op(op)), Fadd(op) => Lifted::Fadd(self.lift_binary_op(op)), Fsub(op) => Lifted::Fsub(self.lift_binary_op(op)), Fmul(op) => Lifted::Fmul(self.lift_binary_op(op)), Fdiv(op) => Lifted::Fdiv(self.lift_binary_op(op)), Adc(op) => Lifted::Adc(self.lift_binary_op_carry(op)), Sbb(op) => Lifted::Sbb(self.lift_binary_op_carry(op)), Rlc(op) => Lifted::Rlc(self.lift_binary_op_carry(op)), Rrc(op) => Lifted::Rrc(self.lift_binary_op_carry(op)), Call(op) => Lifted::Call(self.lift_call(op)), CallOutput(_op) => Lifted::CallOutput(LiftedCallOutput { output: self.get_var_list(0), }), CallParam(_op) => Lifted::CallParam(LiftedCallParam { params: self.get_expr_list(0).iter().map(|i| i.lift()).collect(), }), CallOutputSsa(op) => Lifted::CallOutputSsa(LiftedCallOutputSsa { dest_memory: op.dest_memory, output: self.get_ssa_var_list(1), }), CallParamSsa(op) => Lifted::CallParamSsa(LiftedCallParamSsa { src_memory: op.src_memory, params: self.get_expr_list(1).iter().map(|i| i.lift()).collect(), }), Tailcall(op) => Lifted::Tailcall(self.lift_call(op)), Intrinsic(op) => Lifted::Intrinsic(LiftedIntrinsic { output: self.get_var_list(0), intrinsic: CoreIntrinsic::new( self.function.function().arch(), IntrinsicId(op.intrinsic), ) .expect("Valid intrinsic"), params: self .get_expr_list(3) .iter() .map(|expr| expr.lift()) .collect(), }), Syscall(_op) => Lifted::Syscall(LiftedSyscallCall { output: self .get_expr_list(0) .iter() .map(|expr| expr.lift()) .collect(), params: self .get_expr_list(2) .iter() .map(|expr| expr.lift()) .collect(), }), IntrinsicSsa(op) => Lifted::IntrinsicSsa(LiftedIntrinsicSsa { output: self.get_ssa_var_list(0), intrinsic: CoreIntrinsic::new( self.function.function().arch(), IntrinsicId(op.intrinsic), ) .expect("Valid intrinsic"), params: self .get_expr_list(3) .iter() .map(|expr| expr.lift()) .collect(), }), MemoryIntrinsicSsa(op) => Lifted::MemoryIntrinsicSsa(LiftedMemoryIntrinsicSsa { output: self.lift_operand(op.output), intrinsic: CoreIntrinsic::new( self.function.function().arch(), IntrinsicId(op.intrinsic), ) .expect("Valid intrinsic"), params: self .get_expr_list(2) .iter() .map(|expr| expr.lift()) .collect(), src_memory: op.src_memory, }), MemoryIntrinsicOutputSsa(op) => { Lifted::MemoryIntrinsicOutputSsa(LiftedMemoryIntrinsicOutputSsa { dest_memory: op.dest_memory, output: self.get_ssa_var_list(1), }) } CallSsa(op) => Lifted::CallSsa(self.lift_call_ssa(op)), TailcallSsa(op) => Lifted::TailcallSsa(self.lift_call_ssa(op)), CallUntypedSsa(op) => Lifted::CallUntypedSsa(self.lift_call_untyped_ssa(op)), TailcallUntypedSsa(op) => Lifted::TailcallUntypedSsa(self.lift_call_untyped_ssa(op)), SyscallSsa(op) => { let output_instr = self .function .instruction_from_expr_index(op.output) .expect("Valid output expression index"); Lifted::SyscallSsa(LiftedSyscallSsa { output: get_call_output_ssa(&output_instr) .iter() .map(|expr| expr.lift()) .collect(), params: self .get_expr_list(1) .iter() .map(|expr| expr.lift()) .collect(), src_memory: op.src_memory, }) } SyscallUntypedSsa(op) => { let output_instr = self .function .instruction_from_expr_index(op.output) .expect("Valid output expression index"); let params_instr = self .function .instruction_from_expr_index(op.params) .expect("Valid params expression index"); Lifted::SyscallUntypedSsa(LiftedSyscallUntypedSsa { output: get_call_output_ssa(&output_instr) .iter() .map(|expr| expr.lift()) .collect(), params: get_call_params_ssa(¶ms_instr) .iter() .map(|param| param.lift()) .collect(), stack: self.lift_operand(op.stack), }) } CallUntyped(op) => Lifted::CallUntyped(self.lift_call_untyped(op)), TailcallUntyped(op) => Lifted::TailcallUntyped(self.lift_call_untyped(op)), SyscallUntyped(op) => { let params_instr = self .function .instruction_from_expr_index(op.params) .expect("Valid params expression index"); Lifted::SyscallUntyped(LiftedSyscallUntyped { output: self .get_expr_list(0) .iter() .map(|expr| expr.lift()) .collect(), params: get_call_params(¶ms_instr) .iter() .map(|param| param.lift()) .collect(), stack: self.lift_operand(op.stack), }) } Neg(op) => Lifted::Neg(self.lift_unary_op(op)), Not(op) => Lifted::Not(self.lift_unary_op(op)), Bswap(op) => Lifted::Bswap(self.lift_unary_op(op)), Popcnt(op) => Lifted::Popcnt(self.lift_unary_op(op)), Clz(op) => Lifted::Clz(self.lift_unary_op(op)), Ctz(op) => Lifted::Ctz(self.lift_unary_op(op)), Rbit(op) => Lifted::Rbit(self.lift_unary_op(op)), Cls(op) => Lifted::Cls(self.lift_unary_op(op)), Sx(op) => Lifted::Sx(self.lift_unary_op(op)), Zx(op) => Lifted::Zx(self.lift_unary_op(op)), LowPart(op) => Lifted::LowPart(self.lift_unary_op(op)), BoolToInt(op) => Lifted::BoolToInt(self.lift_unary_op(op)), UnimplMem(op) => Lifted::UnimplMem(self.lift_unary_op(op)), Fsqrt(op) => Lifted::Fsqrt(self.lift_unary_op(op)), Fneg(op) => Lifted::Fneg(self.lift_unary_op(op)), Fabs(op) => Lifted::Fabs(self.lift_unary_op(op)), FloatToInt(op) => Lifted::FloatToInt(self.lift_unary_op(op)), IntToFloat(op) => Lifted::IntToFloat(self.lift_unary_op(op)), FloatConv(op) => Lifted::FloatConv(self.lift_unary_op(op)), RoundToInt(op) => Lifted::RoundToInt(self.lift_unary_op(op)), Floor(op) => Lifted::Floor(self.lift_unary_op(op)), Ceil(op) => Lifted::Ceil(self.lift_unary_op(op)), Ftrunc(op) => Lifted::Ftrunc(self.lift_unary_op(op)), Load(op) => Lifted::Load(self.lift_unary_op(op)), LoadStruct(op) => Lifted::LoadStruct(LiftedLoadStruct { src: self.lift_operand(op.src), offset: op.offset, }), LoadStructSsa(op) => Lifted::LoadStructSsa(LiftedLoadStructSsa { src: self.lift_operand(op.src), offset: op.offset, src_memory: op.src_memory, }), LoadSsa(op) => Lifted::LoadSsa(LiftedLoadSsa { src: self.lift_operand(op.src), src_memory: op.src_memory, }), Ret(_op) => Lifted::Ret(LiftedRet { src: self .get_expr_list(0) .iter() .map(|expr| expr.lift()) .collect(), }), SeparateParamList(_op) => Lifted::SeparateParamList(LiftedSeparateParamList { params: self .get_expr_list(0) .iter() .map(|expr| expr.lift()) .collect(), }), SharedParamSlot(_op) => Lifted::SharedParamSlot(LiftedSharedParamSlot { params: self .get_expr_list(0) .iter() .map(|expr| expr.lift()) .collect(), }), Var(op) => Lifted::Var(op), VarOutput(op) => Lifted::VarOutput(op), VarOutputField(op) => Lifted::VarOutputField(op), StoreOutput(op) => Lifted::StoreOutput(LiftedStoreOutput { dest: self.lift_operand(op.dest), }), AddressOf(op) => Lifted::AddressOf(op), PassByRef(op) => Lifted::PassByRef(self.lift_unary_op(op)), ReturnByRef(op) => Lifted::ReturnByRef(self.lift_unary_op(op)), VarField(op) => Lifted::VarField(op), AddressOfField(op) => Lifted::AddressOfField(op), VarSsa(op) => Lifted::VarSsa(op), VarAliased(op) => Lifted::VarAliased(op), VarSsaField(op) => Lifted::VarSsaField(op), VarAliasedField(op) => Lifted::VarAliasedField(op), VarOutputSsa(op) => Lifted::VarOutputSsa(op), VarOutputSsaField(op) => Lifted::VarOutputSsaField(op), VarOutputAliased(op) => Lifted::VarOutputAliased(op), VarOutputAliasedField(op) => Lifted::VarOutputAliasedField(op), Trap(op) => Lifted::Trap(op), BlockToExpand(_op) => Lifted::BlockToExpand(LiftedBlockToExpand { exprs: self .get_expr_list(0) .iter() .map(|expr| expr.lift()) .collect(), }), }; MediumLevelILLiftedInstruction { function: self.function.clone(), address: self.address, instr_index: self.instr_index, expr_index: self.expr_index, size: self.size, kind, } } pub fn tokens(&self) -> Array { let mut count = 0; let mut tokens = core::ptr::null_mut(); assert!(unsafe { BNGetMediumLevelILExprText( self.function.handle, self.function.function().arch().handle, self.expr_index.0, &mut tokens, &mut count, core::ptr::null_mut(), ) }); unsafe { Array::new(tokens, count, ()) } } /// Value of expression if constant or a known value pub fn value(&self) -> RegisterValue { unsafe { BNGetMediumLevelILExprValue(self.function.handle, self.expr_index.0) }.into() } /// Returns the [`BasicBlock`] containing the given [`MediumLevelILInstruction`]. pub fn basic_block(&self) -> Option>> { // TODO: We might be able to .expect this if we guarantee that self.index is valid. self.function.basic_block_containing_index(self.instr_index) } /// Possible values of expression using path-sensitive static data flow analysis pub fn possible_values(&self) -> PossibleValueSet { self.possible_values_with_opts(&[]) } /// Possible values of expression using path-sensitive static data flow analysis pub fn possible_values_with_opts(&self, options: &[DataFlowQueryOption]) -> PossibleValueSet { let value = unsafe { BNGetMediumLevelILPossibleExprValues( self.function.handle, self.instr_index.0, options.as_ptr() as *mut _, options.len(), ) }; PossibleValueSet::from_owned_core_raw(value) } pub fn possible_ssa_variable_values(&self, ssa_var: &SSAVariable) -> PossibleValueSet { self.possible_ssa_variable_values_with_opts(ssa_var, &[]) } pub fn possible_ssa_variable_values_with_opts( &self, ssa_var: &SSAVariable, options: &[DataFlowQueryOption], ) -> PossibleValueSet { let raw_var = BNVariable::from(ssa_var.variable); let value = unsafe { BNGetMediumLevelILPossibleSSAVarValues( self.function.handle, &raw_var, ssa_var.version, self.instr_index.0, options.as_ptr() as *mut _, options.len(), ) }; PossibleValueSet::from_owned_core_raw(value) } /// Return the ssa version of a [`Variable`] at the given instruction. pub fn ssa_variable_version(&self, var: Variable) -> SSAVariable { let raw_var = BNVariable::from(var); let version = unsafe { BNGetMediumLevelILSSAVarVersionAtILInstruction( self.function.handle, &raw_var, self.instr_index.0, ) }; SSAVariable::new(var, version) } /// Return the ssa version of a [`Variable`] after the given instruction. pub fn ssa_variable_version_after(&self, var: Variable) -> SSAVariable { let raw_var = BNVariable::from(var); let version = unsafe { BNGetMediumLevelILSSAVarVersionAfterILInstruction( self.function.handle, &raw_var, self.instr_index.0, ) }; SSAVariable::new(var, version) } /// Set of branching instructions that must take the true or false path to reach this instruction pub fn branch_dependencies(&self) -> Array { let mut count = 0; let deps = unsafe { BNGetAllMediumLevelILBranchDependence( self.function.handle, self.instr_index.0, &mut count, ) }; assert!(!deps.is_null()); unsafe { Array::new(deps, count, self.function.clone()) } } pub fn branch_dependence_at( &self, branch_instruction: MediumLevelILInstruction, ) -> BranchDependence { let deps = unsafe { BNGetMediumLevelILBranchDependence( self.function.handle, self.instr_index.0, branch_instruction.instr_index.0, ) }; BranchDependence { instruction: branch_instruction, dependence: deps, } } /// Version of active memory contents in SSA form for this instruction pub fn ssa_memory_version(&self) -> usize { unsafe { BNGetMediumLevelILSSAMemoryVersionAtILInstruction( self.function.handle, self.instr_index.0, ) } } /// Version of active memory contents in SSA form for this instruction pub fn ssa_memory_version_after(&self) -> usize { unsafe { BNGetMediumLevelILSSAMemoryVersionAfterILInstruction( self.function.handle, self.instr_index.0, ) } } /// Type of expression pub fn expr_type(&self) -> Option>> { let result = unsafe { BNGetMediumLevelILExprType(self.function.handle, self.expr_index.0) }; (!result.type_.is_null()).then(|| Conf::>::from_owned_raw(result)) } /// Set type of expression /// /// This API is only meant for workflows or for debugging purposes, since the changes they make are not persistent /// and get lost after a database save and reload. To make persistent changes to the analysis, one should use other /// APIs to, for example, change the type of variables. The analysis will then propagate the type of the variable /// and update the type of related expressions. pub fn set_expr_type<'a, T: Into>>(&self, ty: T) { let mut ty: BNTypeWithConfidence = Conf::<&Type>::into_raw(ty.into()); unsafe { BNSetMediumLevelILExprType(self.function.handle, self.expr_index.0, &mut ty) } } pub fn variable_for_register(&self, reg_id: RegisterId) -> Variable { let result = unsafe { BNGetMediumLevelILVariableForRegisterAtInstruction( self.function.handle, reg_id.0, self.instr_index.0, ) }; Variable::from(result) } pub fn variable_for_register_after(&self, reg_id: RegisterId) -> Variable { let result = unsafe { BNGetMediumLevelILVariableForRegisterAfterInstruction( self.function.handle, reg_id.0, self.instr_index.0, ) }; Variable::from(result) } pub fn variable_for_flag(&self, flag_id: FlagId) -> Variable { let result = unsafe { BNGetMediumLevelILVariableForFlagAtInstruction( self.function.handle, flag_id.0, self.instr_index.0, ) }; Variable::from(result) } pub fn variable_for_flag_after(&self, flag_id: FlagId) -> Variable { let result = unsafe { BNGetMediumLevelILVariableForFlagAfterInstruction( self.function.handle, flag_id.0, self.instr_index.0, ) }; Variable::from(result) } pub fn variable_for_stack_location(&self, offset: i64) -> Variable { let result = unsafe { BNGetMediumLevelILVariableForStackLocationAtInstruction( self.function.handle, offset, self.instr_index.0, ) }; Variable::from(result) } pub fn variable_for_stack_location_after(&self, offset: i64) -> Variable { let result = unsafe { BNGetMediumLevelILVariableForStackLocationAfterInstruction( self.function.handle, offset, self.instr_index.0, ) }; Variable::from(result) } pub fn register_value(&self, reg_id: RegisterId) -> RegisterValue { unsafe { BNGetMediumLevelILRegisterValueAtInstruction( self.function.handle, reg_id.0, self.instr_index.0, ) } .into() } pub fn register_value_after(&self, reg_id: RegisterId) -> RegisterValue { unsafe { BNGetMediumLevelILRegisterValueAfterInstruction( self.function.handle, reg_id.0, self.instr_index.0, ) } .into() } pub fn possible_register_values(&self, reg_id: RegisterId) -> PossibleValueSet { self.possible_register_values_with_opts(reg_id, &[]) } pub fn possible_register_values_with_opts( &self, reg_id: RegisterId, options: &[DataFlowQueryOption], ) -> PossibleValueSet { let value = unsafe { BNGetMediumLevelILPossibleRegisterValuesAtInstruction( self.function.handle, reg_id.0, self.instr_index.0, options.as_ptr() as *mut _, options.len(), ) }; PossibleValueSet::from_owned_core_raw(value) } pub fn possible_register_values_after(&self, reg_id: RegisterId) -> PossibleValueSet { self.possible_register_values_after_with_opts(reg_id, &[]) } pub fn possible_register_values_after_with_opts( &self, reg_id: RegisterId, options: &[DataFlowQueryOption], ) -> PossibleValueSet { let value = unsafe { BNGetMediumLevelILPossibleRegisterValuesAfterInstruction( self.function.handle, reg_id.0, self.instr_index.0, options.as_ptr() as *mut _, options.len(), ) }; PossibleValueSet::from_owned_core_raw(value) } pub fn flag_value(&self, flag_id: FlagId) -> RegisterValue { unsafe { BNGetMediumLevelILFlagValueAtInstruction( self.function.handle, flag_id.0, self.instr_index.0, ) } .into() } pub fn flag_value_after(&self, flag_id: FlagId) -> RegisterValue { unsafe { BNGetMediumLevelILFlagValueAfterInstruction( self.function.handle, flag_id.0, self.instr_index.0, ) } .into() } pub fn possible_flag_values(&self, flag_id: FlagId) -> PossibleValueSet { self.possible_flag_values_with_opts(flag_id, &[]) } pub fn possible_flag_values_with_opts( &self, flag_id: FlagId, options: &[DataFlowQueryOption], ) -> PossibleValueSet { let value = unsafe { BNGetMediumLevelILPossibleFlagValuesAtInstruction( self.function.handle, flag_id.0, self.instr_index.0, options.as_ptr() as *mut _, options.len(), ) }; PossibleValueSet::from_owned_core_raw(value) } pub fn possible_flag_values_after_with_opts( &self, flag_id: FlagId, options: &[DataFlowQueryOption], ) -> PossibleValueSet { let value = unsafe { BNGetMediumLevelILPossibleFlagValuesAfterInstruction( self.function.handle, flag_id.0, self.instr_index.0, options.as_ptr() as *mut _, options.len(), ) }; PossibleValueSet::from_owned_core_raw(value) } pub fn stack_contents(&self, offset: i64, size: usize) -> RegisterValue { unsafe { BNGetMediumLevelILStackContentsAtInstruction( self.function.handle, offset, size, self.instr_index.0, ) } .into() } pub fn stack_contents_after(&self, offset: i64, size: usize) -> RegisterValue { unsafe { BNGetMediumLevelILStackContentsAfterInstruction( self.function.handle, offset, size, self.instr_index.0, ) } .into() } pub fn possible_stack_contents_with_opts( &self, offset: i64, size: usize, options: &[DataFlowQueryOption], ) -> PossibleValueSet { let value = unsafe { BNGetMediumLevelILPossibleStackContentsAtInstruction( self.function.handle, offset, size, self.instr_index.0, options.as_ptr() as *mut _, options.len(), ) }; PossibleValueSet::from_owned_core_raw(value) } pub fn possible_stack_contents_after_with_opts( &self, offset: i64, size: usize, options: &[DataFlowQueryOption], ) -> PossibleValueSet { let value = unsafe { BNGetMediumLevelILPossibleStackContentsAfterInstruction( self.function.handle, offset, size, self.instr_index.0, options.as_ptr() as *mut _, options.len(), ) }; PossibleValueSet::from_owned_core_raw(value) } /// Gets the unique variable for a definition instruction. This unique variable can be passed /// to [crate::function::Function::split_variable] to split a variable at a definition. The given `var` is the /// assigned variable to query. /// /// * `var` - variable to query pub fn split_var_for_definition(&self, var: &Variable) -> Variable { let raw_var = BNVariable::from(var); let index = unsafe { BNGetDefaultIndexForMediumLevelILVariableDefinition( self.function.handle, &raw_var, self.instr_index.0, ) }; Variable::new(var.ty, index, var.storage) } fn lift_operand( &self, expr_idx: MediumLevelExpressionIndex, ) -> Box { let operand_instr = self .function .instruction_from_expr_index(expr_idx) .expect("Invalid operand expression index"); Box::new(operand_instr.lift()) } fn lift_binary_op(&self, op: BinaryOp) -> LiftedBinaryOp { LiftedBinaryOp { left: self.lift_operand(op.left), right: self.lift_operand(op.right), } } fn lift_binary_op_carry(&self, op: BinaryOpCarry) -> LiftedBinaryOpCarry { LiftedBinaryOpCarry { left: self.lift_operand(op.left), right: self.lift_operand(op.right), carry: self.lift_operand(op.carry), } } fn lift_unary_op(&self, op: UnaryOp) -> LiftedUnaryOp { LiftedUnaryOp { src: self.lift_operand(op.src), } } fn lift_call(&self, op: Call) -> LiftedCall { LiftedCall { output: self .get_expr_list(0) .iter() .map(|expr| expr.lift()) .collect(), dest: self.lift_operand(op.dest), params: self .get_expr_list(3) .iter() .map(|expr| expr.lift()) .collect(), } } fn lift_call_untyped(&self, op: CallUntyped) -> LiftedCallUntyped { let params_instr = self .function .instruction_from_expr_index(op.params) .expect("Valid params expression index"); LiftedCallUntyped { output: self .get_expr_list(0) .iter() .map(|expr| expr.lift()) .collect(), dest: self.lift_operand(op.dest), params: get_call_params(¶ms_instr) .iter() .map(|expr| expr.lift()) .collect(), stack: self.lift_operand(op.stack), } } fn lift_call_ssa(&self, op: CallSsa) -> LiftedCallSsa { let output_instr = self .function .instruction_from_expr_index(op.output) .expect("Valid output expression index"); LiftedCallSsa { output: get_call_output_ssa(&output_instr) .iter() .map(|expr| expr.lift()) .collect(), dest: self.lift_operand(op.dest), params: self .get_expr_list(2) .iter() .map(|expr| expr.lift()) .collect(), src_memory: op.src_memory, } } fn lift_call_untyped_ssa(&self, op: CallUntypedSsa) -> LiftedCallUntypedSsa { let output_instr = self .function .instruction_from_expr_index(op.output) .expect("Valid output expression index"); let params_instr = self .function .instruction_from_expr_index(op.params) .expect("Valid params expression index"); LiftedCallUntypedSsa { output: get_call_output_ssa(&output_instr) .iter() .map(|expr| expr.lift()) .collect(), dest: self.lift_operand(op.dest), params: get_call_params_ssa(¶ms_instr) .iter() .map(|param| param.lift()) .collect(), stack: self.lift_operand(op.stack), } } } impl Debug for MediumLevelILInstruction { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.debug_struct("MediumLevelILInstruction") .field("address", &self.address) .field("instr_index", &self.instr_index) .field("expr_index", &self.expr_index) .field("size", &self.size) .field("kind", &self.kind) .finish() } } impl CoreArrayProvider for MediumLevelILInstruction { type Raw = usize; type Context = Ref; type Wrapped<'a> = Self; } unsafe impl CoreArrayProviderInner for MediumLevelILInstruction { unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { BNFreeILInstructionList(raw) } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { context .instruction_from_index(MediumLevelInstructionIndex(*raw)) .unwrap() } } #[derive(Debug, Copy, Clone)] pub enum MediumLevelILInstructionKind { Nop, Noret, Bp, Undef, Unimpl, If(MediumLevelILOperationIf), FloatConst(FloatConst), Const(Constant), ConstPtr(Constant), Import(Constant), ExternPtr(ExternPtr), ConstData(ConstData), Jump(Jump), RetHint(Jump), StoreSsa(StoreSsa), StoreStructSsa(StoreStructSsa), StoreStruct(StoreStruct), Store(Store), JumpTo(JumpTo), Goto(Goto), FreeVarSlot(FreeVarSlot), SetVarField(SetVarField), SetVar(SetVar), FreeVarSlotSsa(FreeVarSlotSsa), SetVarSsaField(SetVarSsaField), SetVarAliasedField(SetVarSsaField), SetVarAliased(SetVarAliased), SetVarSsa(SetVarSsa), VarPhi(VarPhi), MemPhi(MemPhi), VarSplit(VarSplit), SetVarSplit(SetVarSplit), VarSplitSsa(VarSplitSsa), SetVarSplitSsa(SetVarSplitSsa), Add(BinaryOp), Sub(BinaryOp), And(BinaryOp), Or(BinaryOp), Xor(BinaryOp), Lsl(BinaryOp), Lsr(BinaryOp), Asr(BinaryOp), Rol(BinaryOp), Ror(BinaryOp), Mul(BinaryOp), MuluDp(BinaryOp), MulsDp(BinaryOp), Divu(BinaryOp), DivuDp(BinaryOp), Divs(BinaryOp), DivsDp(BinaryOp), Modu(BinaryOp), ModuDp(BinaryOp), Mods(BinaryOp), ModsDp(BinaryOp), CmpE(BinaryOp), CmpNe(BinaryOp), CmpSlt(BinaryOp), CmpUlt(BinaryOp), CmpSle(BinaryOp), CmpUle(BinaryOp), CmpSge(BinaryOp), CmpUge(BinaryOp), CmpSgt(BinaryOp), CmpUgt(BinaryOp), TestBit(BinaryOp), AddOverflow(BinaryOp), FcmpE(BinaryOp), FcmpNe(BinaryOp), FcmpLt(BinaryOp), FcmpLe(BinaryOp), FcmpGe(BinaryOp), FcmpGt(BinaryOp), FcmpO(BinaryOp), FcmpUo(BinaryOp), Fadd(BinaryOp), Fsub(BinaryOp), Fmul(BinaryOp), Fdiv(BinaryOp), Adc(BinaryOpCarry), Sbb(BinaryOpCarry), Rlc(BinaryOpCarry), Rrc(BinaryOpCarry), Call(Call), CallOutput(CallOutput), CallParam(CallParam), CallOutputSsa(CallOutputSsa), CallParamSsa(CallParamSsa), Tailcall(Call), Syscall(Syscall), Intrinsic(Intrinsic), IntrinsicSsa(IntrinsicSsa), MemoryIntrinsicSsa(MemoryIntrinsicSsa), MemoryIntrinsicOutputSsa(MemoryIntrinsicOutputSsa), CallSsa(CallSsa), TailcallSsa(CallSsa), CallUntypedSsa(CallUntypedSsa), TailcallUntypedSsa(CallUntypedSsa), SyscallSsa(SyscallSsa), SyscallUntypedSsa(SyscallUntypedSsa), CallUntyped(CallUntyped), TailcallUntyped(CallUntyped), SyscallUntyped(SyscallUntyped), SeparateParamList(SeparateParamList), SharedParamSlot(SharedParamSlot), VarOutput(VarOutput), VarOutputField(VarOutputField), StoreOutput(StoreOutput), Neg(UnaryOp), Not(UnaryOp), Bswap(UnaryOp), Popcnt(UnaryOp), Clz(UnaryOp), Ctz(UnaryOp), Rbit(UnaryOp), Cls(UnaryOp), Sx(UnaryOp), Zx(UnaryOp), LowPart(UnaryOp), BoolToInt(UnaryOp), UnimplMem(UnaryOp), Fsqrt(UnaryOp), Fneg(UnaryOp), Fabs(UnaryOp), FloatToInt(UnaryOp), IntToFloat(UnaryOp), FloatConv(UnaryOp), RoundToInt(UnaryOp), Floor(UnaryOp), Ceil(UnaryOp), Ftrunc(UnaryOp), Load(UnaryOp), LoadStruct(LoadStruct), LoadStructSsa(LoadStructSsa), LoadSsa(LoadSsa), Ret(Ret), Var(Var), AddressOf(Var), PassByRef(UnaryOp), ReturnByRef(UnaryOp), VarField(Field), AddressOfField(Field), VarSsa(VarSsa), VarAliased(VarSsa), VarSsaField(VarSsaField), VarAliasedField(VarSsaField), VarOutputSsa(VarOutputSsa), VarOutputSsaField(VarOutputSsaField), VarOutputAliased(VarOutputAliased), VarOutputAliasedField(VarOutputAliasedField), Trap(Trap), BlockToExpand(BlockToExpand), // A placeholder for instructions that the Rust bindings do not yet support. // Distinct from `Unimpl` as that is a valid instruction. NotYetImplemented, } fn get_float(value: u64, size: usize) -> f64 { match size { 4 => f32::from_bits(value as u32) as f64, 8 => f64::from_bits(value), // TODO how to handle this value? size => todo!("float size {}", size), } } fn get_var(id: u64) -> Variable { Variable::from_identifier(id) } fn get_var_ssa(id: u64, version: usize) -> SSAVariable { SSAVariable::new(get_var(id), version) } fn get_call_params(instr: &MediumLevelILInstruction) -> Vec { match instr.kind { MediumLevelILInstructionKind::CallParam(_op) => instr.get_expr_list(0), _ => vec![], } } fn get_call_output_ssa(instr: &MediumLevelILInstruction) -> Vec { match instr.kind { MediumLevelILInstructionKind::CallOutputSsa(_op) => instr.get_expr_list(1), _ => vec![], } } fn get_call_params_ssa(instr: &MediumLevelILInstruction) -> Vec { match instr.kind { MediumLevelILInstructionKind::CallParamSsa(_op) => instr.get_expr_list(1), _ => vec![], } } /// Conditional branching instruction and an expected conditional result pub struct BranchDependence { pub instruction: MediumLevelILInstruction, pub dependence: ILBranchDependence, } impl CoreArrayProvider for BranchDependence { type Raw = BNILBranchInstructionAndDependence; type Context = Ref; type Wrapped<'a> = Self; } unsafe impl CoreArrayProviderInner for BranchDependence { unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { unsafe { BNFreeILBranchDependenceList(raw) }; } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { Self { instruction: MediumLevelILInstruction::from_expr_index( context.clone(), MediumLevelExpressionIndex(raw.branch), ), dependence: raw.dependence, } } }