use binaryninjacore_sys::*; use crate::architecture::CoreIntrinsic; use crate::disassembly::InstructionTextToken; use crate::operand_iter::OperandIter; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref}; use crate::types::{ Conf, ConstantData, DataFlowQueryOption, ILBranchDependence, PossibleValueSet, RegisterValue, RegisterValueType, SSAVariable, Type, Variable, }; use super::lift::*; use super::operation::*; use super::MediumLevelILFunction; #[derive(Clone)] pub struct MediumLevelILInstruction { pub function: Ref, pub address: u64, pub index: usize, pub size: usize, pub kind: MediumLevelILInstructionKind, } #[derive(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), Tailcall(Call), Syscall(Syscall), Intrinsic(Intrinsic), IntrinsicSsa(IntrinsicSsa), CallSsa(CallSsa), TailcallSsa(CallSsa), CallUntypedSsa(CallUntypedSsa), TailcallUntypedSsa(CallUntypedSsa), SyscallSsa(SyscallSsa), SyscallUntypedSsa(SyscallUntypedSsa), CallUntyped(CallUntyped), TailcallUntyped(CallUntyped), SyscallUntyped(SyscallUntyped), SeparateParamList(SeparateParamList), SharedParamSlot(SharedParamSlot), Neg(UnaryOp), Not(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), VarField(Field), AddressOfField(Field), VarSsa(VarSsa), VarAliased(VarSsa), VarSsaField(VarSsaField), VarAliasedField(VarSsaField), Trap(Trap), } impl core::fmt::Debug for MediumLevelILInstruction { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { write!( f, "<{} at 0x{:08}>", core::any::type_name::(), self.address, ) } } impl MediumLevelILInstruction { pub(crate) fn new(function: Ref, index: usize) -> Self { let op = unsafe { BNGetMediumLevelILByIndex(function.handle, index) }; 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_UNIMPL => Op::Unimpl, MLIL_IF => Op::If(MediumLevelILOperationIf { condition: op.operands[0] as usize, dest_true: op.operands[1], dest_false: op.operands[2], }), 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: op.operands[0] as usize, }), MLIL_RET_HINT => Op::RetHint(Jump { dest: op.operands[0] as usize, }), MLIL_STORE_SSA => Op::StoreSsa(StoreSsa { dest: op.operands[0] as usize, dest_memory: op.operands[1], src_memory: op.operands[2], src: op.operands[3] as usize, }), MLIL_STORE_STRUCT_SSA => Op::StoreStructSsa(StoreStructSsa { dest: op.operands[0] as usize, offset: op.operands[1], dest_memory: op.operands[2], src_memory: op.operands[3], src: op.operands[4] as usize, }), MLIL_STORE_STRUCT => Op::StoreStruct(StoreStruct { dest: op.operands[0] as usize, offset: op.operands[1], src: op.operands[2] as usize, }), MLIL_STORE => Op::Store(Store { dest: op.operands[0] as usize, src: op.operands[1] as usize, }), MLIL_JUMP_TO => Op::JumpTo(JumpTo { dest: op.operands[0] as usize, num_operands: op.operands[1] as usize, first_operand: op.operands[2] as usize, }), MLIL_GOTO => Op::Goto(Goto { dest: op.operands[0], }), 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: op.operands[2] as usize, }), MLIL_SET_VAR => Op::SetVar(SetVar { dest: get_var(op.operands[0]), src: op.operands[1] as usize, }), 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: op.operands[4] as usize, }), 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: op.operands[4] as usize, }), 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: op.operands[3] as usize, }), MLIL_SET_VAR_SSA => Op::SetVarSsa(SetVarSsa { dest: get_var_ssa(op.operands[0], op.operands[1] as usize), src: op.operands[2] as usize, }), 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: op.operands[2] as usize, }), 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: op.operands[4] as usize, }), MLIL_ADD => Op::Add(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_SUB => Op::Sub(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_AND => Op::And(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_OR => Op::Or(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_XOR => Op::Xor(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_LSL => Op::Lsl(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_LSR => Op::Lsr(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_ASR => Op::Asr(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_ROL => Op::Rol(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_ROR => Op::Ror(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_MUL => Op::Mul(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_MULU_DP => Op::MuluDp(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_MULS_DP => Op::MulsDp(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_DIVU => Op::Divu(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_DIVU_DP => Op::DivuDp(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_DIVS => Op::Divs(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_DIVS_DP => Op::DivsDp(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_MODU => Op::Modu(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_MODU_DP => Op::ModuDp(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_MODS => Op::Mods(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_MODS_DP => Op::ModsDp(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_CMP_E => Op::CmpE(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_CMP_NE => Op::CmpNe(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_CMP_SLT => Op::CmpSlt(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_CMP_ULT => Op::CmpUlt(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_CMP_SLE => Op::CmpSle(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_CMP_ULE => Op::CmpUle(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_CMP_SGE => Op::CmpSge(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_CMP_UGE => Op::CmpUge(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_CMP_SGT => Op::CmpSgt(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_CMP_UGT => Op::CmpUgt(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_TEST_BIT => Op::TestBit(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_ADD_OVERFLOW => Op::AddOverflow(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_FCMP_E => Op::FcmpE(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_FCMP_NE => Op::FcmpNe(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_FCMP_LT => Op::FcmpLt(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_FCMP_LE => Op::FcmpLe(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_FCMP_GE => Op::FcmpGe(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_FCMP_GT => Op::FcmpGt(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_FCMP_O => Op::FcmpO(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_FCMP_UO => Op::FcmpUo(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_FADD => Op::Fadd(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_FSUB => Op::Fsub(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_FMUL => Op::Fmul(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_FDIV => Op::Fdiv(BinaryOp { left: op.operands[0] as usize, right: op.operands[1] as usize, }), MLIL_ADC => Op::Adc(BinaryOpCarry { left: op.operands[0] as usize, right: op.operands[1] as usize, carry: op.operands[2] as usize, }), MLIL_SBB => Op::Sbb(BinaryOpCarry { left: op.operands[0] as usize, right: op.operands[1] as usize, carry: op.operands[2] as usize, }), MLIL_RLC => Op::Rlc(BinaryOpCarry { left: op.operands[0] as usize, right: op.operands[1] as usize, carry: op.operands[2] as usize, }), MLIL_RRC => Op::Rrc(BinaryOpCarry { left: op.operands[0] as usize, right: op.operands[1] as usize, carry: op.operands[2] as usize, }), MLIL_CALL => Op::Call(Call { num_outputs: op.operands[0] as usize, first_output: op.operands[1] as usize, dest: op.operands[2] as usize, num_params: op.operands[3] as usize, first_param: op.operands[4] as usize, }), MLIL_TAILCALL => Op::Tailcall(Call { num_outputs: op.operands[0] as usize, first_output: op.operands[1] as usize, dest: op.operands[2] as usize, 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_CALL_SSA => Op::CallSsa(CallSsa { output: op.operands[0] as usize, dest: op.operands[1] as usize, 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: op.operands[0] as usize, dest: op.operands[1] as usize, 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: op.operands[0] as usize, dest: op.operands[1] as usize, params: op.operands[2] as usize, stack: op.operands[3] as usize, }), MLIL_TAILCALL_UNTYPED_SSA => Op::TailcallUntypedSsa(CallUntypedSsa { output: op.operands[0] as usize, dest: op.operands[1] as usize, params: op.operands[2] as usize, stack: op.operands[3] as usize, }), MLIL_SYSCALL_SSA => Op::SyscallSsa(SyscallSsa { output: op.operands[0] as usize, 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: op.operands[0] as usize, params: op.operands[1] as usize, stack: op.operands[2] as usize, }), MLIL_CALL_UNTYPED => Op::CallUntyped(CallUntyped { output: op.operands[0] as usize, dest: op.operands[1] as usize, params: op.operands[2] as usize, stack: op.operands[3] as usize, }), MLIL_TAILCALL_UNTYPED => Op::TailcallUntyped(CallUntyped { output: op.operands[0] as usize, dest: op.operands[1] as usize, params: op.operands[2] as usize, stack: op.operands[3] as usize, }), MLIL_SYSCALL_UNTYPED => Op::SyscallUntyped(SyscallUntyped { output: op.operands[0] as usize, params: op.operands[1] as usize, stack: op.operands[2] as usize, }), MLIL_NEG => Op::Neg(UnaryOp { src: op.operands[0] as usize, }), MLIL_NOT => Op::Not(UnaryOp { src: op.operands[0] as usize, }), MLIL_SX => Op::Sx(UnaryOp { src: op.operands[0] as usize, }), MLIL_ZX => Op::Zx(UnaryOp { src: op.operands[0] as usize, }), MLIL_LOW_PART => Op::LowPart(UnaryOp { src: op.operands[0] as usize, }), MLIL_BOOL_TO_INT => Op::BoolToInt(UnaryOp { src: op.operands[0] as usize, }), MLIL_UNIMPL_MEM => Op::UnimplMem(UnaryOp { src: op.operands[0] as usize, }), MLIL_FSQRT => Op::Fsqrt(UnaryOp { src: op.operands[0] as usize, }), MLIL_FNEG => Op::Fneg(UnaryOp { src: op.operands[0] as usize, }), MLIL_FABS => Op::Fabs(UnaryOp { src: op.operands[0] as usize, }), MLIL_FLOAT_TO_INT => Op::FloatToInt(UnaryOp { src: op.operands[0] as usize, }), MLIL_INT_TO_FLOAT => Op::IntToFloat(UnaryOp { src: op.operands[0] as usize, }), MLIL_FLOAT_CONV => Op::FloatConv(UnaryOp { src: op.operands[0] as usize, }), MLIL_ROUND_TO_INT => Op::RoundToInt(UnaryOp { src: op.operands[0] as usize, }), MLIL_FLOOR => Op::Floor(UnaryOp { src: op.operands[0] as usize, }), MLIL_CEIL => Op::Ceil(UnaryOp { src: op.operands[0] as usize, }), MLIL_FTRUNC => Op::Ftrunc(UnaryOp { src: op.operands[0] as usize, }), MLIL_LOAD => Op::Load(UnaryOp { src: op.operands[0] as usize, }), MLIL_LOAD_STRUCT => Op::LoadStruct(LoadStruct { src: op.operands[0] as usize, offset: op.operands[1], }), MLIL_LOAD_STRUCT_SSA => Op::LoadStructSsa(LoadStructSsa { src: op.operands[0] as usize, offset: op.operands[1], src_memory: op.operands[2], }), MLIL_LOAD_SSA => Op::LoadSsa(LoadSsa { src: op.operands[0] as usize, 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_ADDRESS_OF => Op::AddressOf(Var { src: get_var(op.operands[0]), }), 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_TRAP => Op::Trap(Trap { vector: op.operands[0], }), // translated directly into a list for Expression or Variables // TODO MLIL_MEMORY_INTRINSIC_SSA needs to be handled properly MLIL_CALL_OUTPUT | MLIL_CALL_PARAM | MLIL_CALL_PARAM_SSA | MLIL_CALL_OUTPUT_SSA | MLIL_MEMORY_INTRINSIC_OUTPUT_SSA | MLIL_MEMORY_INTRINSIC_SSA => { unreachable!() } }; Self { function, address: op.address, index, size: op.size, kind, } } 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, 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.get_function(), RegisterValue { state: RegisterValueType::from_raw_value(op.constant_data_kind).unwrap(), 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: OperandIter::new(&*self.function, op.first_operand, op.num_operands) .pairs() .collect(), }), 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: OperandIter::new(&*self.function, op.first_operand, op.num_operands) .ssa_vars() .collect(), }), MemPhi(op) => Lifted::MemPhi(LiftedMemPhi { dest_memory: op.dest_memory, src_memory: OperandIter::new(&*self.function, op.first_operand, op.num_operands) .collect(), }), 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)), Tailcall(op) => Lifted::Tailcall(self.lift_call(op)), Intrinsic(op) => Lifted::Intrinsic(LiftedIntrinsic { output: OperandIter::new(&*self.function, op.first_output, op.num_outputs) .vars() .collect(), intrinsic: CoreIntrinsic(self.function.get_function().arch().0, op.intrinsic), params: OperandIter::new(&*self.function, op.first_param, op.num_params) .exprs() .map(|expr| expr.lift()) .collect(), }), Syscall(op) => Lifted::Syscall(LiftedSyscallCall { output: OperandIter::new(&*self.function, op.first_output, op.num_outputs) .vars() .collect(), params: OperandIter::new(&*self.function, op.first_param, op.num_params) .exprs() .map(|expr| expr.lift()) .collect(), }), IntrinsicSsa(op) => Lifted::IntrinsicSsa(LiftedIntrinsicSsa { output: OperandIter::new(&*self.function, op.first_output, op.num_outputs) .ssa_vars() .collect(), intrinsic: CoreIntrinsic(self.function.get_function().arch().0, op.intrinsic), params: OperandIter::new(&*self.function, op.first_param, op.num_params) .exprs() .map(|expr| expr.lift()) .collect(), }), 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) => Lifted::SyscallSsa(LiftedSyscallSsa { output: get_call_output_ssa(&self.function, op.output).collect(), params: OperandIter::new(&*self.function, op.first_param, op.num_params) .exprs() .map(|expr| expr.lift()) .collect(), src_memory: op.src_memory, }), SyscallUntypedSsa(op) => Lifted::SyscallUntypedSsa(LiftedSyscallUntypedSsa { output: get_call_output_ssa(&self.function, op.output).collect(), params: get_call_params_ssa(&self.function, op.params) .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) => Lifted::SyscallUntyped(LiftedSyscallUntyped { output: get_call_output(&self.function, op.output).collect(), params: get_call_params(&self.function, op.params) .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)), 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: OperandIter::new(&*self.function, op.first_operand, op.num_operands) .exprs() .map(|expr| expr.lift()) .collect(), }), SeparateParamList(op) => Lifted::SeparateParamList(LiftedSeparateParamList { params: OperandIter::new(&*self.function, op.first_param, op.num_params) .exprs() .map(|expr| expr.lift()) .collect(), }), SharedParamSlot(op) => Lifted::SharedParamSlot(LiftedSharedParamSlot { params: OperandIter::new(&*self.function, op.first_param, op.num_params) .exprs() .map(|expr| expr.lift()) .collect(), }), Var(op) => Lifted::Var(op), AddressOf(op) => Lifted::AddressOf(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), Trap(op) => Lifted::Trap(op), }; MediumLevelILLiftedInstruction { function: self.function.clone(), address: self.address, index: self.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.get_function().arch().0, self.index, &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.index) }.into() } /// Possible values of expression using path-sensitive static data flow analysis pub fn possible_values(&self, options: Option<&[DataFlowQueryOption]>) -> PossibleValueSet { let options_ptr = options .map(|op| op.as_ptr() as *mut DataFlowQueryOption) .unwrap_or(core::ptr::null_mut()); let options_len = options.map(|op| op.len()).unwrap_or(0); let mut value = unsafe { BNGetMediumLevelILPossibleExprValues( self.function.handle, self.index, options_ptr, options_len, ) }; let result = unsafe { PossibleValueSet::from_raw(value) }; unsafe { BNFreePossibleValueSet(&mut value) } result } pub fn possible_ssa_variable_values( &self, ssa_var: SSAVariable, options: Option<&[DataFlowQueryOption]>, ) -> PossibleValueSet { let options_ptr = options .map(|op| op.as_ptr() as *mut DataFlowQueryOption) .unwrap_or(core::ptr::null_mut()); let options_len = options.map(|op| op.len()).unwrap_or(0); let mut value = unsafe { BNGetMediumLevelILPossibleSSAVarValues( self.function.handle, &ssa_var.variable.raw(), ssa_var.version, self.index, options_ptr, options_len, ) }; let result = unsafe { PossibleValueSet::from_raw(value) }; unsafe { BNFreePossibleValueSet(&mut value) } result } /// return the variable version used at this instruction pub fn ssa_variable_version(&self, var: Variable) -> SSAVariable { let version = unsafe { BNGetMediumLevelILSSAVarVersionAtILInstruction( self.function.handle, &var.raw(), self.index, ) }; SSAVariable::new(var, version) } /// Set of branching instructions that must take the true or false path to reach this instruction pub fn branch_dependence(&self) -> Array { let mut count = 0; let deps = unsafe { BNGetAllMediumLevelILBranchDependence(self.function.handle, self.index, &mut count) }; assert!(!deps.is_null()); unsafe { Array::new(deps, count, self.function.clone()) } } pub fn branch_dependence_at(&self, instruction: MediumLevelILInstruction) -> BranchDependence { let deps = unsafe { BNGetMediumLevelILBranchDependence(self.function.handle, self.index, instruction.index) }; BranchDependence { 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.index) } } /// Type of expression pub fn expr_type(&self) -> Option>> { let result = unsafe { BNGetMediumLevelILExprType(self.function.handle, self.index) }; (!result.type_.is_null()).then(|| { Conf::new( unsafe { Type::ref_from_raw(result.type_) }, result.confidence, ) }) } /// 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, value: T) { let type_: Conf<&'a Type> = value.into(); let mut type_raw: BNTypeWithConfidence = BNTypeWithConfidence { type_: type_.contents.handle, confidence: type_.confidence, }; unsafe { BNSetMediumLevelILExprType(self.function.handle, self.index, &mut type_raw) } } pub fn variable_for_register(&self, reg_id: u32) -> Variable { let result = unsafe { BNGetMediumLevelILVariableForRegisterAtInstruction( self.function.handle, reg_id, self.index, ) }; unsafe { Variable::from_raw(result) } } pub fn variable_for_flag(&self, flag_id: u32) -> Variable { let result = unsafe { BNGetMediumLevelILVariableForFlagAtInstruction( self.function.handle, flag_id, self.index, ) }; unsafe { Variable::from_raw(result) } } pub fn variable_for_stack_location(&self, offset: i64) -> Variable { let result = unsafe { BNGetMediumLevelILVariableForStackLocationAtInstruction( self.function.handle, offset, self.index, ) }; unsafe { Variable::from_raw(result) } } pub fn register_value(&self, reg_id: u32) -> RegisterValue { unsafe { BNGetMediumLevelILRegisterValueAtInstruction(self.function.handle, reg_id, self.index) } .into() } pub fn register_value_after(&self, reg_id: u32) -> RegisterValue { unsafe { BNGetMediumLevelILRegisterValueAfterInstruction( self.function.handle, reg_id, self.index, ) } .into() } pub fn possible_register_values( &self, reg_id: u32, options: Option<&[DataFlowQueryOption]>, ) -> PossibleValueSet { let options_ptr = options .map(|op| op.as_ptr() as *mut DataFlowQueryOption) .unwrap_or(core::ptr::null_mut()); let options_len = options.map(|op| op.len()).unwrap_or(0); let mut value = unsafe { BNGetMediumLevelILPossibleRegisterValuesAtInstruction( self.function.handle, reg_id, self.index, options_ptr, options_len, ) }; let result = unsafe { PossibleValueSet::from_raw(value) }; unsafe { BNFreePossibleValueSet(&mut value) } result } pub fn possible_register_values_after( &self, reg_id: u32, options: Option<&[DataFlowQueryOption]>, ) -> PossibleValueSet { let options_ptr = options .map(|op| op.as_ptr() as *mut DataFlowQueryOption) .unwrap_or(core::ptr::null_mut()); let options_len = options.map(|op| op.len()).unwrap_or(0); let mut value = unsafe { BNGetMediumLevelILPossibleRegisterValuesAfterInstruction( self.function.handle, reg_id, self.index, options_ptr, options_len, ) }; let result = unsafe { PossibleValueSet::from_raw(value) }; unsafe { BNFreePossibleValueSet(&mut value) } result } pub fn flag_value(&self, flag_id: u32) -> RegisterValue { unsafe { BNGetMediumLevelILFlagValueAtInstruction(self.function.handle, flag_id, self.index) } .into() } pub fn flag_value_after(&self, flag_id: u32) -> RegisterValue { unsafe { BNGetMediumLevelILFlagValueAfterInstruction(self.function.handle, flag_id, self.index) } .into() } pub fn possible_flag_values( &self, flag_id: u32, options: Option<&[DataFlowQueryOption]>, ) -> PossibleValueSet { let options_ptr = options .map(|op| op.as_ptr() as *mut DataFlowQueryOption) .unwrap_or(core::ptr::null_mut()); let options_len = options.map(|op| op.len()).unwrap_or(0); let mut value = unsafe { BNGetMediumLevelILPossibleFlagValuesAtInstruction( self.function.handle, flag_id, self.index, options_ptr, options_len, ) }; let result = unsafe { PossibleValueSet::from_raw(value) }; unsafe { BNFreePossibleValueSet(&mut value) } result } pub fn possible_flag_values_after( &self, flag_id: u32, options: Option<&[DataFlowQueryOption]>, ) -> PossibleValueSet { let options_ptr = options .map(|op| op.as_ptr() as *mut DataFlowQueryOption) .unwrap_or(core::ptr::null_mut()); let options_len = options.map(|op| op.len()).unwrap_or(0); let mut value = unsafe { BNGetMediumLevelILPossibleFlagValuesAfterInstruction( self.function.handle, flag_id, self.index, options_ptr, options_len, ) }; let result = unsafe { PossibleValueSet::from_raw(value) }; unsafe { BNFreePossibleValueSet(&mut value) } result } pub fn stack_contents(&self, offset: i64, size: usize) -> RegisterValue { unsafe { BNGetMediumLevelILStackContentsAtInstruction( self.function.handle, offset, size, self.index, ) } .into() } pub fn stack_contents_after(&self, offset: i64, size: usize) -> RegisterValue { unsafe { BNGetMediumLevelILStackContentsAfterInstruction( self.function.handle, offset, size, self.index, ) } .into() } pub fn possible_stack_contents( &self, offset: i64, size: usize, options: Option<&[DataFlowQueryOption]>, ) -> PossibleValueSet { let options_ptr = options .map(|op| op.as_ptr() as *mut DataFlowQueryOption) .unwrap_or(core::ptr::null_mut()); let options_len = options.map(|op| op.len()).unwrap_or(0); let mut value = unsafe { BNGetMediumLevelILPossibleStackContentsAtInstruction( self.function.handle, offset, size, self.index, options_ptr, options_len, ) }; let result = unsafe { PossibleValueSet::from_raw(value) }; unsafe { BNFreePossibleValueSet(&mut value) } result } pub fn possible_stack_contents_after( &self, offset: i64, size: usize, options: Option<&[DataFlowQueryOption]>, ) -> PossibleValueSet { let options_ptr = options .map(|op| op.as_ptr() as *mut DataFlowQueryOption) .unwrap_or(core::ptr::null_mut()); let options_len = options.map(|op| op.len()).unwrap_or(0); let mut value = unsafe { BNGetMediumLevelILPossibleStackContentsAfterInstruction( self.function.handle, offset, size, self.index, options_ptr, options_len, ) }; let result = unsafe { PossibleValueSet::from_raw(value) }; unsafe { BNFreePossibleValueSet(&mut value) } result } /// 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 index = unsafe { BNGetDefaultIndexForMediumLevelILVariableDefinition( self.function.handle, &var.raw(), self.index, ) }; Variable::new(var.t, index, var.storage) } /// alias for [MediumLevelILInstruction::split_var_for_definition] #[inline] pub fn get_split_var_for_definition(&self, var: &Variable) -> Variable { self.split_var_for_definition(*var) } fn lift_operand(&self, expr_idx: usize) -> Box { Box::new(self.function.lifted_instruction_from_idx(expr_idx)) } 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: OperandIter::new(&*self.function, op.first_output, op.num_outputs) .vars() .collect(), dest: self.lift_operand(op.dest), params: OperandIter::new(&*self.function, op.first_param, op.num_params) .exprs() .map(|expr| expr.lift()) .collect(), } } fn lift_call_untyped(&self, op: CallUntyped) -> LiftedCallUntyped { LiftedCallUntyped { output: get_call_output(&self.function, op.output).collect(), dest: self.lift_operand(op.dest), params: get_call_params(&self.function, op.params) .map(|expr| expr.lift()) .collect(), stack: self.lift_operand(op.stack), } } fn lift_call_ssa(&self, op: CallSsa) -> LiftedCallSsa { LiftedCallSsa { output: get_call_output_ssa(&self.function, op.output).collect(), dest: self.lift_operand(op.dest), params: OperandIter::new(&*self.function, op.first_param, op.num_params) .exprs() .map(|expr| expr.lift()) .collect(), src_memory: op.src_memory, } } fn lift_call_untyped_ssa(&self, op: CallUntypedSsa) -> LiftedCallUntypedSsa { LiftedCallUntypedSsa { output: get_call_output_ssa(&self.function, op.output).collect(), dest: self.lift_operand(op.dest), params: get_call_params_ssa(&self.function, op.params) .map(|param| param.lift()) .collect(), stack: self.lift_operand(op.stack), } } } 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_idx(*raw) } } 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_raw_operation(function: &MediumLevelILFunction, idx: usize) -> BNMediumLevelILInstruction { unsafe { BNGetMediumLevelILByIndex(function.handle, idx) } } fn get_var(id: u64) -> Variable { unsafe { Variable::from_identifier(id) } } fn get_var_ssa(id: u64, version: usize) -> SSAVariable { SSAVariable::new(get_var(id), version) } fn get_call_output(function: &MediumLevelILFunction, idx: usize) -> impl Iterator { let op = get_raw_operation(function, idx); assert_eq!(op.operation, BNMediumLevelILOperation::MLIL_CALL_OUTPUT); OperandIter::new(function, op.operands[1] as usize, op.operands[0] as usize).vars() } fn get_call_params( function: &MediumLevelILFunction, idx: usize, ) -> impl Iterator { let op = get_raw_operation(function, idx); assert_eq!(op.operation, BNMediumLevelILOperation::MLIL_CALL_PARAM); OperandIter::new(function, op.operands[1] as usize, op.operands[0] as usize).exprs() } fn get_call_output_ssa( function: &MediumLevelILFunction, idx: usize, ) -> impl Iterator { let op = get_raw_operation(function, idx); assert_eq!(op.operation, BNMediumLevelILOperation::MLIL_CALL_OUTPUT_SSA); OperandIter::new(function, op.operands[2] as usize, op.operands[1] as usize).ssa_vars() } fn get_call_params_ssa( function: &MediumLevelILFunction, idx: usize, ) -> impl Iterator { let op = get_raw_operation(function, idx); assert_eq!(op.operation, BNMediumLevelILOperation::MLIL_CALL_PARAM_SSA); OperandIter::new(function, op.operands[2] as usize, op.operands[1] as usize).exprs() } /// 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::new(context.clone(), raw.branch), dependence: raw.dependence, } } }