diff --git a/Cargo.toml b/Cargo.toml index dfdd9e026..4cd2ace9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,4 +22,4 @@ members = [ [profile.release] lto = true -debug = "full" \ No newline at end of file +debug = "full" diff --git a/arch/msp430/src/architecture.rs b/arch/msp430/src/architecture.rs index 4cf0cc27e..797146767 100644 --- a/arch/msp430/src/architecture.rs +++ b/arch/msp430/src/architecture.rs @@ -8,7 +8,6 @@ use binaryninja::{ UnusedIntrinsic, UnusedRegisterStack, UnusedRegisterStackInfo, }, disassembly::{InstructionTextToken, InstructionTextTokenKind}, - llil::{LiftedExpr, Lifter}, Endianness, }; @@ -20,6 +19,8 @@ use msp430_asm::{ use binaryninja::architecture::{ BranchKind, FlagClassId, FlagGroupId, FlagId, FlagWriteId, RegisterId, }; +use binaryninja::lowlevelil::expression::ValueExpr; +use binaryninja::lowlevelil::{MutableLiftedILExpr, MutableLiftedILFunction}; use log::error; const MIN_MNEMONIC: usize = 9; @@ -192,7 +193,7 @@ impl Architecture for Msp430 { &self, data: &[u8], addr: u64, - il: &mut Lifter, + il: &mut MutableLiftedILFunction, ) -> Option<(usize, bool)> { match msp430_asm::decode(data) { Ok(inst) => { @@ -224,8 +225,8 @@ impl Architecture for Msp430 { fn flag_group_llil<'a>( &self, _group: Self::FlagGroup, - _il: &'a mut Lifter, - ) -> Option> { + _il: &'a mut MutableLiftedILFunction, + ) -> Option> { None } diff --git a/arch/msp430/src/lift.rs b/arch/msp430/src/lift.rs index 85dd7326a..164433096 100644 --- a/arch/msp430/src/lift.rs +++ b/arch/msp430/src/lift.rs @@ -3,10 +3,7 @@ use crate::flag::{Flag, FlagWrite}; use crate::register::Register; use crate::Msp430; -use binaryninja::{ - architecture::FlagCondition, - llil::{Label, LiftedNonSSA, Lifter, Mutable, NonSSA}, -}; +use binaryninja::{architecture::FlagCondition, lowlevelil::lifting::Label}; use msp430_asm::emulate::Emulated; use msp430_asm::instruction::Instruction; @@ -15,6 +12,8 @@ use msp430_asm::operand::{Operand, OperandWidth}; use msp430_asm::single_operand::SingleOperand; use msp430_asm::two_operand::TwoOperand; +use binaryninja::lowlevelil::expression::ValueExpr; +use binaryninja::lowlevelil::{MutableLiftedILExpr, MutableLiftedILFunction}; use log::info; macro_rules! auto_increment { @@ -169,7 +168,11 @@ macro_rules! conditional_jump { }; } -pub(crate) fn lift_instruction(inst: &Instruction, addr: u64, il: &Lifter) { +pub(crate) fn lift_instruction( + inst: &Instruction, + addr: u64, + il: &MutableLiftedILFunction, +) { match inst { Instruction::Rrc(inst) => { let size = match inst.operand_width() { @@ -631,14 +634,8 @@ pub(crate) fn lift_instruction(inst: &Instruction, addr: u64, il: &Lifter( operand: &Operand, size: usize, - il: &'a Lifter, -) -> binaryninja::llil::Expression< - 'a, - Msp430, - Mutable, - NonSSA, - binaryninja::llil::ValueExpr, -> { + il: &'a MutableLiftedILFunction, +) -> MutableLiftedILExpr<'a, Msp430, ValueExpr> { match operand { Operand::RegisterDirect(r) => il.reg(size, Register::try_from(*r as u32).unwrap()), Operand::Indexed((r, offset)) => il diff --git a/arch/msp430/src/register.rs b/arch/msp430/src/register.rs index a49a4e645..826a6ed6d 100644 --- a/arch/msp430/src/register.rs +++ b/arch/msp430/src/register.rs @@ -1,6 +1,7 @@ use binaryninja::architecture; use binaryninja::architecture::{ImplicitRegisterExtend, RegisterId}; +use binaryninja::lowlevelil::LowLevelILRegister; use std::borrow::Cow; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -133,8 +134,8 @@ impl architecture::RegisterInfo for Register { } } -impl From for binaryninja::llil::Register { +impl From for LowLevelILRegister { fn from(register: Register) -> Self { - binaryninja::llil::Register::ArchReg(register) + LowLevelILRegister::ArchReg(register) } } diff --git a/arch/riscv/src/lib.rs b/arch/riscv/src/lib.rs index b3a996b1d..8fb29c186 100644 --- a/arch/riscv/src/lib.rs +++ b/arch/riscv/src/lib.rs @@ -20,11 +20,6 @@ use binaryninja::{ disassembly::{InstructionTextToken, InstructionTextTokenKind}, function::Function, functionrecognizer::FunctionRecognizer, - llil, - llil::{ - ExprInfo, InstrInfo, Label, Liftable, LiftableWithSize, LiftedNonSSA, Lifter, Mutable, - NonSSA, - }, rc::Ref, relocation::{ CoreRelocationHandler, CustomRelocationHandlerHandle, RelocationHandler, RelocationInfo, @@ -41,8 +36,14 @@ use std::marker::PhantomData; use binaryninja::architecture::{BranchKind, IntrinsicId, RegisterId}; use binaryninja::confidence::{Conf, MAX_CONFIDENCE, MIN_CONFIDENCE}; -use binaryninja::llil::{ExpressionHandler, InstructionHandler}; use binaryninja::logger::Logger; +use binaryninja::lowlevelil::expression::{LowLevelILExpressionKind, ValueExpr}; +use binaryninja::lowlevelil::instruction::LowLevelILInstructionKind; +use binaryninja::lowlevelil::lifting::{Label, LiftableLowLevelIL, LiftableLowLevelILWithSize}; +use binaryninja::lowlevelil::{ + expression::ExpressionHandler, instruction::InstructionHandler, LowLevelILRegister, + MutableLiftedILExpr, MutableLiftedILFunction, RegularLowLevelILFunction, +}; use riscv_dis::{ FloatReg, FloatRegType, Instr, IntRegType, Op, RegFile, Register as RiscVRegister, RiscVDisassembler, RoundMode, @@ -135,9 +136,9 @@ impl From> for Register { } } -impl From> for llil::Register> { +impl From> for LowLevelILRegister> { fn from(reg: Register) -> Self { - llil::Register::ArchReg(reg) + LowLevelILRegister::ArchReg(reg) } } @@ -201,13 +202,15 @@ impl architecture::Register for Register { } } -impl<'a, D: 'static + RiscVDisassembler + Send + Sync> Liftable<'a, RiscVArch> for Register { - type Result = llil::ValueExpr; +impl<'a, D: 'static + RiscVDisassembler + Send + Sync> LiftableLowLevelIL<'a, RiscVArch> + for Register +{ + type Result = ValueExpr; fn lift( - il: &'a llil::Lifter>, + il: &'a MutableLiftedILFunction>, reg: Self, - ) -> llil::Expression<'a, RiscVArch, Mutable, NonSSA, Self::Result> { + ) -> MutableLiftedILExpr<'a, RiscVArch, Self::Result> { match reg.reg_type() { RegType::Integer(0) => il.const_int(reg.size(), 0), RegType::Integer(_) => il.reg(reg.size(), reg), @@ -216,14 +219,14 @@ impl<'a, D: 'static + RiscVDisassembler + Send + Sync> Liftable<'a, RiscVArch } } -impl<'a, D: 'static + RiscVDisassembler + Send + Sync> LiftableWithSize<'a, RiscVArch> +impl<'a, D: 'static + RiscVDisassembler + Send + Sync> LiftableLowLevelILWithSize<'a, RiscVArch> for Register { fn lift_with_size( - il: &'a llil::Lifter>, + il: &'a MutableLiftedILFunction>, reg: Self, size: usize, - ) -> llil::Expression<'a, RiscVArch, Mutable, NonSSA, llil::ValueExpr> { + ) -> MutableLiftedILExpr<'a, RiscVArch, ValueExpr> { #[cfg(debug_assertions)] { if reg.size() < size { @@ -1063,7 +1066,7 @@ impl architecture::Architecture fo &self, data: &[u8], addr: u64, - il: &mut llil::Lifter, + il: &mut MutableLiftedILFunction, ) -> Option<(usize, bool)> { let max_width = self.default_integer_size(); @@ -1240,7 +1243,7 @@ impl architecture::Architecture fo (0, _, _) => il.jump(target).append(), // indirect jump (rd_id, rs1_id, _) if rd_id == rs1_id => { // store the target in a temporary register so we don't clobber it when rd == rs1 - let tmp_reg: llil::Register> = llil::Register::Temp(0); + let tmp_reg: LowLevelILRegister> = LowLevelILRegister::Temp(0); il.set_reg(max_width, tmp_reg, target).append(); // indirect jump with storage of next address to non-`ra` register il.set_reg( @@ -1314,41 +1317,41 @@ impl architecture::Architecture fo Op::Ebreak => il.bp().append(), Op::Uret => { il.intrinsic( - Lifter::::NO_OUTPUTS, + MutableLiftedILFunction::::NO_OUTPUTS, Intrinsic::Uret, - Lifter::::NO_INPUTS, + MutableLiftedILFunction::::NO_INPUTS, ) .append(); il.no_ret().append(); } Op::Sret => { il.intrinsic( - Lifter::::NO_OUTPUTS, + MutableLiftedILFunction::::NO_OUTPUTS, Intrinsic::Sret, - Lifter::::NO_INPUTS, + MutableLiftedILFunction::::NO_INPUTS, ) .append(); il.no_ret().append(); } Op::Mret => { il.intrinsic( - Lifter::::NO_OUTPUTS, + MutableLiftedILFunction::::NO_OUTPUTS, Intrinsic::Mret, - Lifter::::NO_INPUTS, + MutableLiftedILFunction::::NO_INPUTS, ) .append(); il.no_ret().append(); } Op::Wfi => il .intrinsic( - Lifter::::NO_OUTPUTS, + MutableLiftedILFunction::::NO_OUTPUTS, Intrinsic::Wfi, - Lifter::::NO_INPUTS, + MutableLiftedILFunction::::NO_INPUTS, ) .append(), Op::Fence(i) => il .intrinsic( - Lifter::::NO_OUTPUTS, + MutableLiftedILFunction::::NO_OUTPUTS, Intrinsic::Fence, [il.const_int(4, i.imm() as u32 as u64)], ) @@ -1356,19 +1359,23 @@ impl architecture::Architecture fo Op::Csrrw(i) => { let rd = Register::from(i.rd()); - let rs1 = Liftable::lift(il, Register::from(i.rs1())); + let rs1 = LiftableLowLevelIL::lift(il, Register::from(i.rs1())); let csr = il.const_int(4, i.csr() as u64); if i.rd().id() == 0 { - il.intrinsic(Lifter::::NO_OUTPUTS, Intrinsic::Csrwr, [csr, rs1]) - .append(); + il.intrinsic( + MutableLiftedILFunction::::NO_OUTPUTS, + Intrinsic::Csrwr, + [csr, rs1], + ) + .append(); } else { il.intrinsic([rd], Intrinsic::Csrrw, [rs1]).append(); } } Op::Csrrs(i) => { let rd = Register::from(i.rd()); - let rs1 = Liftable::lift(il, Register::from(i.rs1())); + let rs1 = LiftableLowLevelIL::lift(il, Register::from(i.rs1())); let csr = il.const_int(4, i.csr() as u64); if i.rs1().id() == 0 { @@ -1379,7 +1386,7 @@ impl architecture::Architecture fo } Op::Csrrc(i) => { let rd = Register::from(i.rd()); - let rs1 = Liftable::lift(il, Register::from(i.rs1())); + let rs1 = LiftableLowLevelIL::lift(il, Register::from(i.rs1())); let csr = il.const_int(4, i.csr() as u64); if i.rs1().id() == 0 { @@ -1394,8 +1401,12 @@ impl architecture::Architecture fo let imm = il.const_int(max_width, i.imm() as u64); if i.rd().id() == 0 { - il.intrinsic(Lifter::::NO_OUTPUTS, Intrinsic::Csrwr, [csr, imm]) - .append(); + il.intrinsic( + MutableLiftedILFunction::::NO_OUTPUTS, + Intrinsic::Csrwr, + [csr, imm], + ) + .append(); } else { il.intrinsic([rd], Intrinsic::Csrrw, [csr, imm]).append(); } @@ -1438,7 +1449,7 @@ impl architecture::Architecture fo let rd = a.rd(); let dest_reg = match rd.id() { - 0 => llil::Register::Temp(0), + 0 => LowLevelILRegister::Temp(0), _ => Register::from(rd).into(), }; @@ -1488,14 +1499,14 @@ impl architecture::Architecture fo let rs2 = a.rs2(); let dest_reg = match rd.id() { - 0 => llil::Register::Temp(0), + 0 => LowLevelILRegister::Temp(0), _ => Register::from(rd).into(), }; let mut next_temp_reg = 1; let mut alloc_reg = |rs: riscv_dis::IntReg| match (rs.id(), rd.id()) { (id, r) if id != 0 && id == r => { - let reg = llil::Register::Temp(next_temp_reg); + let reg = LowLevelILRegister::Temp(next_temp_reg); next_temp_reg += 1; il.set_reg(max_width, reg, Register::from(rs)).append(); @@ -1516,8 +1527,8 @@ impl architecture::Architecture fo il.set_reg(max_width, dest_reg, load_expr).append(); - let val_expr = LiftableWithSize::lift_with_size(il, reg_with_val, size); - let dest_reg_val = LiftableWithSize::lift_with_size(il, dest_reg, size); + let val_expr = LiftableLowLevelILWithSize::lift_with_size(il, reg_with_val, size); + let dest_reg_val = LiftableLowLevelILWithSize::lift_with_size(il, dest_reg, size); let val_to_store = match op { Op::AmoSwap(..) => val_expr, @@ -1575,7 +1586,7 @@ impl architecture::Architecture fo }; il.set_reg(width, rd, result).append(); } else { - let product = llil::Register::Temp(0); + let product = LowLevelILRegister::Temp(0); il.intrinsic( [product], Intrinsic::Fmul(f.width(), f.rm()), @@ -1730,7 +1741,7 @@ impl architecture::Architecture fo } Op::Fle(f) | Op::Flt(f) | Op::Feq(f) => { let rd = match f.rd().id() { - 0 => llil::Register::Temp(0), + 0 => LowLevelILRegister::Temp(0), _ => Register::from(f.rd()).into(), }; let left = Register::from(f.rs1()); @@ -1764,7 +1775,7 @@ impl architecture::Architecture fo } Op::FcvtToInt(f) => { let rd = match f.rd().id() { - 0 => llil::Register::Temp(0), + 0 => LowLevelILRegister::Temp(0), _ => Register::from(f.rd()).into(), }; let rs1 = Register::from(f.rs1()); @@ -1799,7 +1810,7 @@ impl architecture::Architecture fo let rs1 = Register::from(f.rs1()); let rd_width = f.rd_width() as usize; let rs1_width = f.rs1_width() as usize; - let rs1 = LiftableWithSize::lift_with_size(il, rs1, rs1_width); + let rs1 = LiftableLowLevelILWithSize::lift_with_size(il, rs1, rs1_width); if f.zx() { il.intrinsic( [rd], @@ -1821,7 +1832,7 @@ impl architecture::Architecture fo } Op::FmvToInt(f) => { let rd = match f.rd().id() { - 0 => llil::Register::Temp(0), + 0 => LowLevelILRegister::Temp(0), _ => Register::from(f.rd()).into(), }; let rs1 = Register::from(f.rs1()); @@ -1837,7 +1848,7 @@ impl architecture::Architecture fo let rd = Register::from(f.rd()); let rs1 = Register::from(f.rs1()); let width = f.width() as usize; - let rs1 = LiftableWithSize::lift_with_size(il, rs1, width); + let rs1 = LiftableLowLevelILWithSize::lift_with_size(il, rs1, width); il.set_reg(width, rd, rs1).append(); } Op::Fclass(f) => { @@ -2791,7 +2802,7 @@ impl FunctionRecognizer for RiscVELFPLTRecognizer { &self, bv: &BinaryView, func: &Function, - llil: &llil::RegularFunction, + llil: &RegularLowLevelILFunction, ) -> bool { // Look for the following code pattern: // t3 = plt @@ -2807,11 +2818,13 @@ impl FunctionRecognizer for RiscVELFPLTRecognizer { let mut next_llil_instr = llil.basic_blocks().iter().next().unwrap().iter(); // Match instruction that fetches PC-relative PLT address range - let auipc = next_llil_instr.next().unwrap().info(); + let auipc = next_llil_instr.next().unwrap().kind(); let (auipc_dest, plt_base) = match auipc { - InstrInfo::SetReg(r) => { - let value = match r.source_expr().info() { - ExprInfo::Const(v) | ExprInfo::ConstPtr(v) => v.value(), + LowLevelILInstructionKind::SetReg(r) => { + let value = match r.source_expr().kind() { + LowLevelILExpressionKind::Const(v) | LowLevelILExpressionKind::ConstPtr(v) => { + v.value() + } _ => return false, }; (r.dest_reg(), value) @@ -2820,34 +2833,46 @@ impl FunctionRecognizer for RiscVELFPLTRecognizer { }; // Match load instruction that loads the imported address - let load = next_llil_instr.next().unwrap().info(); + let load = next_llil_instr.next().unwrap().kind(); let (mut entry, mut target_reg) = match load { - InstrInfo::SetReg(r) => match r.source_expr().info() { - ExprInfo::Load(l) => { + LowLevelILInstructionKind::SetReg(r) => match r.source_expr().kind() { + LowLevelILExpressionKind::Load(l) => { let target_reg = r.dest_reg(); - let entry = match l.source_mem_expr().info() { - ExprInfo::Reg(lr) if lr.source_reg() == auipc_dest => plt_base, - ExprInfo::Add(a) => match (a.left().info(), a.right().info()) { - (ExprInfo::Reg(a), ExprInfo::Const(b) | ExprInfo::ConstPtr(b)) - if a.source_reg() == auipc_dest => - { - plt_base.wrapping_add(b.value()) - } - (ExprInfo::Const(b) | ExprInfo::ConstPtr(b), ExprInfo::Reg(a)) - if a.source_reg() == auipc_dest => - { - plt_base.wrapping_add(b.value()) + let entry = match l.source_mem_expr().kind() { + LowLevelILExpressionKind::Reg(lr) if lr.source_reg() == auipc_dest => { + plt_base + } + LowLevelILExpressionKind::Add(a) => { + match (a.left().kind(), a.right().kind()) { + ( + LowLevelILExpressionKind::Reg(a), + LowLevelILExpressionKind::Const(b) + | LowLevelILExpressionKind::ConstPtr(b), + ) if a.source_reg() == auipc_dest => { + plt_base.wrapping_add(b.value()) + } + ( + LowLevelILExpressionKind::Const(b) + | LowLevelILExpressionKind::ConstPtr(b), + LowLevelILExpressionKind::Reg(a), + ) if a.source_reg() == auipc_dest => { + plt_base.wrapping_add(b.value()) + } + _ => return false, } - _ => return false, - }, - ExprInfo::Sub(a) => match (a.left().info(), a.right().info()) { - (ExprInfo::Reg(a), ExprInfo::Const(b) | ExprInfo::ConstPtr(b)) - if a.source_reg() == auipc_dest => - { - plt_base.wrapping_sub(b.value()) + } + LowLevelILExpressionKind::Sub(a) => { + match (a.left().kind(), a.right().kind()) { + ( + LowLevelILExpressionKind::Reg(a), + LowLevelILExpressionKind::Const(b) + | LowLevelILExpressionKind::ConstPtr(b), + ) if a.source_reg() == auipc_dest => { + plt_base.wrapping_sub(b.value()) + } + _ => return false, } - _ => return false, - }, + } _ => return false, }; (entry, target_reg) @@ -2870,14 +2895,14 @@ impl FunctionRecognizer for RiscVELFPLTRecognizer { } // (OPTIONAL) Check if we are storing in temp0, adjust target reg if so - let mut temp_reg_inst = next_llil_instr.next().unwrap().info(); + let mut temp_reg_inst = next_llil_instr.next().unwrap().kind(); match &temp_reg_inst { - InstrInfo::SetReg(r) if llil.instruction_count() >= 5 => { - match r.source_expr().info() { - ExprInfo::Reg(op) if target_reg == op.source_reg() => { + LowLevelILInstructionKind::SetReg(r) if llil.instruction_count() >= 5 => { + match r.source_expr().kind() { + LowLevelILExpressionKind::Reg(op) if target_reg == op.source_reg() => { // Update the target_reg to the temp reg. target_reg = r.dest_reg(); - temp_reg_inst = next_llil_instr.next().unwrap().info() + temp_reg_inst = next_llil_instr.next().unwrap().kind() } _ => {} } @@ -2888,9 +2913,11 @@ impl FunctionRecognizer for RiscVELFPLTRecognizer { // Match instruction that stores the next instruction address into a register let next_pc_inst = temp_reg_inst; let (next_pc_dest, next_pc, cur_pc) = match next_pc_inst { - InstrInfo::SetReg(r) => { - let value = match r.source_expr().info() { - ExprInfo::Const(v) | ExprInfo::ConstPtr(v) => v.value(), + LowLevelILInstructionKind::SetReg(r) => { + let value = match r.source_expr().kind() { + LowLevelILExpressionKind::Const(v) | LowLevelILExpressionKind::ConstPtr(v) => { + v.value() + } _ => return false, }; (r.dest_reg(), value, r.address()) @@ -2902,17 +2929,17 @@ impl FunctionRecognizer for RiscVELFPLTRecognizer { } // Match tail call at the end and make sure it is going to the import - let jump = next_llil_instr.next().unwrap().info(); + let jump = next_llil_instr.next().unwrap().kind(); match jump { - InstrInfo::TailCall(j) => { - match j.target().info() { - ExprInfo::Reg(r) if r.source_reg() == target_reg => (), + LowLevelILInstructionKind::TailCall(j) => { + match j.target().kind() { + LowLevelILExpressionKind::Reg(r) if r.source_reg() == target_reg => (), _ => return false, }; } - InstrInfo::Jump(j) => { - match j.target().info() { - ExprInfo::Reg(r) if r.source_reg() == target_reg => (), + LowLevelILInstructionKind::Jump(j) => { + match j.target().kind() { + LowLevelILExpressionKind::Reg(r) if r.source_reg() == target_reg => (), _ => return false, }; } diff --git a/plugins/warp/src/cache.rs b/plugins/warp/src/cache.rs index b28ab903f..94d7a4d5c 100644 --- a/plugins/warp/src/cache.rs +++ b/plugins/warp/src/cache.rs @@ -4,12 +4,15 @@ use binaryninja::architecture::Architecture; use binaryninja::binaryview::{BinaryView, BinaryViewExt}; use binaryninja::confidence::MAX_CONFIDENCE; use binaryninja::function::Function as BNFunction; -use binaryninja::llil::{FunctionMutability, NonSSA, RegularNonSSA}; +use binaryninja::lowlevelil::function::{ + FunctionMutability, LowLevelILFunction, NonSSA, RegularNonSSA, +}; +use binaryninja::lowlevelil::RegularLowLevelILFunction; use binaryninja::rc::Guard; use binaryninja::rc::Ref as BNRef; use binaryninja::symbol::Symbol as BNSymbol; use binaryninja::types::NamedTypeReference as BNNamedTypeReference; -use binaryninja::{llil, ObjectDestructor}; +use binaryninja::ObjectDestructor; use dashmap::mapref::one::Ref; use dashmap::DashMap; use std::collections::HashSet; @@ -65,9 +68,9 @@ pub fn try_cached_function_match(function: &BNFunction) -> Option { .to_owned() } -pub fn cached_function( +pub fn cached_function( function: &BNFunction, - llil: &llil::LowLevelILFunction>, + llil: &RegularLowLevelILFunction, ) -> Function { let view = function.view(); let view_id = ViewID::from(view.as_ref()); @@ -121,7 +124,7 @@ where pub fn cached_function_guid( function: &BNFunction, - llil: &llil::LowLevelILFunction>, + llil: &LowLevelILFunction>, ) -> FunctionGUID { let view = function.view(); let view_id = ViewID::from(view); @@ -199,10 +202,10 @@ pub struct FunctionCache { } impl FunctionCache { - pub fn function( + pub fn function( &self, function: &BNFunction, - llil: &llil::LowLevelILFunction>, + llil: &RegularLowLevelILFunction, ) -> Function { let function_id = FunctionID::from(function); match self.cache.get(&function_id) { @@ -330,7 +333,7 @@ impl GUIDCache { pub fn function_guid( &self, function: &BNFunction, - llil: &llil::LowLevelILFunction>, + llil: &LowLevelILFunction>, ) -> FunctionGUID { let function_id = FunctionID::from(function); match self.cache.get(&function_id) { diff --git a/plugins/warp/src/lib.rs b/plugins/warp/src/lib.rs index ed95f46cc..8df82c9b2 100644 --- a/plugins/warp/src/lib.rs +++ b/plugins/warp/src/lib.rs @@ -9,11 +9,14 @@ use binaryninja::basicblock::BasicBlock as BNBasicBlock; use binaryninja::binaryview::BinaryViewExt; use binaryninja::confidence::MAX_CONFIDENCE; use binaryninja::function::{Function as BNFunction, NativeBlock}; -use binaryninja::llil; -use binaryninja::llil::{ - ExprInfo, ExpressionHandler, FunctionMutability, InstrInfo, Instruction, InstructionHandler, - NonSSA, Register, RegularNonSSA, VisitorAction, +use binaryninja::lowlevelil::expression::{ExpressionHandler, LowLevelILExpressionKind}; +use binaryninja::lowlevelil::function::{ + FunctionMutability, LowLevelILFunction, NonSSA, RegularNonSSA, }; +use binaryninja::lowlevelil::instruction::{ + InstructionHandler, LowLevelILInstruction, LowLevelILInstructionKind, +}; +use binaryninja::lowlevelil::{LowLevelILRegister, VisitorAction}; use binaryninja::rc::Ref as BNRef; use std::path::PathBuf; use warp::signature::basic_block::BasicBlockGUID; @@ -43,7 +46,7 @@ pub fn user_signature_dir() -> PathBuf { pub fn build_function( func: &BNFunction, - llil: &llil::LowLevelILFunction>, + llil: &LowLevelILFunction>, ) -> Function { let bn_fn_ty = func.function_type(); Function { @@ -69,13 +72,13 @@ pub fn sorted_basic_blocks(func: &BNFunction) -> Vec>(); - basic_blocks.sort_by_key(|f| f.raw_start()); + basic_blocks.sort_by_key(|f| f.start_index()); basic_blocks } pub fn function_guid( func: &BNFunction, - llil: &llil::LowLevelILFunction>, + llil: &LowLevelILFunction>, ) -> FunctionGUID { let basic_blocks = sorted_basic_blocks(func); let basic_block_guids = basic_blocks @@ -87,7 +90,7 @@ pub fn function_guid( pub fn basic_block_guid( basic_block: &BNBasicBlock, - llil: &llil::LowLevelILFunction>, + llil: &LowLevelILFunction>, ) -> BasicBlockGUID { let func = basic_block.function(); let view = func.view(); @@ -95,14 +98,16 @@ pub fn basic_block_guid( let max_instr_len = arch.max_instr_len(); // NOPs and useless moves are blacklisted to allow for hot-patchable functions. - let is_blacklisted_instr = |instr: &Instruction>| { - match instr.info() { - InstrInfo::Nop(_) => true, - InstrInfo::SetReg(op) => { - match op.source_expr().info() { - ExprInfo::Reg(source_op) if op.dest_reg() == source_op.source_reg() => { + let is_blacklisted_instr = |instr: &LowLevelILInstruction>| { + match instr.kind() { + LowLevelILInstructionKind::Nop(_) => true, + LowLevelILInstructionKind::SetReg(op) => { + match op.source_expr().kind() { + LowLevelILExpressionKind::Reg(source_op) + if op.dest_reg() == source_op.source_reg() => + { match op.dest_reg() { - Register::ArchReg(r) => { + LowLevelILRegister::ArchReg(r) => { // If this register has no implicit extend then we can safely assume it's a NOP. // Ex. on x86_64 we don't want to remove `mov edi, edi` as it will zero the upper 32 bits. // Ex. on x86 we do want to remove `mov edi, edi` as it will not have a side effect like above. @@ -111,7 +116,7 @@ pub fn basic_block_guid( ImplicitRegisterExtend::NoExtend ) } - Register::Temp(_) => false, + LowLevelILRegister::Temp(_) => false, } } _ => false, @@ -121,16 +126,18 @@ pub fn basic_block_guid( } }; - let is_variant_instr = |instr: &Instruction>| { - let is_variant_expr = |expr: &ExprInfo>| { + let is_variant_instr = |instr: &LowLevelILInstruction>| { + let is_variant_expr = |expr: &LowLevelILExpressionKind>| { match expr { - ExprInfo::ConstPtr(op) if !view.sections_at(op.value()).is_empty() => { + LowLevelILExpressionKind::ConstPtr(op) + if !view.sections_at(op.value()).is_empty() => + { // Constant Pointer must be in a section for it to be relocatable. // NOTE: We cannot utilize segments here as there will be a zero based segment. true } - ExprInfo::ExternPtr(_) => true, - ExprInfo::Const(op) if !view.sections_at(op.value()).is_empty() => { + LowLevelILExpressionKind::ExternPtr(_) => true, + LowLevelILExpressionKind::Const(op) if !view.sections_at(op.value()).is_empty() => { // Constant value must be in a section for it to be relocatable. // NOTE: We cannot utilize segments here as there will be a zero based segment. true @@ -141,7 +148,7 @@ pub fn basic_block_guid( // Visit instruction expressions looking for variant expression, [VisitorAction::Halt] means variant. instr.visit_tree(&mut |expr| { - if is_variant_expr(&expr.info()) { + if is_variant_expr(&expr.kind()) { // Found a variant expression VisitorAction::Halt } else { @@ -150,7 +157,7 @@ pub fn basic_block_guid( }) == VisitorAction::Halt }; - let basic_block_range = basic_block.raw_start()..basic_block.raw_end(); + let basic_block_range = basic_block.start_index()..basic_block.end_index(); let mut basic_block_bytes = Vec::with_capacity(basic_block_range.count()); for instr_addr in basic_block.into_iter() { let mut instr_bytes = view.read_vec(instr_addr, max_instr_len); diff --git a/plugins/warp/src/plugin/workflow.rs b/plugins/warp/src/plugin/workflow.rs index 8906bdd82..85290c3f9 100644 --- a/plugins/warp/src/plugin/workflow.rs +++ b/plugins/warp/src/plugin/workflow.rs @@ -3,7 +3,7 @@ use crate::matcher::cached_function_matcher; use binaryninja::backgroundtask::BackgroundTask; use binaryninja::binaryview::{BinaryView, BinaryViewExt}; use binaryninja::command::Command; -use binaryninja::llil; +use binaryninja::lowlevelil::function::RegularNonSSA; use binaryninja::workflow::{Activity, AnalysisContext, Workflow}; use std::time::Instant; @@ -73,7 +73,8 @@ pub fn insert_workflow() { let guid_activity = |ctx: &AnalysisContext| { let function = ctx.function(); - if let Some(llil) = unsafe { ctx.llil_function::>() } { + // TODO: Returning RegularNonSSA means we cant modify the il (the lifting code was written just for lifted il, that needs to be fixed) + if let Some(llil) = unsafe { ctx.llil_function::() } { cached_function_guid(&function, &llil); } }; diff --git a/rust/build.rs b/rust/build.rs index 81e96e954..16d663c4d 100644 --- a/rust/build.rs +++ b/rust/build.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + fn main() { // TODO : Enable the following when https://github.com/rust-lang/rust/issues/43781 stabilizes // #[cfg(doc)] @@ -19,4 +21,21 @@ fn main() { link_path.to_string_lossy() ); } + + let out_dir = std::env::var("OUT_DIR").expect("OUT_DIR specified"); + let out_dir_path = PathBuf::from(out_dir); + + // Copy all binaries to OUT_DIR for unit tests. + let bin_dir: PathBuf = "fixtures/bin".into(); + if let Ok(entries) = std::fs::read_dir(bin_dir) { + for entry in entries { + let entry = entry.unwrap(); + let path = entry.path(); + if path.is_file() { + let file_name = path.file_name().unwrap(); + let dest_path = out_dir_path.join(file_name); + std::fs::copy(&path, &dest_path).expect("failed to copy binary to OUT_DIR"); + } + } + } } diff --git a/rust/examples/workflow.rs b/rust/examples/workflow.rs index 84e6eab36..9279a02f9 100644 --- a/rust/examples/workflow.rs +++ b/rust/examples/workflow.rs @@ -1,7 +1,7 @@ use binaryninja::binaryview::BinaryViewExt; -use binaryninja::llil::{ - ExprInfo, ExpressionHandler, InstructionHandler, LiftedNonSSA, NonSSA, VisitorAction, -}; +use binaryninja::lowlevelil::expression::{ExpressionHandler, LowLevelILExpressionKind}; +use binaryninja::lowlevelil::instruction::InstructionHandler; +use binaryninja::lowlevelil::VisitorAction; use binaryninja::workflow::{Activity, AnalysisContext, Workflow}; const RUST_ACTIVITY_NAME: &str = "analysis.plugins.rustexample"; @@ -24,12 +24,12 @@ fn example_activity(analysis_context: &AnalysisContext) { func.workflow().map(|wf| wf.name()) ); // If we have llil available, replace that as well. - if let Some(llil) = unsafe { analysis_context.llil_function::>() } { + if let Some(llil) = unsafe { analysis_context.llil_function() } { for basic_block in &func.basic_blocks() { for instr in basic_block.iter() { if let Some(llil_instr) = llil.instruction_at(instr) { llil_instr.visit_tree(&mut |expr| { - if let ExprInfo::Const(_op) = expr.info() { + if let LowLevelILExpressionKind::Const(_op) = expr.kind() { // Replace all consts with 0x1337. println!("Replacing llil expression @ 0x{:x} : {}", instr, expr.index); unsafe { @@ -71,7 +71,7 @@ pub fn main() { for block in &llil.basic_blocks() { for instr in block.iter() { instr.visit_tree(&mut |expr| { - if let ExprInfo::Const(value) = expr.info() { + if let LowLevelILExpressionKind::Const(value) = expr.kind() { if value.value() == 0x1337 { println!( "Found constant 0x1337 at instruction 0x{:x} in function {}", diff --git a/rust/src/architecture.rs b/rust/src/architecture.rs index 9a3648894..dd51ddfa2 100644 --- a/rust/src/architecture.rs +++ b/rust/src/architecture.rs @@ -23,9 +23,7 @@ use crate::{ callingconvention::CallingConvention, databuffer::DataBuffer, disassembly::InstructionTextToken, - llil::{ - get_default_flag_cond_llil, get_default_flag_write_llil, FlagWriteOp, LiftedExpr, Lifter, - }, + lowlevelil::{MutableLiftedILExpr, MutableLiftedILFunction}, platform::Platform, rc::*, relocation::CoreRelocationHandler, @@ -48,6 +46,10 @@ use crate::functionrecognizer::FunctionRecognizer; use crate::relocation::{CustomRelocationHandlerHandle, RelocationHandler}; use crate::confidence::Conf; +use crate::lowlevelil::expression::ValueExpr; +use crate::lowlevelil::lifting::{ + get_default_flag_cond_llil, get_default_flag_write_llil, LowLevelILFlagWriteOp, +}; pub use binaryninjacore_sys::BNFlagRole as FlagRole; pub use binaryninjacore_sys::BNImplicitRegisterExtend as ImplicitRegisterExtend; pub use binaryninjacore_sys::BNLowLevelILFlagCondition as FlagCondition; @@ -458,7 +460,7 @@ pub trait Architecture: 'static + Sized + AsRef { &self, data: &[u8], addr: u64, - il: &mut Lifter, + il: &mut MutableLiftedILFunction, ) -> Option<(usize, bool)>; /// Fallback flag value calculation path. This method is invoked when the core is unable to @@ -474,9 +476,9 @@ pub trait Architecture: 'static + Sized + AsRef { &self, flag: Self::Flag, flag_write_type: Self::FlagWrite, - op: FlagWriteOp, - il: &'a mut Lifter, - ) -> Option> { + op: LowLevelILFlagWriteOp, + il: &'a mut MutableLiftedILFunction, + ) -> Option> { let role = flag.role(flag_write_type.class()); Some(get_default_flag_write_llil(self, role, op, il)) } @@ -504,8 +506,8 @@ pub trait Architecture: 'static + Sized + AsRef { &self, cond: FlagCondition, class: Option, - il: &'a mut Lifter, - ) -> Option> { + il: &'a mut MutableLiftedILFunction, + ) -> Option> { Some(get_default_flag_cond_llil(self, cond, class, il)) } @@ -526,8 +528,8 @@ pub trait Architecture: 'static + Sized + AsRef { fn flag_group_llil<'a>( &self, _group: Self::FlagGroup, - _il: &'a mut Lifter, - ) -> Option> { + _il: &'a mut MutableLiftedILFunction, + ) -> Option> { None } @@ -1503,7 +1505,7 @@ impl Architecture for CoreArchitecture { &self, data: &[u8], addr: u64, - il: &mut Lifter, + il: &mut MutableLiftedILFunction, ) -> Option<(usize, bool)> { let mut size = data.len(); let success = unsafe { @@ -1527,9 +1529,9 @@ impl Architecture for CoreArchitecture { &self, _flag: Self::Flag, _flag_write: Self::FlagWrite, - _op: FlagWriteOp, - _il: &'a mut Lifter, - ) -> Option> { + _op: LowLevelILFlagWriteOp, + _il: &'a mut MutableLiftedILFunction, + ) -> Option> { None } @@ -1565,16 +1567,16 @@ impl Architecture for CoreArchitecture { &self, _cond: FlagCondition, _class: Option, - _il: &'a mut Lifter, - ) -> Option> { + _il: &'a mut MutableLiftedILFunction, + ) -> Option> { None } fn flag_group_llil<'a>( &self, _group: Self::FlagGroup, - _il: &'a mut Lifter, - ) -> Option> { + _il: &'a mut MutableLiftedILFunction, + ) -> Option> { None } @@ -2204,7 +2206,7 @@ where }; let data = unsafe { std::slice::from_raw_parts(data, *len) }; - let mut lifter = unsafe { Lifter::from_raw(custom_arch_handle, il) }; + let mut lifter = unsafe { MutableLiftedILFunction::from_raw(custom_arch_handle, il) }; match custom_arch.instruction_llil(data, addr, &mut lifter) { Some((res_len, res_value)) => { @@ -2594,10 +2596,10 @@ where let flag_write = custom_arch.flag_write_from_id(FlagWriteId(flag_write)); let flag = custom_arch.flag_from_id(FlagId(flag)); let operands = unsafe { std::slice::from_raw_parts(operands_raw, operand_count) }; - let mut lifter = unsafe { Lifter::from_raw(custom_arch_handle, il) }; + let mut lifter = unsafe { MutableLiftedILFunction::from_raw(custom_arch_handle, il) }; if let (Some(flag_write), Some(flag)) = (flag_write, flag) { - if let Some(op) = FlagWriteOp::from_op(custom_arch, size, op, operands) { + if let Some(op) = LowLevelILFlagWriteOp::from_op(custom_arch, size, op, operands) { if let Some(expr) = custom_arch.flag_write_llil(flag, flag_write, op, &mut lifter) { // TODO verify that returned expr is a bool value return expr.index.0; @@ -2646,7 +2648,7 @@ where let class = custom_arch.flag_class_from_id(FlagClassId(class)); - let mut lifter = unsafe { Lifter::from_raw(custom_arch_handle, il) }; + let mut lifter = unsafe { MutableLiftedILFunction::from_raw(custom_arch_handle, il) }; if let Some(expr) = custom_arch.flag_cond_llil(cond, class, &mut lifter) { // TODO verify that returned expr is a bool value return expr.index.0; @@ -2668,7 +2670,7 @@ where handle: ctxt as *mut A, }; - let mut lifter = unsafe { Lifter::from_raw(custom_arch_handle, il) }; + let mut lifter = unsafe { MutableLiftedILFunction::from_raw(custom_arch_handle, il) }; if let Some(group) = custom_arch.flag_group_from_id(FlagGroupId(group)) { if let Some(expr) = custom_arch.flag_group_llil(group, &mut lifter) { diff --git a/rust/src/basicblock.rs b/rust/src/basicblock.rs index 96406fd11..b880d28f5 100644 --- a/rust/src/basicblock.rs +++ b/rust/src/basicblock.rs @@ -12,13 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::fmt; - use crate::architecture::CoreArchitecture; use crate::function::Function; use crate::rc::*; use crate::BranchType; use binaryninjacore_sys::*; +use std::fmt; +use std::fmt::Debug; enum EdgeDirection { Incoming, @@ -90,6 +90,7 @@ unsafe impl<'a, C: 'a + BlockContext> CoreArrayProviderInner for Edge<'a, C> { pub trait BlockContext: Clone + Sync + Send + Sized { type Instruction; + type InstructionIndex: Debug + From; type Iter: Iterator; fn start(&self, block: &BasicBlock) -> Self::Instruction; @@ -130,14 +131,12 @@ impl BasicBlock { self.context.iter(self) } - // TODO: This needs to be generic over the IL index / mapped address. - pub fn raw_start(&self) -> u64 { - unsafe { BNGetBasicBlockStart(self.handle) } + pub fn start_index(&self) -> C::InstructionIndex { + C::InstructionIndex::from(unsafe { BNGetBasicBlockStart(self.handle) }) } - // TODO: This needs to be generic over the IL index / mapped address. - pub fn raw_end(&self) -> u64 { - unsafe { BNGetBasicBlockEnd(self.handle) } + pub fn end_index(&self) -> C::InstructionIndex { + C::InstructionIndex::from(unsafe { BNGetBasicBlockEnd(self.handle) }) } pub fn raw_length(&self) -> u64 { @@ -148,7 +147,6 @@ impl BasicBlock { unsafe { let mut count = 0; let edges = BNGetBasicBlockIncomingEdges(self.handle, &mut count); - Array::new( edges, count, @@ -164,7 +162,6 @@ impl BasicBlock { unsafe { let mut count = 0; let edges = BNGetBasicBlockOutgoingEdges(self.handle, &mut count); - Array::new( edges, count, @@ -176,7 +173,7 @@ impl BasicBlock { } } - // is this valid for il blocks? + // is this valid for il blocks? (it looks like up to MLIL it is) pub fn has_undetermined_outgoing_edges(&self) -> bool { unsafe { BNBasicBlockHasUndeterminedOutgoingEdges(self.handle) } } @@ -185,18 +182,18 @@ impl BasicBlock { unsafe { BNBasicBlockCanExit(self.handle) } } + // TODO: Should we new type this? I just cant tell where the consumers of this are. pub fn index(&self) -> usize { unsafe { BNGetBasicBlockIndex(self.handle) } } pub fn immediate_dominator(&self) -> Option> { unsafe { + // TODO: We don't allow the user to calculate post dominators let block = BNGetBasicBlockImmediateDominator(self.handle, false); - if block.is_null() { return None; } - Some(Ref::new(BasicBlock::from_raw(block, self.context.clone()))) } } @@ -204,8 +201,8 @@ impl BasicBlock { pub fn dominators(&self) -> Array> { unsafe { let mut count = 0; + // TODO: We don't allow the user to calculate post dominators let blocks = BNGetBasicBlockDominators(self.handle, &mut count, false); - Array::new(blocks, count, self.context.clone()) } } @@ -213,8 +210,8 @@ impl BasicBlock { pub fn strict_dominators(&self) -> Array> { unsafe { let mut count = 0; + // TODO: We don't allow the user to calculate post dominators let blocks = BNGetBasicBlockStrictDominators(self.handle, &mut count, false); - Array::new(blocks, count, self.context.clone()) } } @@ -222,8 +219,8 @@ impl BasicBlock { pub fn dominator_tree_children(&self) -> Array> { unsafe { let mut count = 0; + // TODO: We don't allow the user to calculate post dominators let blocks = BNGetBasicBlockDominatorTreeChildren(self.handle, &mut count, false); - Array::new(blocks, count, self.context.clone()) } } @@ -231,8 +228,8 @@ impl BasicBlock { pub fn dominance_frontier(&self) -> Array> { unsafe { let mut count = 0; + // TODO: We don't allow the user to calculate post dominators let blocks = BNGetBasicBlockDominanceFrontier(self.handle, &mut count, false); - Array::new(blocks, count, self.context.clone()) } } @@ -249,16 +246,23 @@ impl IntoIterator for &BasicBlock { } } +impl IntoIterator for BasicBlock { + type Item = C::Instruction; + type IntoIter = C::Iter; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + impl fmt::Debug for BasicBlock { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - " {}>", - self.handle, - &self.context, - self.raw_start(), - self.raw_end() - ) + f.debug_struct("BasicBlock") + .field("context", &self.context) + .field("start_index", &self.start_index()) + .field("end_index", &self.end_index()) + .field("raw_length", &self.raw_length()) + .finish() } } diff --git a/rust/src/binaryview.rs b/rust/src/binaryview.rs index 78cc62d25..99eddf784 100644 --- a/rust/src/binaryview.rs +++ b/rust/src/binaryview.rs @@ -1062,7 +1062,6 @@ pub trait BinaryViewExt: BinaryViewBase { unsafe { let mut count = 0; let blocks = BNGetBasicBlocksForAddress(self.as_ref().handle, addr, &mut count); - Array::new(blocks, count, NativeBlock::new()) } } @@ -1071,7 +1070,6 @@ pub trait BinaryViewExt: BinaryViewBase { unsafe { let mut count = 0; let blocks = BNGetBasicBlocksStartingAtAddress(self.as_ref().handle, addr, &mut count); - Array::new(blocks, count, NativeBlock::new()) } } diff --git a/rust/src/function.rs b/rust/src/function.rs index f7a54e6c5..655f4f005 100644 --- a/rust/src/function.rs +++ b/rust/src/function.rs @@ -22,7 +22,6 @@ use crate::{ component::Component, disassembly::{DisassemblySettings, DisassemblyTextLine}, flowgraph::FlowGraph, - llil, mlil::FunctionGraphType, platform::Platform, references::CodeReference, @@ -41,6 +40,7 @@ pub use binaryninjacore_sys::BNHighlightStandardColor as HighlightStandardColor; use crate::architecture::RegisterId; use crate::confidence::Conf; use crate::hlil::HighLevelILFunction; +use crate::lowlevelil::{LiftedILFunction, RegularLowLevelILFunction}; use crate::mlil::MediumLevelILFunction; use crate::variable::{ IndirectBranchInfo, MergedVariable, NamedVariableWithType, RegisterValue, RegisterValueType, @@ -141,18 +141,19 @@ impl NativeBlock { impl BlockContext for NativeBlock { type Instruction = u64; + type InstructionIndex = u64; type Iter = NativeBlockIter; fn start(&self, block: &BasicBlock) -> u64 { - block.raw_start() + block.start_index() } fn iter(&self, block: &BasicBlock) -> NativeBlockIter { NativeBlockIter { arch: block.arch(), bv: block.function().view(), - cur: block.raw_start(), - end: block.raw_end(), + cur: block.start_index(), + end: block.end_index(), } } } @@ -479,11 +480,14 @@ impl Function { } } - pub fn low_level_il(&self) -> Result>, ()> { + pub fn low_level_il(&self) -> Result>, ()> { unsafe { let llil_ptr = BNGetFunctionLowLevelIL(self.handle); match llil_ptr.is_null() { - false => Ok(llil::RegularFunction::ref_from_raw(self.arch(), llil_ptr)), + false => Ok(RegularLowLevelILFunction::ref_from_raw( + self.arch(), + llil_ptr, + )), true => Err(()), } } @@ -491,28 +495,30 @@ impl Function { pub fn low_level_il_if_available( &self, - ) -> Option>> { + ) -> Option>> { let llil_ptr = unsafe { BNGetFunctionLowLevelILIfAvailable(self.handle) }; match llil_ptr.is_null() { - false => Some(unsafe { llil::RegularFunction::ref_from_raw(self.arch(), llil_ptr) }), + false => { + Some(unsafe { RegularLowLevelILFunction::ref_from_raw(self.arch(), llil_ptr) }) + } true => None, } } - pub fn lifted_il(&self) -> Result>, ()> { + pub fn lifted_il(&self) -> Result>, ()> { unsafe { let llil_ptr = BNGetFunctionLiftedIL(self.handle); match llil_ptr.is_null() { - false => Ok(llil::LiftedFunction::ref_from_raw(self.arch(), llil_ptr)), + false => Ok(LiftedILFunction::ref_from_raw(self.arch(), llil_ptr)), true => Err(()), } } } - pub fn lifted_il_if_available(&self) -> Option>> { + pub fn lifted_il_if_available(&self) -> Option>> { let llil_ptr = unsafe { BNGetFunctionLiftedILIfAvailable(self.handle) }; match llil_ptr.is_null() { - false => Some(unsafe { llil::LiftedFunction::ref_from_raw(self.arch(), llil_ptr) }), + false => Some(unsafe { LiftedILFunction::ref_from_raw(self.arch(), llil_ptr) }), true => None, } } diff --git a/rust/src/functionrecognizer.rs b/rust/src/functionrecognizer.rs index f16b87cb5..5ab68e8f1 100644 --- a/rust/src/functionrecognizer.rs +++ b/rust/src/functionrecognizer.rs @@ -1,6 +1,7 @@ -use crate::llil::LowLevelILFunction; +use crate::lowlevelil::function::LowLevelILFunction; +use crate::lowlevelil::RegularLowLevelILFunction; use crate::mlil::MediumLevelILFunction; -use crate::{architecture::CoreArchitecture, binaryview::BinaryView, function::Function, llil}; +use crate::{architecture::CoreArchitecture, binaryview::BinaryView, function::Function}; use binaryninjacore_sys::*; use std::os::raw::c_void; @@ -9,7 +10,7 @@ pub trait FunctionRecognizer { &self, _bv: &BinaryView, _func: &Function, - _llil: &llil::RegularFunction, + _llil: &RegularLowLevelILFunction, ) -> bool { false } diff --git a/rust/src/hlil/block.rs b/rust/src/hlil/block.rs index 0bbedd182..8b5a000c0 100644 --- a/rust/src/hlil/block.rs +++ b/rust/src/hlil/block.rs @@ -1,15 +1,13 @@ use std::ops::Range; -use binaryninjacore_sys::BNGetHighLevelILIndexForInstruction; - use crate::basicblock::{BasicBlock, BlockContext}; use crate::rc::Ref; -use super::{HighLevelILFunction, HighLevelILInstruction}; +use super::{HighLevelILFunction, HighLevelILInstruction, HighLevelInstructionIndex}; pub struct HighLevelILBlockIter { function: Ref, - range: Range, + range: Range, } impl Iterator for HighLevelILBlockIter { @@ -18,10 +16,9 @@ impl Iterator for HighLevelILBlockIter { fn next(&mut self) -> Option { self.range .next() - .map(|i| unsafe { - BNGetHighLevelILIndexForInstruction(self.function.handle, i as usize) - }) - .map(|i| HighLevelILInstruction::new(self.function.to_owned(), i)) + .map(|i| HighLevelInstructionIndex(i)) + // TODO: Is this already MAPPED>!>?!? If so we map twice that is BAD!!!! + .and_then(|i| self.function.instruction_from_index(i)) } } @@ -31,25 +28,27 @@ pub struct HighLevelILBlock { impl core::fmt::Debug for HighLevelILBlock { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + // TODO: Actual basic block please write!(f, "mlil_bb {:?}", self.function) } } impl BlockContext for HighLevelILBlock { - type Iter = HighLevelILBlockIter; type Instruction = HighLevelILInstruction; + type InstructionIndex = HighLevelInstructionIndex; + type Iter = HighLevelILBlockIter; fn start(&self, block: &BasicBlock) -> HighLevelILInstruction { - let expr_idx = unsafe { - BNGetHighLevelILIndexForInstruction(self.function.handle, block.raw_start() as usize) - }; - HighLevelILInstruction::new(self.function.to_owned(), expr_idx) + // TODO: Is this start index already mappedd????? + self.function + .instruction_from_index(block.start_index()) + .unwrap() } fn iter(&self, block: &BasicBlock) -> HighLevelILBlockIter { HighLevelILBlockIter { function: self.function.to_owned(), - range: block.raw_start()..block.raw_end(), + range: block.start_index().0..block.end_index().0, } } } diff --git a/rust/src/hlil/function.rs b/rust/src/hlil/function.rs index b2f7c9885..f6508be69 100644 --- a/rust/src/hlil/function.rs +++ b/rust/src/hlil/function.rs @@ -3,10 +3,9 @@ use std::hash::{Hash, Hasher}; use binaryninjacore_sys::*; -use super::{HighLevelILBlock, HighLevelILInstruction, HighLevelILLiftedInstruction}; -use crate::architecture::CoreArchitecture; +use super::{HighLevelILBlock, HighLevelILInstruction, HighLevelInstructionIndex}; use crate::basicblock::BasicBlock; -use crate::function::Function; +use crate::function::{Function, Location}; use crate::rc::{Array, Ref, RefCountable}; use crate::variable::{SSAVariable, Variable}; @@ -24,45 +23,47 @@ impl HighLevelILFunction { Self { handle, full_ast }.to_owned() } - pub fn instruction_from_idx(&self, expr_idx: usize) -> HighLevelILInstruction { - HighLevelILInstruction::new(self.to_owned(), expr_idx) - } - - pub fn lifted_instruction_from_idx(&self, expr_idx: usize) -> HighLevelILLiftedInstruction { - self.instruction_from_idx(expr_idx).lift() + pub fn instruction_from_index( + &self, + index: HighLevelInstructionIndex, + ) -> Option { + let mapped_index = unsafe { BNGetHighLevelILIndexForInstruction(self.handle, index.0) }; + self.instruction_from_mapped_index(HighLevelInstructionIndex(mapped_index)) } - pub fn instruction_from_instruction_idx(&self, instr_idx: usize) -> HighLevelILInstruction { - HighLevelILInstruction::new(self.as_non_ast(), unsafe { - BNGetHighLevelILIndexForInstruction(self.handle, instr_idx) - }) + // TODO: instruction_from_index should be it right? we always want to get the mapped index right? + // TODO: This needs testing... + pub fn instruction_from_mapped_index( + &self, + mapped_index: HighLevelInstructionIndex, + ) -> Option { + if mapped_index.0 >= self.instruction_count() { + None + } else { + Some(HighLevelILInstruction::new(self.to_owned(), mapped_index)) + } } - pub fn lifted_instruction_from_instruction_idx( - &self, - instr_idx: usize, - ) -> HighLevelILLiftedInstruction { - self.instruction_from_instruction_idx(instr_idx).lift() + pub fn root_instruction_index(&self) -> HighLevelInstructionIndex { + HighLevelInstructionIndex(unsafe { BNGetHighLevelILRootExpr(self.handle) }) } pub fn root(&self) -> HighLevelILInstruction { - HighLevelILInstruction::new(self.as_ast(), unsafe { - BNGetHighLevelILRootExpr(self.handle) - }) + HighLevelILInstruction::new(self.as_ast(), self.root_instruction_index()) } pub fn set_root(&self, new_root: &HighLevelILInstruction) { - unsafe { BNSetHighLevelILRootExpr(self.handle, new_root.index) } - } - - pub fn lifted_root(&self) -> HighLevelILLiftedInstruction { - self.root().lift() + unsafe { BNSetHighLevelILRootExpr(self.handle, new_root.index.0) } } pub fn instruction_count(&self) -> usize { unsafe { BNGetHighLevelILInstructionCount(self.handle) } } + pub fn expression_count(&self) -> usize { + unsafe { BNGetHighLevelILExprCount(self.handle) } + } + pub fn ssa_form(&self) -> HighLevelILFunction { let ssa = unsafe { BNGetHighLevelILSSAForm(self.handle) }; assert!(!ssa.is_null()); @@ -85,7 +86,6 @@ impl HighLevelILFunction { let context = HighLevelILBlock { function: self.to_owned(), }; - unsafe { Array::new(blocks, count, context) } } @@ -105,13 +105,20 @@ impl HighLevelILFunction { .to_owned() } - pub fn current_address(&self) -> u64 { - unsafe { BNHighLevelILGetCurrentAddress(self.handle) } + // TODO: Rename to `current_location`? + pub fn current_address(&self) -> Location { + let addr = unsafe { BNHighLevelILGetCurrentAddress(self.handle) }; + Location::from(addr) } - pub fn set_current_address(&self, address: u64, arch: Option) { - let arch = arch.unwrap_or_else(|| self.function().arch()); - unsafe { BNHighLevelILSetCurrentAddress(self.handle, arch.handle, address) } + // TODO: Rename to `set_current_location`? + pub fn set_current_address(&self, location: impl Into) { + let location = location.into(); + let arch = location + .arch + .map(|a| a.handle) + .unwrap_or_else(std::ptr::null_mut); + unsafe { BNHighLevelILSetCurrentAddress(self.handle, arch, location.addr) } } /// Gets the instruction that contains the given SSA variable's definition. @@ -126,14 +133,14 @@ impl HighLevelILFunction { variable.version, ) }; - (index < self.instruction_count()) - .then(|| HighLevelILInstruction::new(self.to_owned(), index)) + // TODO: Is this mapped index? + self.instruction_from_mapped_index(HighLevelInstructionIndex(index)) } pub fn ssa_memory_definition(&self, version: usize) -> Option { let index = unsafe { BNGetHighLevelILSSAMemoryDefinition(self.handle, version) }; - (index < self.instruction_count()) - .then(|| HighLevelILInstruction::new(self.to_owned(), index)) + // TODO: Is this mapped index? + self.instruction_from_mapped_index(HighLevelInstructionIndex(index)) } /// Gets all the instructions that use the given SSA variable. @@ -176,7 +183,7 @@ impl HighLevelILFunction { self.handle, &variable.variable.into(), variable.version, - instr.index, + instr.index.0, ) } } @@ -200,7 +207,7 @@ impl HighLevelILFunction { /// Determines if `variable` is live at a given point in the function pub fn is_variable_live_at(&self, variable: Variable, instr: &HighLevelILInstruction) -> bool { - unsafe { BNIsHighLevelILVarLiveAt(self.handle, &variable.into(), instr.index) } + unsafe { BNIsHighLevelILVarLiveAt(self.handle, &variable.into(), instr.index.0) } } /// This gets just the HLIL variables - you may be interested in the union @@ -238,6 +245,9 @@ impl Debug for HighLevelILFunction { f.debug_struct("HighLevelILFunction") .field("arch", &self.function().arch()) .field("instruction_count", &self.instruction_count()) + .field("expression_count", &self.expression_count()) + .field("root", &self.root()) + .field("root", &self.root()) .finish() } } diff --git a/rust/src/hlil/instruction.rs b/rust/src/hlil/instruction.rs index 4ae493865..b06ad6862 100644 --- a/rust/src/hlil/instruction.rs +++ b/rust/src/hlil/instruction.rs @@ -1,4 +1,6 @@ use binaryninjacore_sys::*; +use std::fmt; +use std::fmt::{Debug, Display, Formatter}; use super::operation::*; use super::{HighLevelILFunction, HighLevelILLiftedInstruction, HighLevelILLiftedInstructionKind}; @@ -10,11 +12,38 @@ use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref}; use crate::types::Type; use crate::variable::{ConstantData, RegisterValue, SSAVariable, Variable}; +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct HighLevelInstructionIndex(pub usize); + +impl HighLevelInstructionIndex { + pub fn next(&self) -> Self { + Self(self.0 + 1) + } +} + +impl From for HighLevelInstructionIndex { + fn from(index: usize) -> Self { + Self(index) + } +} + +impl From for HighLevelInstructionIndex { + fn from(index: u64) -> Self { + Self(index as usize) + } +} + +impl Display for HighLevelInstructionIndex { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.write_fmt(format_args!("{}", self.0)) + } +} + #[derive(Clone)] pub struct HighLevelILInstruction { pub function: Ref, pub address: u64, - pub index: usize, + pub index: HighLevelInstructionIndex, pub size: usize, pub kind: HighLevelILInstructionKind, } @@ -145,8 +174,11 @@ pub enum HighLevelILInstructionKind { DoWhileSsa(WhileSsa), } impl HighLevelILInstruction { - pub(crate) fn new(function: Ref, index: usize) -> Self { - let op = unsafe { BNGetHighLevelILByIndex(function.handle, index, function.full_ast) }; + pub(crate) fn new( + function: Ref, + index: HighLevelInstructionIndex, + ) -> Self { + let op = unsafe { BNGetHighLevelILByIndex(function.handle, index.0, function.full_ast) }; use BNHighLevelILOperation::*; use HighLevelILInstructionKind as Op; let kind = match op.operation { @@ -903,7 +935,7 @@ impl HighLevelILInstruction { let lines = unsafe { BNGetHighLevelILExprText( self.function.handle, - self.index, + self.index.0, self.function.full_ast, &mut count, core::ptr::null_mut(), @@ -914,7 +946,7 @@ impl HighLevelILInstruction { /// Type of expression pub fn expr_type(&self) -> Option>> { - let result = unsafe { BNGetHighLevelILExprType(self.function.handle, self.index) }; + let result = unsafe { BNGetHighLevelILExprType(self.function.handle, self.index.0) }; (!result.type_.is_null()).then(|| { Conf::new( unsafe { Type::ref_from_raw(result.type_) }, @@ -925,7 +957,9 @@ impl HighLevelILInstruction { /// Version of active memory contents in SSA form for this instruction pub fn ssa_memory_version(&self) -> usize { - unsafe { BNGetHighLevelILSSAMemoryVersionAtILInstruction(self.function.handle, self.index) } + unsafe { + BNGetHighLevelILSSAMemoryVersionAtILInstruction(self.function.handle, self.index.0) + } } pub fn ssa_variable_version(&self, variable: Variable) -> SSAVariable { @@ -933,14 +967,22 @@ impl HighLevelILInstruction { BNGetHighLevelILSSAVarVersionAtILInstruction( self.function.handle, &variable.into(), - self.index, + self.index.0, ) }; SSAVariable::new(variable, version) } fn lift_operand(&self, expr_idx: usize) -> Box { - Box::new(self.function.lifted_instruction_from_idx(expr_idx)) + // TODO: UGH, if your gonna call it expr_idx, call the instruction and expression!!!!! + // TODO: We dont even need to say instruction in the type! + // TODO: IF you want to have an instruction type, there needs to be a seperate expression type + // TODO: See the lowlevelil module. + let expr_idx_is_really_instr_idx = HighLevelInstructionIndex(expr_idx); + let operand_instr = self + .function + .instruction_from_mapped_index(expr_idx_is_really_instr_idx); + Box::new(operand_instr.unwrap().lift()) } fn lift_binary_op(&self, op: BinaryOp) -> LiftedBinaryOp { @@ -1030,12 +1072,13 @@ unsafe impl CoreArrayProviderInner for HighLevelILInstruction { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { - Self::new(context.clone(), *raw) + Self::new(context.clone(), HighLevelInstructionIndex(*raw)) } } -impl core::fmt::Debug for HighLevelILInstruction { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { +impl Debug for HighLevelILInstruction { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + // TODO: Actual debug impl please! write!( f, "<{} at 0x{:08}>", diff --git a/rust/src/hlil/lift.rs b/rust/src/hlil/lift.rs index fdfebc0be..f48650dfc 100644 --- a/rust/src/hlil/lift.rs +++ b/rust/src/hlil/lift.rs @@ -1,5 +1,5 @@ use super::operation::*; -use super::HighLevelILFunction; +use super::{HighLevelILFunction, HighLevelInstructionIndex}; use crate::architecture::CoreIntrinsic; use crate::rc::Ref; @@ -21,11 +21,15 @@ pub enum HighLevelILLiftedOperand { VarSsaList(Vec), } +// TODO: UGH, if your gonna call it expr_idx, call the instruction and expression!!!!! +// TODO: We dont even need to say instruction in the type! +// TODO: IF you want to have an instruction type, there needs to be a seperate expression type +// TODO: See the lowlevelil module. #[derive(Clone, Debug, PartialEq)] pub struct HighLevelILLiftedInstruction { pub function: Ref, pub address: u64, - pub index: usize, + pub index: HighLevelInstructionIndex, pub size: usize, pub kind: HighLevelILLiftedInstructionKind, } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 200026998..8df7c45fc 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -71,8 +71,8 @@ pub mod headless; pub mod hlil; pub mod interaction; pub mod linearview; -pub mod llil; pub mod logger; +pub mod lowlevelil; pub mod mainthread; pub mod metadata; pub mod mlil; @@ -120,22 +120,6 @@ pub use binaryninjacore_sys::BNILBranchDependence as ILBranchDependence; pub const BN_FULL_CONFIDENCE: u8 = u8::MAX; pub const BN_INVALID_EXPR: usize = usize::MAX; -unsafe extern "C" fn cb_progress_func bool>( - ctxt: *mut c_void, - progress: usize, - total: usize, -) -> bool { - if ctxt.is_null() { - return true; - } - let closure = &mut *(ctxt as *mut F); - closure(progress, total) -} - -unsafe extern "C" fn cb_progress_nop(_ctxt: *mut c_void, _arg1: usize, _arg2: usize) -> bool { - true -} - /// The main way to open and load files into Binary Ninja. Make sure you've properly initialized the core before calling this function. See [`crate::headless::init()`] pub fn load(file_path: impl AsRef) -> Option> { let file_path = file_path.as_ref().into_bytes_with_nul(); @@ -651,6 +635,22 @@ pub fn add_optional_plugin_dependency(name: S) { }; } +unsafe extern "C" fn cb_progress_func bool>( + ctxt: *mut c_void, + progress: usize, + total: usize, +) -> bool { + if ctxt.is_null() { + return true; + } + let closure = &mut *(ctxt as *mut F); + closure(progress, total) +} + +unsafe extern "C" fn cb_progress_nop(_ctxt: *mut c_void, _arg1: usize, _arg2: usize) -> bool { + true +} + // Provide ABI version automatically so that the core can verify binary compatibility #[cfg(not(feature = "no_exports"))] #[no_mangle] diff --git a/rust/src/llil/mod.rs b/rust/src/llil/mod.rs deleted file mode 100644 index 08947bf59..000000000 --- a/rust/src/llil/mod.rs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2021-2024 Vector 35 Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use std::fmt; - -// TODO : provide some way to forbid emitting register reads for certain registers -// also writing for certain registers (e.g. zero register must prohibit il.set_reg and il.reg -// (replace with nop or const(0) respectively) -// requirements on load/store memory address sizes? -// can reg/set_reg be used with sizes that differ from what is in BNRegisterInfo? - -use crate::architecture::Register as ArchReg; -use crate::architecture::{Architecture, RegisterId}; -use crate::function::Location; - -mod block; -mod expression; -mod function; -mod instruction; -mod lifting; -pub mod operation; - -pub use self::expression::*; -pub use self::function::*; -pub use self::instruction::*; -pub use self::lifting::get_default_flag_cond_llil; -pub use self::lifting::get_default_flag_write_llil; -pub use self::lifting::{ - ExpressionBuilder, FlagWriteOp, Label, Liftable, LiftableWithSize, RegisterOrConstant, -}; - -pub use self::block::LowLevelILBlock as LowLevelBlock; -pub use self::block::LowLevelILBlockIter as LowLevelBlockIter; - -pub type Lifter = LowLevelILFunction>; -pub type LiftedFunction = LowLevelILFunction>; -pub type LiftedExpr<'a, Arch> = Expression<'a, Arch, Mutable, NonSSA, ValueExpr>; -pub type RegularFunction = LowLevelILFunction>; -pub type SSAFunction = LowLevelILFunction; - -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum Register { - ArchReg(R), - // TODO: Might want to be changed to TempRegisterId. - // TODO: If we do that then we would need to get rid of `Register::id()` - Temp(u32), -} - -impl Register { - fn id(&self) -> RegisterId { - match *self { - Register::ArchReg(ref r) => r.id(), - Register::Temp(id) => RegisterId(0x8000_0000 | id), - } - } -} - -impl fmt::Debug for Register { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Register::ArchReg(ref r) => write!(f, "{}", r.name().as_ref()), - Register::Temp(id) => write!(f, "temp{}", id), - } - } -} - -#[derive(Copy, Clone, Debug)] -pub enum SSARegister { - Full(Register, u32), // no such thing as partial access to a temp register, I think - Partial(R, u32, R), // partial accesses only possible for arch registers, I think -} - -impl SSARegister { - pub fn version(&self) -> u32 { - match *self { - SSARegister::Full(_, ver) | SSARegister::Partial(_, ver, _) => ver, - } - } -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub enum VisitorAction { - Descend, - Sibling, - Halt, -} diff --git a/rust/src/lowlevelil.rs b/rust/src/lowlevelil.rs new file mode 100644 index 000000000..5df7252b9 --- /dev/null +++ b/rust/src/lowlevelil.rs @@ -0,0 +1,99 @@ +// Copyright 2021-2024 Vector 35 Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::fmt; + +// TODO : provide some way to forbid emitting register reads for certain registers +// also writing for certain registers (e.g. zero register must prohibit il.set_reg and il.reg +// (replace with nop or const(0) respectively) +// requirements on load/store memory address sizes? +// can reg/set_reg be used with sizes that differ from what is in BNRegisterInfo? + +use crate::architecture::Register as ArchReg; +use crate::architecture::{Architecture, RegisterId}; +use crate::function::Location; + +mod block; +pub mod expression; +pub mod function; +pub mod instruction; +pub mod lifting; +pub mod operation; + +use self::expression::*; +use self::function::*; +use self::instruction::*; + +pub type MutableLiftedILFunction = LowLevelILFunction>; +pub type LiftedILFunction = LowLevelILFunction>; +pub type MutableLiftedILExpr<'a, Arch, ReturnType> = + LowLevelILExpression<'a, Arch, Mutable, NonSSA, ReturnType>; +pub type RegularLowLevelILFunction = + LowLevelILFunction>; +pub type RegularLowLevelILInstruction<'a, Arch> = + LowLevelILInstruction<'a, Arch, Finalized, NonSSA>; +pub type RegularLowLevelILInstructionKind<'a, Arch> = + LowLevelILInstructionKind<'a, Arch, Finalized, NonSSA>; +pub type RegularLowLevelILExpression<'a, Arch, ReturnType> = + LowLevelILExpression<'a, Arch, Finalized, NonSSA, ReturnType>; +pub type RegularLowLevelILExpressionKind<'a, Arch> = + LowLevelILExpressionKind<'a, Arch, Finalized, NonSSA>; +pub type LowLevelILSSAFunction = LowLevelILFunction; + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum LowLevelILRegister { + ArchReg(R), + // TODO: Might want to be changed to TempRegisterId. + // TODO: If we do that then we would need to get rid of `Register::id()` + Temp(u32), +} + +impl LowLevelILRegister { + fn id(&self) -> RegisterId { + match *self { + LowLevelILRegister::ArchReg(ref r) => r.id(), + LowLevelILRegister::Temp(id) => RegisterId(0x8000_0000 | id), + } + } +} + +impl fmt::Debug for LowLevelILRegister { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + LowLevelILRegister::ArchReg(ref r) => write!(f, "{}", r.name().as_ref()), + LowLevelILRegister::Temp(id) => write!(f, "temp{}", id), + } + } +} + +#[derive(Copy, Clone, Debug)] +pub enum LowLevelILSSARegister { + Full(LowLevelILRegister, u32), // no such thing as partial access to a temp register, I think + Partial(R, u32, R), // partial accesses only possible for arch registers, I think +} + +impl LowLevelILSSARegister { + pub fn version(&self) -> u32 { + match *self { + LowLevelILSSARegister::Full(_, ver) | LowLevelILSSARegister::Partial(_, ver, _) => ver, + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub enum VisitorAction { + Descend, + Sibling, + Halt, +} diff --git a/rust/src/llil/block.rs b/rust/src/lowlevelil/block.rs similarity index 81% rename from rust/src/llil/block.rs rename to rust/src/lowlevelil/block.rs index da3215785..0dc6eccb8 100644 --- a/rust/src/llil/block.rs +++ b/rust/src/lowlevelil/block.rs @@ -37,12 +37,13 @@ where M: FunctionMutability, F: FunctionForm, { - type Item = Instruction<'func, A, M, F>; + type Item = LowLevelILInstruction<'func, A, M, F>; fn next(&mut self) -> Option { self.range .next() - .map(|i| Instruction::new(self.function, InstructionIndex(i))) + .map(|idx| LowLevelInstructionIndex(idx)) + .and_then(|idx| self.function.instruction_from_index(idx)) } } @@ -62,19 +63,20 @@ where M: FunctionMutability, F: FunctionForm, { - type Instruction = Instruction<'func, A, M, F>; + type Instruction = LowLevelILInstruction<'func, A, M, F>; + type InstructionIndex = LowLevelInstructionIndex; type Iter = LowLevelILBlockIter<'func, A, M, F>; - fn start(&self, block: &BasicBlock) -> Instruction<'func, A, M, F> { - // TODO: block.raw_start should really return InstructionIndex... - Instruction::new(self.function, InstructionIndex(block.raw_start() as usize)) + fn start(&self, block: &BasicBlock) -> LowLevelILInstruction<'func, A, M, F> { + self.function + .instruction_from_index(block.start_index()) + .unwrap() } fn iter(&self, block: &BasicBlock) -> LowLevelILBlockIter<'func, A, M, F> { - // TODO: block.raw_start should really return InstructionIndex... LowLevelILBlockIter { function: self.function, - range: (block.raw_start() as usize)..(block.raw_end() as usize), + range: (block.start_index().0)..(block.end_index().0), } } } diff --git a/rust/src/llil/expression.rs b/rust/src/lowlevelil/expression.rs similarity index 68% rename from rust/src/llil/expression.rs rename to rust/src/lowlevelil/expression.rs index 6fab75057..33c3eba44 100644 --- a/rust/src/llil/expression.rs +++ b/rust/src/lowlevelil/expression.rs @@ -24,11 +24,11 @@ use std::fmt; use std::fmt::{Display, Formatter}; use std::marker::PhantomData; -/// Used as a marker for an [`Expression`] that **can** produce a value. +/// Used as a marker for an [`LowLevelILExpression`] that **can** produce a value. #[derive(Copy, Clone, Debug)] pub struct ValueExpr; -/// Used as a marker for an [`Expression`] that can **not** produce a value. +/// Used as a marker for an [`LowLevelILExpression`] that can **not** produce a value. #[derive(Copy, Clone, Debug)] pub struct VoidExpr; @@ -37,28 +37,29 @@ impl ExpressionResultType for ValueExpr {} impl ExpressionResultType for VoidExpr {} #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct ExpressionIndex(pub usize); +pub struct LowLevelILExpressionIndex(pub usize); -impl Display for ExpressionIndex { +impl Display for LowLevelILExpressionIndex { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.write_fmt(format_args!("{}", self.0)) } } +// TODO: Probably want to rename this with a LowLevelIL prefix to avoid collisions when we add handlers for other ILs pub trait ExpressionHandler<'func, A, M, F> where A: Architecture, M: FunctionMutability, F: FunctionForm, { - fn info(&self) -> ExprInfo<'func, A, M, F>; + fn kind(&self) -> LowLevelILExpressionKind<'func, A, M, F>; fn visit_tree(&self, f: &mut T) -> VisitorAction where - T: FnMut(&Expression<'func, A, M, F, ValueExpr>) -> VisitorAction; + T: FnMut(&LowLevelILExpression<'func, A, M, F, ValueExpr>) -> VisitorAction; } -pub struct Expression<'func, A, M, F, R> +pub struct LowLevelILExpression<'func, A, M, F, R> where A: 'func + Architecture, M: FunctionMutability, @@ -66,13 +67,13 @@ where R: ExpressionResultType, { pub(crate) function: &'func LowLevelILFunction, - pub index: ExpressionIndex, + pub index: LowLevelILExpressionIndex, // tag the 'return' type of this expression pub(crate) _ty: PhantomData, } -impl<'func, A, M, F, R> Expression<'func, A, M, F, R> +impl<'func, A, M, F, R> LowLevelILExpression<'func, A, M, F, R> where A: 'func + Architecture, M: FunctionMutability, @@ -81,7 +82,7 @@ where { pub(crate) fn new( function: &'func LowLevelILFunction, - index: ExpressionIndex, + index: LowLevelILExpressionIndex, ) -> Self { // TODO: Validate expression here? Self { @@ -92,7 +93,7 @@ where } } -impl<'func, A, M, F, R> fmt::Debug for Expression<'func, A, M, F, R> +impl<'func, A, M, F, R> fmt::Debug for LowLevelILExpression<'func, A, M, F, R> where A: 'func + Architecture, M: FunctionMutability, @@ -106,12 +107,13 @@ where } } -impl<'func, A, M> ExpressionHandler<'func, A, M, SSA> for Expression<'func, A, M, SSA, ValueExpr> +impl<'func, A, M> ExpressionHandler<'func, A, M, SSA> + for LowLevelILExpression<'func, A, M, SSA, ValueExpr> where A: 'func + Architecture, M: FunctionMutability, { - fn info(&self) -> ExprInfo<'func, A, M, SSA> { + fn kind(&self) -> LowLevelILExpressionKind<'func, A, M, SSA> { #[allow(unused_imports)] use binaryninjacore_sys::BNLowLevelILOperation::*; let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, self.index.0) }; @@ -119,16 +121,16 @@ where match op.operation { // Any invalid ops for SSA will be checked here. // SAFETY: We have checked for illegal operations. - _ => unsafe { ExprInfo::from_raw(self.function, op) }, + _ => unsafe { LowLevelILExpressionKind::from_raw(self.function, op) }, } } fn visit_tree(&self, f: &mut T) -> VisitorAction where - T: FnMut(&Expression<'func, A, M, SSA, ValueExpr>) -> VisitorAction, + T: FnMut(&LowLevelILExpression<'func, A, M, SSA, ValueExpr>) -> VisitorAction, { // First visit the current expression. - let info = self.info(); + let info = self.kind(); match f(self) { VisitorAction::Descend => {} action => return action, @@ -139,12 +141,12 @@ where } impl<'func, A, M> ExpressionHandler<'func, A, M, NonSSA> - for Expression<'func, A, M, NonSSA, ValueExpr> + for LowLevelILExpression<'func, A, M, NonSSA, ValueExpr> where A: 'func + Architecture, M: FunctionMutability, { - fn info(&self) -> ExprInfo<'func, A, M, NonSSA> { + fn kind(&self) -> LowLevelILExpressionKind<'func, A, M, NonSSA> { #[allow(unused_imports)] use binaryninjacore_sys::BNLowLevelILOperation::*; let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, self.index.0) }; @@ -152,16 +154,18 @@ where match op.operation { // Any invalid ops for Lifted IL will be checked here. // SAFETY: We have checked for illegal operations. - _ => unsafe { ExprInfo::from_raw(self.function, op) }, + _ => unsafe { LowLevelILExpressionKind::from_raw(self.function, op) }, } } fn visit_tree(&self, f: &mut T) -> VisitorAction where - T: FnMut(&Expression<'func, A, M, NonSSA, ValueExpr>) -> VisitorAction, + T: FnMut( + &LowLevelILExpression<'func, A, M, NonSSA, ValueExpr>, + ) -> VisitorAction, { // First visit the current expression. - let info = self.info(); + let info = self.kind(); match f(self) { VisitorAction::Descend => {} action => return action, @@ -172,12 +176,12 @@ where } impl<'func, A, M> ExpressionHandler<'func, A, M, NonSSA> - for Expression<'func, A, M, NonSSA, ValueExpr> + for LowLevelILExpression<'func, A, M, NonSSA, ValueExpr> where A: 'func + Architecture, M: FunctionMutability, { - fn info(&self) -> ExprInfo<'func, A, M, NonSSA> { + fn kind(&self) -> LowLevelILExpressionKind<'func, A, M, NonSSA> { use binaryninjacore_sys::BNLowLevelILOperation::*; let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, self.index.0) }; match op.operation { @@ -185,16 +189,18 @@ where LLIL_FLAG_COND => unreachable!("LLIL_FLAG_COND is only valid in Lifted IL"), LLIL_FLAG_GROUP => unreachable!("LLIL_FLAG_GROUP is only valid in Lifted IL"), // SAFETY: We have checked for illegal operations. - _ => unsafe { ExprInfo::from_raw(self.function, op) }, + _ => unsafe { LowLevelILExpressionKind::from_raw(self.function, op) }, } } fn visit_tree(&self, f: &mut T) -> VisitorAction where - T: FnMut(&Expression<'func, A, M, NonSSA, ValueExpr>) -> VisitorAction, + T: FnMut( + &LowLevelILExpression<'func, A, M, NonSSA, ValueExpr>, + ) -> VisitorAction, { // First visit the current expression. - let info = self.info(); + let info = self.kind(); match f(self) { VisitorAction::Descend => {} action => return action, @@ -204,7 +210,7 @@ where } } -impl<'func, A, F> Expression<'func, A, Finalized, F, ValueExpr> +impl<'func, A, F> LowLevelILExpression<'func, A, Finalized, F, ValueExpr> where A: 'func + Architecture, F: FunctionForm, @@ -213,7 +219,7 @@ where } #[derive(Debug)] -pub enum ExprInfo<'func, A, M, F> +pub enum LowLevelILExpressionKind<'func, A, M, F> where A: 'func + Architecture, M: FunctionMutability, @@ -316,7 +322,7 @@ where Undef(Operation<'func, A, M, F, operation::NoArgs>), } -impl<'func, A, M, F> ExprInfo<'func, A, M, F> +impl<'func, A, M, F> LowLevelILExpressionKind<'func, A, M, F> where A: 'func + Architecture, M: FunctionMutability, @@ -330,96 +336,104 @@ where use binaryninjacore_sys::BNLowLevelILOperation::*; match op.operation { - LLIL_LOAD | LLIL_LOAD_SSA => ExprInfo::Load(Operation::new(function, op)), - LLIL_POP => ExprInfo::Pop(Operation::new(function, op)), + LLIL_LOAD | LLIL_LOAD_SSA => { + LowLevelILExpressionKind::Load(Operation::new(function, op)) + } + LLIL_POP => LowLevelILExpressionKind::Pop(Operation::new(function, op)), LLIL_REG | LLIL_REG_SSA | LLIL_REG_SSA_PARTIAL => { - ExprInfo::Reg(Operation::new(function, op)) + LowLevelILExpressionKind::Reg(Operation::new(function, op)) + } + LLIL_REG_SPLIT | LLIL_REG_SPLIT_SSA => { + LowLevelILExpressionKind::RegSplit(Operation::new(function, op)) + } + LLIL_CONST => LowLevelILExpressionKind::Const(Operation::new(function, op)), + LLIL_CONST_PTR => LowLevelILExpressionKind::ConstPtr(Operation::new(function, op)), + LLIL_FLAG | LLIL_FLAG_SSA => { + LowLevelILExpressionKind::Flag(Operation::new(function, op)) + } + LLIL_FLAG_BIT | LLIL_FLAG_BIT_SSA => { + LowLevelILExpressionKind::FlagBit(Operation::new(function, op)) } - LLIL_REG_SPLIT | LLIL_REG_SPLIT_SSA => ExprInfo::RegSplit(Operation::new(function, op)), - LLIL_CONST => ExprInfo::Const(Operation::new(function, op)), - LLIL_CONST_PTR => ExprInfo::ConstPtr(Operation::new(function, op)), - LLIL_FLAG | LLIL_FLAG_SSA => ExprInfo::Flag(Operation::new(function, op)), - LLIL_FLAG_BIT | LLIL_FLAG_BIT_SSA => ExprInfo::FlagBit(Operation::new(function, op)), - LLIL_EXTERN_PTR => ExprInfo::ExternPtr(Operation::new(function, op)), - - LLIL_ADD => ExprInfo::Add(Operation::new(function, op)), - LLIL_ADC => ExprInfo::Adc(Operation::new(function, op)), - LLIL_SUB => ExprInfo::Sub(Operation::new(function, op)), - LLIL_SBB => ExprInfo::Sbb(Operation::new(function, op)), - LLIL_AND => ExprInfo::And(Operation::new(function, op)), - LLIL_OR => ExprInfo::Or(Operation::new(function, op)), - LLIL_XOR => ExprInfo::Xor(Operation::new(function, op)), - LLIL_LSL => ExprInfo::Lsl(Operation::new(function, op)), - LLIL_LSR => ExprInfo::Lsr(Operation::new(function, op)), - LLIL_ASR => ExprInfo::Asr(Operation::new(function, op)), - LLIL_ROL => ExprInfo::Rol(Operation::new(function, op)), - LLIL_RLC => ExprInfo::Rlc(Operation::new(function, op)), - LLIL_ROR => ExprInfo::Ror(Operation::new(function, op)), - LLIL_RRC => ExprInfo::Rrc(Operation::new(function, op)), - LLIL_MUL => ExprInfo::Mul(Operation::new(function, op)), - - LLIL_MULU_DP => ExprInfo::MuluDp(Operation::new(function, op)), - LLIL_MULS_DP => ExprInfo::MulsDp(Operation::new(function, op)), - - LLIL_DIVU => ExprInfo::Divu(Operation::new(function, op)), - LLIL_DIVS => ExprInfo::Divs(Operation::new(function, op)), - - LLIL_DIVU_DP => ExprInfo::DivuDp(Operation::new(function, op)), - LLIL_DIVS_DP => ExprInfo::DivsDp(Operation::new(function, op)), - - LLIL_MODU => ExprInfo::Modu(Operation::new(function, op)), - LLIL_MODS => ExprInfo::Mods(Operation::new(function, op)), - - LLIL_MODU_DP => ExprInfo::ModuDp(Operation::new(function, op)), - LLIL_MODS_DP => ExprInfo::ModsDp(Operation::new(function, op)), - - LLIL_NEG => ExprInfo::Neg(Operation::new(function, op)), - LLIL_NOT => ExprInfo::Not(Operation::new(function, op)), - - LLIL_SX => ExprInfo::Sx(Operation::new(function, op)), - LLIL_ZX => ExprInfo::Zx(Operation::new(function, op)), - LLIL_LOW_PART => ExprInfo::LowPart(Operation::new(function, op)), - - LLIL_CMP_E => ExprInfo::CmpE(Operation::new(function, op)), - LLIL_CMP_NE => ExprInfo::CmpNe(Operation::new(function, op)), - LLIL_CMP_SLT => ExprInfo::CmpSlt(Operation::new(function, op)), - LLIL_CMP_ULT => ExprInfo::CmpUlt(Operation::new(function, op)), - LLIL_CMP_SLE => ExprInfo::CmpSle(Operation::new(function, op)), - LLIL_CMP_ULE => ExprInfo::CmpUle(Operation::new(function, op)), - LLIL_CMP_SGE => ExprInfo::CmpSge(Operation::new(function, op)), - LLIL_CMP_UGE => ExprInfo::CmpUge(Operation::new(function, op)), - LLIL_CMP_SGT => ExprInfo::CmpSgt(Operation::new(function, op)), - LLIL_CMP_UGT => ExprInfo::CmpUgt(Operation::new(function, op)), - - LLIL_BOOL_TO_INT => ExprInfo::BoolToInt(Operation::new(function, op)), - - LLIL_FADD => ExprInfo::Fadd(Operation::new(function, op)), - LLIL_FSUB => ExprInfo::Fsub(Operation::new(function, op)), - LLIL_FMUL => ExprInfo::Fmul(Operation::new(function, op)), - LLIL_FDIV => ExprInfo::Fdiv(Operation::new(function, op)), - - LLIL_FSQRT => ExprInfo::Fsqrt(Operation::new(function, op)), - LLIL_FNEG => ExprInfo::Fneg(Operation::new(function, op)), - LLIL_FABS => ExprInfo::Fabs(Operation::new(function, op)), - LLIL_FLOAT_TO_INT => ExprInfo::FloatToInt(Operation::new(function, op)), - LLIL_INT_TO_FLOAT => ExprInfo::IntToFloat(Operation::new(function, op)), - LLIL_FLOAT_CONV => ExprInfo::FloatConv(Operation::new(function, op)), - LLIL_ROUND_TO_INT => ExprInfo::RoundToInt(Operation::new(function, op)), - LLIL_FLOOR => ExprInfo::Floor(Operation::new(function, op)), - LLIL_CEIL => ExprInfo::Ceil(Operation::new(function, op)), - LLIL_FTRUNC => ExprInfo::Ftrunc(Operation::new(function, op)), - - LLIL_FCMP_E => ExprInfo::FcmpE(Operation::new(function, op)), - LLIL_FCMP_NE => ExprInfo::FcmpNE(Operation::new(function, op)), - LLIL_FCMP_LT => ExprInfo::FcmpLT(Operation::new(function, op)), - LLIL_FCMP_LE => ExprInfo::FcmpLE(Operation::new(function, op)), - LLIL_FCMP_GT => ExprInfo::FcmpGT(Operation::new(function, op)), - LLIL_FCMP_GE => ExprInfo::FcmpGE(Operation::new(function, op)), - LLIL_FCMP_O => ExprInfo::FcmpO(Operation::new(function, op)), - LLIL_FCMP_UO => ExprInfo::FcmpUO(Operation::new(function, op)), - - LLIL_UNIMPL => ExprInfo::Unimpl(Operation::new(function, op)), - LLIL_UNIMPL_MEM => ExprInfo::UnimplMem(Operation::new(function, op)), + LLIL_EXTERN_PTR => LowLevelILExpressionKind::ExternPtr(Operation::new(function, op)), + + LLIL_ADD => LowLevelILExpressionKind::Add(Operation::new(function, op)), + LLIL_ADC => LowLevelILExpressionKind::Adc(Operation::new(function, op)), + LLIL_SUB => LowLevelILExpressionKind::Sub(Operation::new(function, op)), + LLIL_SBB => LowLevelILExpressionKind::Sbb(Operation::new(function, op)), + LLIL_AND => LowLevelILExpressionKind::And(Operation::new(function, op)), + LLIL_OR => LowLevelILExpressionKind::Or(Operation::new(function, op)), + LLIL_XOR => LowLevelILExpressionKind::Xor(Operation::new(function, op)), + LLIL_LSL => LowLevelILExpressionKind::Lsl(Operation::new(function, op)), + LLIL_LSR => LowLevelILExpressionKind::Lsr(Operation::new(function, op)), + LLIL_ASR => LowLevelILExpressionKind::Asr(Operation::new(function, op)), + LLIL_ROL => LowLevelILExpressionKind::Rol(Operation::new(function, op)), + LLIL_RLC => LowLevelILExpressionKind::Rlc(Operation::new(function, op)), + LLIL_ROR => LowLevelILExpressionKind::Ror(Operation::new(function, op)), + LLIL_RRC => LowLevelILExpressionKind::Rrc(Operation::new(function, op)), + LLIL_MUL => LowLevelILExpressionKind::Mul(Operation::new(function, op)), + + LLIL_MULU_DP => LowLevelILExpressionKind::MuluDp(Operation::new(function, op)), + LLIL_MULS_DP => LowLevelILExpressionKind::MulsDp(Operation::new(function, op)), + + LLIL_DIVU => LowLevelILExpressionKind::Divu(Operation::new(function, op)), + LLIL_DIVS => LowLevelILExpressionKind::Divs(Operation::new(function, op)), + + LLIL_DIVU_DP => LowLevelILExpressionKind::DivuDp(Operation::new(function, op)), + LLIL_DIVS_DP => LowLevelILExpressionKind::DivsDp(Operation::new(function, op)), + + LLIL_MODU => LowLevelILExpressionKind::Modu(Operation::new(function, op)), + LLIL_MODS => LowLevelILExpressionKind::Mods(Operation::new(function, op)), + + LLIL_MODU_DP => LowLevelILExpressionKind::ModuDp(Operation::new(function, op)), + LLIL_MODS_DP => LowLevelILExpressionKind::ModsDp(Operation::new(function, op)), + + LLIL_NEG => LowLevelILExpressionKind::Neg(Operation::new(function, op)), + LLIL_NOT => LowLevelILExpressionKind::Not(Operation::new(function, op)), + + LLIL_SX => LowLevelILExpressionKind::Sx(Operation::new(function, op)), + LLIL_ZX => LowLevelILExpressionKind::Zx(Operation::new(function, op)), + LLIL_LOW_PART => LowLevelILExpressionKind::LowPart(Operation::new(function, op)), + + LLIL_CMP_E => LowLevelILExpressionKind::CmpE(Operation::new(function, op)), + LLIL_CMP_NE => LowLevelILExpressionKind::CmpNe(Operation::new(function, op)), + LLIL_CMP_SLT => LowLevelILExpressionKind::CmpSlt(Operation::new(function, op)), + LLIL_CMP_ULT => LowLevelILExpressionKind::CmpUlt(Operation::new(function, op)), + LLIL_CMP_SLE => LowLevelILExpressionKind::CmpSle(Operation::new(function, op)), + LLIL_CMP_ULE => LowLevelILExpressionKind::CmpUle(Operation::new(function, op)), + LLIL_CMP_SGE => LowLevelILExpressionKind::CmpSge(Operation::new(function, op)), + LLIL_CMP_UGE => LowLevelILExpressionKind::CmpUge(Operation::new(function, op)), + LLIL_CMP_SGT => LowLevelILExpressionKind::CmpSgt(Operation::new(function, op)), + LLIL_CMP_UGT => LowLevelILExpressionKind::CmpUgt(Operation::new(function, op)), + + LLIL_BOOL_TO_INT => LowLevelILExpressionKind::BoolToInt(Operation::new(function, op)), + + LLIL_FADD => LowLevelILExpressionKind::Fadd(Operation::new(function, op)), + LLIL_FSUB => LowLevelILExpressionKind::Fsub(Operation::new(function, op)), + LLIL_FMUL => LowLevelILExpressionKind::Fmul(Operation::new(function, op)), + LLIL_FDIV => LowLevelILExpressionKind::Fdiv(Operation::new(function, op)), + + LLIL_FSQRT => LowLevelILExpressionKind::Fsqrt(Operation::new(function, op)), + LLIL_FNEG => LowLevelILExpressionKind::Fneg(Operation::new(function, op)), + LLIL_FABS => LowLevelILExpressionKind::Fabs(Operation::new(function, op)), + LLIL_FLOAT_TO_INT => LowLevelILExpressionKind::FloatToInt(Operation::new(function, op)), + LLIL_INT_TO_FLOAT => LowLevelILExpressionKind::IntToFloat(Operation::new(function, op)), + LLIL_FLOAT_CONV => LowLevelILExpressionKind::FloatConv(Operation::new(function, op)), + LLIL_ROUND_TO_INT => LowLevelILExpressionKind::RoundToInt(Operation::new(function, op)), + LLIL_FLOOR => LowLevelILExpressionKind::Floor(Operation::new(function, op)), + LLIL_CEIL => LowLevelILExpressionKind::Ceil(Operation::new(function, op)), + LLIL_FTRUNC => LowLevelILExpressionKind::Ftrunc(Operation::new(function, op)), + + LLIL_FCMP_E => LowLevelILExpressionKind::FcmpE(Operation::new(function, op)), + LLIL_FCMP_NE => LowLevelILExpressionKind::FcmpNE(Operation::new(function, op)), + LLIL_FCMP_LT => LowLevelILExpressionKind::FcmpLT(Operation::new(function, op)), + LLIL_FCMP_LE => LowLevelILExpressionKind::FcmpLE(Operation::new(function, op)), + LLIL_FCMP_GT => LowLevelILExpressionKind::FcmpGT(Operation::new(function, op)), + LLIL_FCMP_GE => LowLevelILExpressionKind::FcmpGE(Operation::new(function, op)), + LLIL_FCMP_O => LowLevelILExpressionKind::FcmpO(Operation::new(function, op)), + LLIL_FCMP_UO => LowLevelILExpressionKind::FcmpUO(Operation::new(function, op)), + + LLIL_UNIMPL => LowLevelILExpressionKind::Unimpl(Operation::new(function, op)), + LLIL_UNIMPL_MEM => LowLevelILExpressionKind::UnimplMem(Operation::new(function, op)), // TODO TEST_BIT ADD_OVERFLOW LLIL_REG_STACK_PUSH LLIL_REG_STACK_POP _ => { @@ -430,7 +444,7 @@ where op.address ); - ExprInfo::Undef(Operation::new(function, op)) + LowLevelILExpressionKind::Undef(Operation::new(function, op)) } } } @@ -440,7 +454,7 @@ where /// If the expression is malformed or is `Unimpl` there /// is no meaningful size associated with the result. pub fn size(&self) -> Option { - use self::ExprInfo::*; + use self::LowLevelILExpressionKind::*; match *self { Undef(..) | Unimpl(..) => None, @@ -463,7 +477,7 @@ where /// /// It does not examine the operands for equality. pub fn is_same_op_as(&self, other: &Self) -> bool { - use self::ExprInfo::*; + use self::LowLevelILExpressionKind::*; match (self, other) { (&Reg(..), &Reg(..)) => true, @@ -472,7 +486,7 @@ where } pub fn as_cmp_op(&self) -> Option<&Operation<'func, A, M, F, operation::Condition>> { - use self::ExprInfo::*; + use self::LowLevelILExpressionKind::*; match *self { CmpE(ref op) | CmpNe(ref op) | CmpSlt(ref op) | CmpUlt(ref op) | CmpSle(ref op) @@ -484,7 +498,7 @@ where } pub fn as_binary_op(&self) -> Option<&Operation<'func, A, M, F, operation::BinaryOp>> { - use self::ExprInfo::*; + use self::LowLevelILExpressionKind::*; match *self { Add(ref op) | Sub(ref op) | And(ref op) | Or(ref op) | Xor(ref op) | Lsl(ref op) @@ -498,7 +512,7 @@ where pub fn as_binary_op_carry( &self, ) -> Option<&Operation<'func, A, M, F, operation::BinaryOpCarry>> { - use self::ExprInfo::*; + use self::LowLevelILExpressionKind::*; match *self { Adc(ref op) | Sbb(ref op) | Rlc(ref op) | Rrc(ref op) => Some(op), @@ -509,7 +523,7 @@ where pub fn as_double_prec_div_op( &self, ) -> Option<&Operation<'func, A, M, F, operation::DoublePrecDivOp>> { - use self::ExprInfo::*; + use self::LowLevelILExpressionKind::*; match *self { DivuDp(ref op) | DivsDp(ref op) | ModuDp(ref op) | ModsDp(ref op) => Some(op), @@ -518,7 +532,7 @@ where } pub fn as_unary_op(&self) -> Option<&Operation<'func, A, M, F, operation::UnaryOp>> { - use self::ExprInfo::*; + use self::LowLevelILExpressionKind::*; match *self { Neg(ref op) | Not(ref op) | Sx(ref op) | Zx(ref op) | LowPart(ref op) @@ -531,9 +545,9 @@ where pub fn visit_sub_expressions(&self, mut visitor: T) -> VisitorAction where - T: FnMut(Expression<'func, A, M, F, ValueExpr>) -> VisitorAction, + T: FnMut(LowLevelILExpression<'func, A, M, F, ValueExpr>) -> VisitorAction, { - use ExprInfo::*; + use LowLevelILExpressionKind::*; macro_rules! visit { ($expr:expr) => { @@ -584,7 +598,7 @@ where } pub(crate) fn raw_struct(&self) -> &BNLowLevelILInstruction { - use self::ExprInfo::*; + use self::LowLevelILExpressionKind::*; match *self { Undef(ref op) => &op.op, @@ -635,12 +649,12 @@ where } } -impl<'func, A> ExprInfo<'func, A, Mutable, NonSSA> +impl<'func, A> LowLevelILExpressionKind<'func, A, Mutable, NonSSA> where A: 'func + Architecture, { pub fn flag_write(&self) -> Option { - use self::ExprInfo::*; + use self::LowLevelILExpressionKind::*; match *self { Undef(ref _op) => None, diff --git a/rust/src/llil/function.rs b/rust/src/lowlevelil/function.rs similarity index 78% rename from rust/src/llil/function.rs rename to rust/src/lowlevelil/function.rs index 013c54065..56cd91a1a 100644 --- a/rust/src/llil/function.rs +++ b/rust/src/lowlevelil/function.rs @@ -25,6 +25,7 @@ use std::marker::PhantomData; use crate::architecture::CoreArchitecture; use crate::basicblock::BasicBlock; use crate::function::Function; +use crate::lowlevelil::block::LowLevelILBlock; use crate::rc::*; use super::*; @@ -57,7 +58,7 @@ impl FunctionForm for SSA {} impl FunctionForm for NonSSA {} pub struct LowLevelILFunction { - pub(crate) borrower: A::Handle, + pub(crate) arch_handle: A::Handle, pub(crate) handle: *mut BNLowLevelILFunction, _arch: PhantomData<*mut A>, _mutability: PhantomData, @@ -70,11 +71,14 @@ where M: FunctionMutability, F: FunctionForm, { - pub(crate) unsafe fn from_raw(borrower: A::Handle, handle: *mut BNLowLevelILFunction) -> Self { + pub(crate) unsafe fn from_raw( + arch_handle: A::Handle, + handle: *mut BNLowLevelILFunction, + ) -> Self { debug_assert!(!handle.is_null()); Self { - borrower, + arch_handle, handle, _arch: PhantomData, _mutability: PhantomData, @@ -83,41 +87,53 @@ where } pub(crate) unsafe fn ref_from_raw( - borrower: A::Handle, + arch_handle: A::Handle, handle: *mut BNLowLevelILFunction, ) -> Ref { debug_assert!(!handle.is_null()); - Ref::new(Self::from_raw(borrower, handle)) + Ref::new(Self::from_raw(arch_handle, handle)) } pub(crate) fn arch(&self) -> &A { - self.borrower.borrow() + self.arch_handle.borrow() } - pub fn instruction_at>(&self, loc: L) -> Option> { - use binaryninjacore_sys::BNGetLowLevelILInstructionCount; - use binaryninjacore_sys::BNLowLevelILGetInstructionStart; + pub fn instruction_at>( + &self, + loc: L, + ) -> Option> { + Some(LowLevelILInstruction::new( + self, + self.instruction_index_at(loc)?, + )) + } + pub fn instruction_index_at>( + &self, + loc: L, + ) -> Option { + use binaryninjacore_sys::BNLowLevelILGetInstructionStart; let loc: Location = loc.into(); let arch = loc.arch.unwrap_or_else(|| *self.arch().as_ref()); - - unsafe { - let instr_idx = BNLowLevelILGetInstructionStart(self.handle, arch.handle, loc.addr); - - if instr_idx >= BNGetLowLevelILInstructionCount(self.handle) { - None - } else { - Some(Instruction::new(self, InstructionIndex(instr_idx))) - } + let instr_idx = + unsafe { BNLowLevelILGetInstructionStart(self.handle, arch.handle, loc.addr) }; + // `instr_idx` will equal self.instruction_count() if the instruction is not valid. + if instr_idx >= self.instruction_count() { + None + } else { + Some(LowLevelInstructionIndex(instr_idx)) } } - // TODO: This should be a Option instead - pub fn instruction_from_idx(&self, index: InstructionIndex) -> Instruction { + pub fn instruction_from_index( + &self, + index: LowLevelInstructionIndex, + ) -> Option> { if index.0 >= self.instruction_count() { - panic!("instruction index {} out of bounds", index); + None + } else { + Some(LowLevelILInstruction::new(self, index)) } - Instruction::new(self, index) } pub fn instruction_count(&self) -> usize { @@ -127,6 +143,13 @@ where } } + pub fn expression_count(&self) -> usize { + unsafe { + use binaryninjacore_sys::BNGetLowLevelILExprCount; + BNGetLowLevelILExprCount(self.handle) + } + } + pub fn function(&self) -> Ref { unsafe { let func = BNGetLowLevelILOwnerFunction(self.handle); @@ -143,13 +166,13 @@ where A: Architecture, F: FunctionForm, { - pub fn basic_blocks(&self) -> Array>> { + pub fn basic_blocks(&self) -> Array>> { use binaryninjacore_sys::BNGetLowLevelILBasicBlockList; unsafe { let mut count = 0; let blocks = BNGetLowLevelILBasicBlockList(self.handle, &mut count); - let context = LowLevelBlock { function: self }; + let context = LowLevelILBlock { function: self }; Array::new(blocks, count, context) } } @@ -198,7 +221,7 @@ where { unsafe fn inc_ref(handle: &Self) -> Ref { Ref::new(Self { - borrower: handle.borrower.clone(), + arch_handle: handle.arch_handle.clone(), handle: BNNewLowLevelILFunctionReference(handle.handle), _arch: PhantomData, _mutability: PhantomData, @@ -221,6 +244,7 @@ where f.debug_struct("LowLevelILFunction") .field("arch", &self.arch()) .field("instruction_count", &self.instruction_count()) + .field("expression_count", &self.expression_count()) .finish() } } diff --git a/rust/src/llil/instruction.rs b/rust/src/lowlevelil/instruction.rs similarity index 56% rename from rust/src/llil/instruction.rs rename to rust/src/lowlevelil/instruction.rs index 07f3cdc43..a96107651 100644 --- a/rust/src/llil/instruction.rs +++ b/rust/src/lowlevelil/instruction.rs @@ -24,47 +24,60 @@ use std::fmt::{Debug, Display, Formatter}; use super::VisitorAction; #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct InstructionIndex(pub usize); +pub struct LowLevelInstructionIndex(pub usize); -impl InstructionIndex { +impl LowLevelInstructionIndex { pub fn next(&self) -> Self { Self(self.0 + 1) } } -impl Display for InstructionIndex { +impl From for LowLevelInstructionIndex { + fn from(index: usize) -> Self { + Self(index) + } +} + +impl From for LowLevelInstructionIndex { + fn from(index: u64) -> Self { + Self(index as usize) + } +} + +impl Display for LowLevelInstructionIndex { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { f.write_fmt(format_args!("{}", self.0)) } } +// TODO: Probably want to rename this with a LowLevelIL prefix to avoid collisions when we add handlers for other ILs pub trait InstructionHandler<'func, A, M, F> where A: Architecture, M: FunctionMutability, F: FunctionForm, { - fn info(&self) -> InstrInfo<'func, A, M, F>; + fn kind(&self) -> LowLevelILInstructionKind<'func, A, M, F>; /// Visit the sub expressions of this instruction. /// /// NOTE: This does not visit the root expression, i.e. the instruction. fn visit_tree(&self, f: &mut T) -> VisitorAction where - T: FnMut(&Expression<'func, A, M, F, ValueExpr>) -> VisitorAction; + T: FnMut(&LowLevelILExpression<'func, A, M, F, ValueExpr>) -> VisitorAction; } -pub struct Instruction<'func, A, M, F> +pub struct LowLevelILInstruction<'func, A, M, F> where A: 'func + Architecture, M: FunctionMutability, F: FunctionForm, { pub(crate) function: &'func LowLevelILFunction, - pub index: InstructionIndex, + pub index: LowLevelInstructionIndex, } -impl<'func, A, M, F> Instruction<'func, A, M, F> +impl<'func, A, M, F> LowLevelILInstruction<'func, A, M, F> where A: 'func + Architecture, M: FunctionMutability, @@ -72,19 +85,28 @@ where { // TODO: Should we check the instruction count here with BNGetLowLevelILInstructionCount? // TODO: If we _can_ then this should become an Option methinks - pub fn new(function: &'func LowLevelILFunction, index: InstructionIndex) -> Self { + pub fn new( + function: &'func LowLevelILFunction, + index: LowLevelInstructionIndex, + ) -> Self { Self { function, index } } pub fn address(&self) -> u64 { - let expr_idx = - unsafe { BNGetLowLevelILIndexForInstruction(self.function.handle, self.index.0) }; - let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, expr_idx) }; - op.address + self.into_raw().address + } + + // TODO: Document the difference between the self.index and the expr_idx. + pub fn expr_idx(&self) -> usize { + unsafe { BNGetLowLevelILIndexForInstruction(self.function.handle, self.index.0) } + } + + pub fn into_raw(&self) -> BNLowLevelILInstruction { + unsafe { BNGetLowLevelILByIndex(self.function.handle, self.expr_idx()) } } } -impl<'func, A, M, F> Debug for Instruction<'func, A, M, F> +impl<'func, A, M, F> Debug for LowLevelILInstruction<'func, A, M, F> where A: 'func + Architecture, M: FunctionMutability, @@ -98,87 +120,92 @@ where } } -impl<'func, A, M> InstructionHandler<'func, A, M, SSA> for Instruction<'func, A, M, SSA> +impl<'func, A, M> InstructionHandler<'func, A, M, SSA> for LowLevelILInstruction<'func, A, M, SSA> where A: 'func + Architecture, M: FunctionMutability, { - fn info(&self) -> InstrInfo<'func, A, M, SSA> { + fn kind(&self) -> LowLevelILInstructionKind<'func, A, M, SSA> { #[allow(unused_imports)] use binaryninjacore_sys::BNLowLevelILOperation::*; - let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, self.index.0) }; + let raw_op = self.into_raw(); #[allow(clippy::match_single_binding)] - match op.operation { + match raw_op.operation { // Any invalid ops for Non-Lifted IL will be checked here. // SAFETY: We have checked for illegal operations. - _ => unsafe { InstrInfo::from_raw(self.function, self.index, op) }, + _ => unsafe { LowLevelILInstructionKind::from_raw(self.function, self.index, raw_op) }, } } fn visit_tree(&self, f: &mut T) -> VisitorAction where - T: FnMut(&Expression<'func, A, M, SSA, ValueExpr>) -> VisitorAction, + T: FnMut(&LowLevelILExpression<'func, A, M, SSA, ValueExpr>) -> VisitorAction, { // Recursively visit sub expressions. - self.info().visit_sub_expressions(|e| e.visit_tree(f)) + self.kind().visit_sub_expressions(|e| e.visit_tree(f)) } } impl<'func, A, M> InstructionHandler<'func, A, M, NonSSA> - for Instruction<'func, A, M, NonSSA> + for LowLevelILInstruction<'func, A, M, NonSSA> where A: 'func + Architecture, M: FunctionMutability, { - fn info(&self) -> InstrInfo<'func, A, M, NonSSA> { + fn kind(&self) -> LowLevelILInstructionKind<'func, A, M, NonSSA> { #[allow(unused_imports)] use binaryninjacore_sys::BNLowLevelILOperation::*; - let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, self.index.0) }; + let raw_op = self.into_raw(); #[allow(clippy::match_single_binding)] - match op.operation { + match raw_op.operation { // Any invalid ops for Non-Lifted IL will be checked here. // SAFETY: We have checked for illegal operations. - _ => unsafe { InstrInfo::from_raw(self.function, self.index, op) }, + _ => unsafe { LowLevelILInstructionKind::from_raw(self.function, self.index, raw_op) }, } } fn visit_tree(&self, f: &mut T) -> VisitorAction where - T: FnMut(&Expression<'func, A, M, NonSSA, ValueExpr>) -> VisitorAction, + T: FnMut( + &LowLevelILExpression<'func, A, M, NonSSA, ValueExpr>, + ) -> VisitorAction, { // Recursively visit sub expressions. - self.info().visit_sub_expressions(|e| e.visit_tree(f)) + self.kind().visit_sub_expressions(|e| e.visit_tree(f)) } } impl<'func, A, M> InstructionHandler<'func, A, M, NonSSA> - for Instruction<'func, A, M, NonSSA> + for LowLevelILInstruction<'func, A, M, NonSSA> where A: 'func + Architecture, M: FunctionMutability, { - fn info(&self) -> InstrInfo<'func, A, M, NonSSA> { + fn kind(&self) -> LowLevelILInstructionKind<'func, A, M, NonSSA> { #[allow(unused_imports)] use binaryninjacore_sys::BNLowLevelILOperation::*; - let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, self.index.0) }; + let raw_op = self.into_raw(); #[allow(clippy::match_single_binding)] - match op.operation { + match raw_op.operation { // Any invalid ops for Non-Lifted IL will be checked here. // SAFETY: We have checked for illegal operations. - _ => unsafe { InstrInfo::from_raw(self.function, self.index, op) }, + _ => unsafe { LowLevelILInstructionKind::from_raw(self.function, self.index, raw_op) }, } } fn visit_tree(&self, f: &mut T) -> VisitorAction where - T: FnMut(&Expression<'func, A, M, NonSSA, ValueExpr>) -> VisitorAction, + T: FnMut( + &LowLevelILExpression<'func, A, M, NonSSA, ValueExpr>, + ) -> VisitorAction, { // Recursively visit sub expressions. - self.info().visit_sub_expressions(|e| e.visit_tree(f)) + self.kind().visit_sub_expressions(|e| e.visit_tree(f)) } } -pub enum InstrInfo<'func, A, M, F> +#[derive(Debug)] +pub enum LowLevelILInstructionKind<'func, A, M, F> where A: 'func + Architecture, M: FunctionMutability, @@ -211,10 +238,10 @@ where Undef(Operation<'func, A, M, F, operation::NoArgs>), /// The instruction is an expression. - Value(Expression<'func, A, M, F, ValueExpr>), + Value(LowLevelILExpression<'func, A, M, F, ValueExpr>), } -impl<'func, A, M, F> InstrInfo<'func, A, M, F> +impl<'func, A, M, F> LowLevelILInstructionKind<'func, A, M, F> where A: Architecture, M: FunctionMutability, @@ -222,53 +249,66 @@ where { pub(crate) unsafe fn from_raw( function: &'func LowLevelILFunction, - index: InstructionIndex, + index: LowLevelInstructionIndex, op: BNLowLevelILInstruction, ) -> Self { use binaryninjacore_sys::BNLowLevelILOperation::*; match op.operation { - LLIL_NOP => InstrInfo::Nop(Operation::new(function, op)), - LLIL_SET_REG | LLIL_SET_REG_SSA => InstrInfo::SetReg(Operation::new(function, op)), + LLIL_NOP => LowLevelILInstructionKind::Nop(Operation::new(function, op)), + LLIL_SET_REG | LLIL_SET_REG_SSA => { + LowLevelILInstructionKind::SetReg(Operation::new(function, op)) + } LLIL_SET_REG_SPLIT | LLIL_SET_REG_SPLIT_SSA => { - InstrInfo::SetRegSplit(Operation::new(function, op)) + LowLevelILInstructionKind::SetRegSplit(Operation::new(function, op)) + } + LLIL_SET_FLAG | LLIL_SET_FLAG_SSA => { + LowLevelILInstructionKind::SetFlag(Operation::new(function, op)) + } + LLIL_STORE | LLIL_STORE_SSA => { + LowLevelILInstructionKind::Store(Operation::new(function, op)) } - LLIL_SET_FLAG | LLIL_SET_FLAG_SSA => InstrInfo::SetFlag(Operation::new(function, op)), - LLIL_STORE | LLIL_STORE_SSA => InstrInfo::Store(Operation::new(function, op)), - LLIL_PUSH => InstrInfo::Push(Operation::new(function, op)), + LLIL_PUSH => LowLevelILInstructionKind::Push(Operation::new(function, op)), - LLIL_JUMP => InstrInfo::Jump(Operation::new(function, op)), - LLIL_JUMP_TO => InstrInfo::JumpTo(Operation::new(function, op)), + LLIL_JUMP => LowLevelILInstructionKind::Jump(Operation::new(function, op)), + LLIL_JUMP_TO => LowLevelILInstructionKind::JumpTo(Operation::new(function, op)), LLIL_CALL | LLIL_CALL_STACK_ADJUST | LLIL_CALL_SSA => { - InstrInfo::Call(Operation::new(function, op)) + LowLevelILInstructionKind::Call(Operation::new(function, op)) + } + LLIL_TAILCALL | LLIL_TAILCALL_SSA => { + LowLevelILInstructionKind::TailCall(Operation::new(function, op)) } - LLIL_TAILCALL | LLIL_TAILCALL_SSA => InstrInfo::TailCall(Operation::new(function, op)), - LLIL_RET => InstrInfo::Ret(Operation::new(function, op)), - LLIL_NORET => InstrInfo::NoRet(Operation::new(function, op)), + LLIL_RET => LowLevelILInstructionKind::Ret(Operation::new(function, op)), + LLIL_NORET => LowLevelILInstructionKind::NoRet(Operation::new(function, op)), - LLIL_IF => InstrInfo::If(Operation::new(function, op)), - LLIL_GOTO => InstrInfo::Goto(Operation::new(function, op)), + LLIL_IF => LowLevelILInstructionKind::If(Operation::new(function, op)), + LLIL_GOTO => LowLevelILInstructionKind::Goto(Operation::new(function, op)), - LLIL_SYSCALL | LLIL_SYSCALL_SSA => InstrInfo::Syscall(Operation::new(function, op)), + LLIL_SYSCALL | LLIL_SYSCALL_SSA => { + LowLevelILInstructionKind::Syscall(Operation::new(function, op)) + } LLIL_INTRINSIC | LLIL_INTRINSIC_SSA => { - InstrInfo::Intrinsic(Operation::new(function, op)) + LowLevelILInstructionKind::Intrinsic(Operation::new(function, op)) } - LLIL_BP => InstrInfo::Bp(Operation::new(function, op)), - LLIL_TRAP => InstrInfo::Trap(Operation::new(function, op)), - LLIL_UNDEF => InstrInfo::Undef(Operation::new(function, op)), + LLIL_BP => LowLevelILInstructionKind::Bp(Operation::new(function, op)), + LLIL_TRAP => LowLevelILInstructionKind::Trap(Operation::new(function, op)), + LLIL_UNDEF => LowLevelILInstructionKind::Undef(Operation::new(function, op)), // Could not identify an instruction, therefor must be a value expression. // The conversion from instruction index to expression index is safe here. - _ => InstrInfo::Value(Expression::new(function, ExpressionIndex(index.0))), + _ => LowLevelILInstructionKind::Value(LowLevelILExpression::new( + function, + LowLevelILExpressionIndex(index.0), + )), } } fn visit_sub_expressions(&self, mut visitor: T) -> VisitorAction where - T: FnMut(&Expression<'func, A, M, F, ValueExpr>) -> VisitorAction, + T: FnMut(&LowLevelILExpression<'func, A, M, F, ValueExpr>) -> VisitorAction, { - use InstrInfo::*; + use LowLevelILInstructionKind::*; macro_rules! visit { ($expr:expr) => { diff --git a/rust/src/llil/lifting.rs b/rust/src/lowlevelil/lifting.rs similarity index 72% rename from rust/src/llil/lifting.rs rename to rust/src/lowlevelil/lifting.rs index c0e58b628..ff2d6dbf5 100644 --- a/rust/src/llil/lifting.rs +++ b/rust/src/lowlevelil/lifting.rs @@ -14,53 +14,51 @@ use std::marker::PhantomData; +use binaryninjacore_sys::{BNAddLowLevelILLabelForAddress, BNLowLevelILOperation}; +use binaryninjacore_sys::{BNLowLevelILLabel, BNRegisterOrConstant}; + +use super::*; use crate::architecture::Register as ArchReg; use crate::architecture::{Architecture, FlagWriteId, RegisterId}; use crate::architecture::{ Flag, FlagClass, FlagCondition, FlagGroup, FlagRole, FlagWrite, Intrinsic, }; use crate::function::Location; -use crate::llil::{ - Expression, ExpressionIndex, ExpressionResultType, LiftedExpr, LiftedNonSSA, Lifter, - LowLevelILFunction, Mutable, NonSSA, Register, ValueExpr, VoidExpr, -}; -use binaryninjacore_sys::{BNAddLowLevelILLabelForAddress, BNLowLevelILOperation}; -use binaryninjacore_sys::{BNLowLevelILLabel, BNRegisterOrConstant}; -pub trait Liftable<'func, A: 'func + Architecture> { +pub trait LiftableLowLevelIL<'func, A: 'func + Architecture> { type Result: ExpressionResultType; fn lift( - il: &'func LowLevelILFunction>, + il: &'func MutableLiftedILFunction, expr: Self, - ) -> Expression<'func, A, Mutable, NonSSA, Self::Result>; + ) -> MutableLiftedILExpr<'func, A, Self::Result>; } -pub trait LiftableWithSize<'func, A: 'func + Architecture>: - Liftable<'func, A, Result = ValueExpr> +pub trait LiftableLowLevelILWithSize<'func, A: 'func + Architecture>: + LiftableLowLevelIL<'func, A, Result = ValueExpr> { fn lift_with_size( - il: &'func LowLevelILFunction>, + il: &'func MutableLiftedILFunction, expr: Self, size: usize, - ) -> Expression<'func, A, Mutable, NonSSA, ValueExpr>; + ) -> MutableLiftedILExpr<'func, A, ValueExpr>; } #[derive(Copy, Clone)] -pub enum RegisterOrConstant { - Register(usize, Register), +pub enum LowLevelILRegisterOrConstant { + Register(usize, LowLevelILRegister), Constant(usize, u64), } -impl From> for BNRegisterOrConstant { - fn from(value: RegisterOrConstant) -> Self { +impl From> for BNRegisterOrConstant { + fn from(value: LowLevelILRegisterOrConstant) -> Self { match value { - RegisterOrConstant::Register(_, r) => Self { + LowLevelILRegisterOrConstant::Register(_, r) => Self { constant: false, reg: r.id().0, value: 0, }, - RegisterOrConstant::Constant(_, value) => Self { + LowLevelILRegisterOrConstant::Constant(_, value) => Self { constant: true, reg: 0, value, @@ -71,79 +69,179 @@ impl From> for BNRegisterOrConstant { // TODO flesh way out #[derive(Copy, Clone)] -pub enum FlagWriteOp { - SetReg(usize, RegisterOrConstant), - SetRegSplit(usize, RegisterOrConstant, RegisterOrConstant), - - Sub(usize, RegisterOrConstant, RegisterOrConstant), - Add(usize, RegisterOrConstant, RegisterOrConstant), - - Load(usize, RegisterOrConstant), - - Push(usize, RegisterOrConstant), - Neg(usize, RegisterOrConstant), - Not(usize, RegisterOrConstant), - Sx(usize, RegisterOrConstant), - Zx(usize, RegisterOrConstant), - LowPart(usize, RegisterOrConstant), - BoolToInt(usize, RegisterOrConstant), - FloatToInt(usize, RegisterOrConstant), - - Store(usize, RegisterOrConstant, RegisterOrConstant), - - And(usize, RegisterOrConstant, RegisterOrConstant), - Or(usize, RegisterOrConstant, RegisterOrConstant), - Xor(usize, RegisterOrConstant, RegisterOrConstant), - Lsl(usize, RegisterOrConstant, RegisterOrConstant), - Lsr(usize, RegisterOrConstant, RegisterOrConstant), - Asr(usize, RegisterOrConstant, RegisterOrConstant), - Rol(usize, RegisterOrConstant, RegisterOrConstant), - Ror(usize, RegisterOrConstant, RegisterOrConstant), - Mul(usize, RegisterOrConstant, RegisterOrConstant), - MuluDp(usize, RegisterOrConstant, RegisterOrConstant), - MulsDp(usize, RegisterOrConstant, RegisterOrConstant), - Divu(usize, RegisterOrConstant, RegisterOrConstant), - Divs(usize, RegisterOrConstant, RegisterOrConstant), - Modu(usize, RegisterOrConstant, RegisterOrConstant), - Mods(usize, RegisterOrConstant, RegisterOrConstant), - DivuDp(usize, RegisterOrConstant, RegisterOrConstant), - DivsDp(usize, RegisterOrConstant, RegisterOrConstant), - ModuDp(usize, RegisterOrConstant, RegisterOrConstant), - ModsDp(usize, RegisterOrConstant, RegisterOrConstant), - - TestBit(usize, RegisterOrConstant, RegisterOrConstant), - AddOverflow(usize, RegisterOrConstant, RegisterOrConstant), +pub enum LowLevelILFlagWriteOp { + SetReg(usize, LowLevelILRegisterOrConstant), + SetRegSplit( + usize, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + ), + + Sub( + usize, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + ), + Add( + usize, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + ), + + Load(usize, LowLevelILRegisterOrConstant), + + Push(usize, LowLevelILRegisterOrConstant), + Neg(usize, LowLevelILRegisterOrConstant), + Not(usize, LowLevelILRegisterOrConstant), + Sx(usize, LowLevelILRegisterOrConstant), + Zx(usize, LowLevelILRegisterOrConstant), + LowPart(usize, LowLevelILRegisterOrConstant), + BoolToInt(usize, LowLevelILRegisterOrConstant), + FloatToInt(usize, LowLevelILRegisterOrConstant), + + Store( + usize, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + ), + + And( + usize, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + ), + Or( + usize, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + ), + Xor( + usize, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + ), + Lsl( + usize, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + ), + Lsr( + usize, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + ), + Asr( + usize, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + ), + Rol( + usize, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + ), + Ror( + usize, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + ), + Mul( + usize, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + ), + MuluDp( + usize, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + ), + MulsDp( + usize, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + ), + Divu( + usize, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + ), + Divs( + usize, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + ), + Modu( + usize, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + ), + Mods( + usize, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + ), + DivuDp( + usize, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + ), + DivsDp( + usize, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + ), + ModuDp( + usize, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + ), + ModsDp( + usize, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + ), + + TestBit( + usize, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + ), + AddOverflow( + usize, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + ), Adc( usize, - RegisterOrConstant, - RegisterOrConstant, - RegisterOrConstant, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, ), Sbb( usize, - RegisterOrConstant, - RegisterOrConstant, - RegisterOrConstant, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, ), Rlc( usize, - RegisterOrConstant, - RegisterOrConstant, - RegisterOrConstant, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, ), Rrc( usize, - RegisterOrConstant, - RegisterOrConstant, - RegisterOrConstant, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, + LowLevelILRegisterOrConstant, ), Pop(usize), // TODO: floating point stuff, llil comparison ops that set flags, intrinsics } -impl FlagWriteOp { +impl LowLevelILFlagWriteOp { pub(crate) fn from_op( arch: &A, size: usize, @@ -154,28 +252,30 @@ impl FlagWriteOp { A: Architecture, R: ArchReg, { - use self::FlagWriteOp::*; + use self::LowLevelILFlagWriteOp::*; use binaryninjacore_sys::BNLowLevelILOperation::*; fn build_op( arch: &A, size: usize, operand: &BNRegisterOrConstant, - ) -> RegisterOrConstant + ) -> LowLevelILRegisterOrConstant where A: Architecture, R: ArchReg, { if operand.constant { - RegisterOrConstant::Constant(size, operand.value) + LowLevelILRegisterOrConstant::Constant(size, operand.value) } else { let il_reg = if 0x8000_0000 & operand.reg == 0 { - Register::ArchReg(arch.register_from_id(RegisterId(operand.reg)).unwrap()) + LowLevelILRegister::ArchReg( + arch.register_from_id(RegisterId(operand.reg)).unwrap(), + ) } else { - Register::Temp(operand.reg) + LowLevelILRegister::Temp(operand.reg) }; - RegisterOrConstant::Register(size, il_reg) + LowLevelILRegisterOrConstant::Register(size, il_reg) } } @@ -240,7 +340,7 @@ impl FlagWriteOp { } pub(crate) fn size_and_op(&self) -> (usize, BNLowLevelILOperation) { - use self::FlagWriteOp::*; + use self::LowLevelILFlagWriteOp::*; use binaryninjacore_sys::BNLowLevelILOperation::*; match *self { @@ -296,7 +396,7 @@ impl FlagWriteOp { } pub(crate) fn raw_operands(&self) -> (usize, [BNRegisterOrConstant; 5]) { - use self::FlagWriteOp::*; + use self::LowLevelILFlagWriteOp::*; let mut operands: [BNRegisterOrConstant; 5] = [BNRegisterOrConstant::default(); 5]; @@ -365,9 +465,9 @@ impl FlagWriteOp { pub fn get_default_flag_write_llil<'func, A>( arch: &A, role: FlagRole, - op: FlagWriteOp, - il: &'func Lifter, -) -> LiftedExpr<'func, A> + op: LowLevelILFlagWriteOp, + il: &'func MutableLiftedILFunction, +) -> MutableLiftedILExpr<'func, A, ValueExpr> where A: 'func + Architecture, { @@ -387,15 +487,15 @@ where ) }; - Expression::new(il, ExpressionIndex(expr_idx)) + LowLevelILExpression::new(il, LowLevelILExpressionIndex(expr_idx)) } pub fn get_default_flag_cond_llil<'func, A>( arch: &A, cond: FlagCondition, class: Option, - il: &'func Lifter, -) -> LiftedExpr<'func, A> + il: &'func MutableLiftedILFunction, +) -> MutableLiftedILExpr<'func, A, ValueExpr> where A: 'func + Architecture, { @@ -410,25 +510,25 @@ where il.handle, ); - Expression::new(il, ExpressionIndex(expr_idx)) + LowLevelILExpression::new(il, LowLevelILExpressionIndex(expr_idx)) } } macro_rules! prim_int_lifter { ($x:ty) => { - impl<'a, A: 'a + Architecture> Liftable<'a, A> for $x { + impl<'a, A: 'a + Architecture> LiftableLowLevelIL<'a, A> for $x { type Result = ValueExpr; - fn lift(il: &'a LowLevelILFunction>, val: Self) - -> Expression<'a, A, Mutable, NonSSA, Self::Result> + fn lift(il: &'a MutableLiftedILFunction, val: Self) + -> MutableLiftedILExpr<'a, A, Self::Result> { il.const_int(std::mem::size_of::(), val as i64 as u64) } } - impl<'a, A: 'a + Architecture> LiftableWithSize<'a, A> for $x { - fn lift_with_size(il: &'a LowLevelILFunction>, val: Self, size: usize) - -> Expression<'a, A, Mutable, NonSSA, ValueExpr> + impl<'a, A: 'a + Architecture> LiftableLowLevelILWithSize<'a, A> for $x { + fn lift_with_size(il: &'a MutableLiftedILFunction, val: Self, size: usize) + -> MutableLiftedILExpr<'a, A, ValueExpr> { let raw = val as i64; @@ -461,74 +561,89 @@ prim_int_lifter!(u16); prim_int_lifter!(u32); prim_int_lifter!(u64); -impl<'a, R: ArchReg, A: 'a + Architecture> Liftable<'a, A> for Register +impl<'a, R: ArchReg, A: 'a + Architecture> LiftableLowLevelIL<'a, A> for LowLevelILRegister where - R: Liftable<'a, A, Result = ValueExpr> + Into>, + R: LiftableLowLevelIL<'a, A, Result = ValueExpr> + Into>, { type Result = ValueExpr; fn lift( - il: &'a LowLevelILFunction>, + il: &'a MutableLiftedILFunction, reg: Self, - ) -> Expression<'a, A, Mutable, NonSSA, Self::Result> { + ) -> MutableLiftedILExpr<'a, A, Self::Result> { match reg { - Register::ArchReg(r) => R::lift(il, r), - Register::Temp(t) => il.reg(il.arch().default_integer_size(), Register::Temp(t)), + LowLevelILRegister::ArchReg(r) => R::lift(il, r), + LowLevelILRegister::Temp(t) => il.reg( + il.arch().default_integer_size(), + LowLevelILRegister::Temp(t), + ), } } } -impl<'a, R: ArchReg, A: 'a + Architecture> LiftableWithSize<'a, A> for Register +impl<'a, R: ArchReg, A: 'a + Architecture> LiftableLowLevelILWithSize<'a, A> + for LowLevelILRegister where - R: LiftableWithSize<'a, A> + Into>, + R: LiftableLowLevelILWithSize<'a, A> + Into>, { fn lift_with_size( - il: &'a LowLevelILFunction>, + il: &'a MutableLiftedILFunction, reg: Self, size: usize, - ) -> Expression<'a, A, Mutable, NonSSA, ValueExpr> { + ) -> MutableLiftedILExpr<'a, A, ValueExpr> { match reg { - Register::ArchReg(r) => R::lift_with_size(il, r, size), - Register::Temp(t) => il.reg(size, Register::Temp(t)), + LowLevelILRegister::ArchReg(r) => R::lift_with_size(il, r, size), + LowLevelILRegister::Temp(t) => il.reg(size, LowLevelILRegister::Temp(t)), } } } -impl<'a, R: ArchReg, A: 'a + Architecture> Liftable<'a, A> for RegisterOrConstant +impl<'a, R: ArchReg, A: 'a + Architecture> LiftableLowLevelIL<'a, A> + for LowLevelILRegisterOrConstant where - R: LiftableWithSize<'a, A, Result = ValueExpr> + Into>, + R: LiftableLowLevelILWithSize<'a, A, Result = ValueExpr> + Into>, { type Result = ValueExpr; fn lift( - il: &'a LowLevelILFunction>, + il: &'a MutableLiftedILFunction, reg: Self, - ) -> Expression<'a, A, Mutable, NonSSA, Self::Result> { + ) -> MutableLiftedILExpr<'a, A, Self::Result> { match reg { - RegisterOrConstant::Register(size, r) => Register::::lift_with_size(il, r, size), - RegisterOrConstant::Constant(size, value) => u64::lift_with_size(il, value, size), + LowLevelILRegisterOrConstant::Register(size, r) => { + LowLevelILRegister::::lift_with_size(il, r, size) + } + LowLevelILRegisterOrConstant::Constant(size, value) => { + u64::lift_with_size(il, value, size) + } } } } -impl<'a, R: ArchReg, A: 'a + Architecture> LiftableWithSize<'a, A> for RegisterOrConstant +impl<'a, R: ArchReg, A: 'a + Architecture> LiftableLowLevelILWithSize<'a, A> + for LowLevelILRegisterOrConstant where - R: LiftableWithSize<'a, A> + Into>, + R: LiftableLowLevelILWithSize<'a, A> + Into>, { fn lift_with_size( - il: &'a LowLevelILFunction>, + il: &'a MutableLiftedILFunction, reg: Self, size: usize, - ) -> Expression<'a, A, Mutable, NonSSA, ValueExpr> { + ) -> MutableLiftedILExpr<'a, A, ValueExpr> { // TODO ensure requested size is compatible with size of this constant match reg { - RegisterOrConstant::Register(_, r) => Register::::lift_with_size(il, r, size), - RegisterOrConstant::Constant(_, value) => u64::lift_with_size(il, value, size), + LowLevelILRegisterOrConstant::Register(_, r) => { + LowLevelILRegister::::lift_with_size(il, r, size) + } + LowLevelILRegisterOrConstant::Constant(_, value) => { + u64::lift_with_size(il, value, size) + } } } } -impl<'a, A, R> Liftable<'a, A> for Expression<'a, A, Mutable, NonSSA, R> +impl<'a, A, R> LiftableLowLevelIL<'a, A> + for LowLevelILExpression<'a, A, Mutable, NonSSA, R> where A: 'a + Architecture, R: ExpressionResultType, @@ -536,26 +651,26 @@ where type Result = R; fn lift( - il: &'a LowLevelILFunction>, + il: &'a MutableLiftedILFunction, expr: Self, - ) -> Expression<'a, A, Mutable, NonSSA, Self::Result> { + ) -> MutableLiftedILExpr<'a, A, Self::Result> { debug_assert!(expr.function.handle == il.handle); expr } } -impl<'a, A: 'a + Architecture> LiftableWithSize<'a, A> - for Expression<'a, A, Mutable, NonSSA, ValueExpr> +impl<'a, A: 'a + Architecture> LiftableLowLevelILWithSize<'a, A> + for LowLevelILExpression<'a, A, Mutable, NonSSA, ValueExpr> { fn lift_with_size( - il: &'a LowLevelILFunction>, + il: &'a MutableLiftedILFunction, expr: Self, _size: usize, - ) -> Expression<'a, A, Mutable, NonSSA, Self::Result> { + ) -> MutableLiftedILExpr<'a, A, Self::Result> { #[cfg(debug_assertions)] { - use crate::llil::ExpressionHandler; - if let Some(expr_size) = expr.info().size() { + use crate::lowlevelil::ExpressionHandler; + if let Some(expr_size) = expr.kind().size() { if expr_size != _size { log::warn!( "il @ {:x} attempted to lift {} byte expression as {} bytes", @@ -567,26 +682,23 @@ impl<'a, A: 'a + Architecture> LiftableWithSize<'a, A> } } - Liftable::lift(il, expr) + LiftableLowLevelIL::lift(il, expr) } } -impl<'func, A, R> Expression<'func, A, Mutable, NonSSA, R> +impl<'func, A, R> LowLevelILExpression<'func, A, Mutable, NonSSA, R> where A: 'func + Architecture, R: ExpressionResultType, { pub fn with_source_operand(self, op: u32) -> Self { use binaryninjacore_sys::BNLowLevelILSetExprSourceOperand; - unsafe { BNLowLevelILSetExprSourceOperand(self.function.handle, self.index.0, op) } - self } pub fn append(self) { - let il = self.function; - il.instruction(self); + self.function.add_instruction(self); } } @@ -611,7 +723,7 @@ where A: 'a + Architecture, R: ExpressionResultType, { - pub fn from_expr(expr: Expression<'a, A, Mutable, NonSSA, R>) -> Self { + pub fn from_expr(expr: LowLevelILExpression<'a, A, Mutable, NonSSA, R>) -> Self { use binaryninjacore_sys::BNGetLowLevelILByIndex; let instr = unsafe { BNGetLowLevelILByIndex(expr.function.handle, expr.index.0) }; @@ -635,7 +747,7 @@ where self } - pub fn build(self) -> Expression<'a, A, Mutable, NonSSA, R> { + pub fn build(self) -> LowLevelILExpression<'a, A, Mutable, NonSSA, R> { use binaryninjacore_sys::BNLowLevelILAddExpr; let expr_idx = unsafe { @@ -651,25 +763,23 @@ where ) }; - Expression::new(self.function, ExpressionIndex(expr_idx)) + LowLevelILExpression::new(self.function, LowLevelILExpressionIndex(expr_idx)) } pub fn with_source_operand( self, op: u32, - ) -> Expression<'a, A, Mutable, NonSSA, R> { + ) -> LowLevelILExpression<'a, A, Mutable, NonSSA, R> { self.build().with_source_operand(op) } pub fn append(self) { let expr = self.build(); - let il = expr.function; - - il.instruction(expr); + expr.function.add_instruction(expr); } } -impl<'a, A, R> Liftable<'a, A> for ExpressionBuilder<'a, A, R> +impl<'a, A, R> LiftableLowLevelIL<'a, A> for ExpressionBuilder<'a, A, R> where A: 'a + Architecture, R: ExpressionResultType, @@ -677,24 +787,24 @@ where type Result = R; fn lift( - il: &'a LowLevelILFunction>, + il: &'a MutableLiftedILFunction, expr: Self, - ) -> Expression<'a, A, Mutable, NonSSA, Self::Result> { + ) -> MutableLiftedILExpr<'a, A, Self::Result> { debug_assert!(expr.function.handle == il.handle); expr.build() } } -impl<'a, A> LiftableWithSize<'a, A> for ExpressionBuilder<'a, A, ValueExpr> +impl<'a, A> LiftableLowLevelILWithSize<'a, A> for ExpressionBuilder<'a, A, ValueExpr> where A: 'a + Architecture, { fn lift_with_size( - il: &'a LowLevelILFunction>, + il: &'a MutableLiftedILFunction, expr: Self, _size: usize, - ) -> Expression<'a, A, Mutable, NonSSA, ValueExpr> { + ) -> MutableLiftedILExpr<'a, A, ValueExpr> { #[cfg(debug_assertions)] { use binaryninjacore_sys::BNLowLevelILOperation::{LLIL_UNIMPL, LLIL_UNIMPL_MEM}; @@ -709,19 +819,19 @@ where } } - Liftable::lift(il, expr) + LiftableLowLevelIL::lift(il, expr) } } macro_rules! no_arg_lifter { ($name:ident, $op:ident, $result:ty) => { - pub fn $name(&self) -> Expression, $result> { + pub fn $name(&self) -> LowLevelILExpression, $result> { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::$op; let expr_idx = unsafe { BNLowLevelILAddExpr(self.handle, $op, 0, 0, 0, 0, 0, 0) }; - Expression::new(self, ExpressionIndex(expr_idx)) + LowLevelILExpression::new(self, LowLevelILExpressionIndex(expr_idx)) } }; } @@ -751,9 +861,9 @@ macro_rules! unsized_unary_op_lifter { pub fn $name<'a, E>( &'a self, expr: E, - ) -> Expression<'a, A, Mutable, NonSSA, $result> + ) -> LowLevelILExpression<'a, A, Mutable, NonSSA, $result> where - E: Liftable<'a, A, Result = ValueExpr>, + E: LiftableLowLevelIL<'a, A, Result = ValueExpr>, { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::$op; @@ -764,7 +874,7 @@ macro_rules! unsized_unary_op_lifter { BNLowLevelILAddExpr(self.handle, $op, 0, 0, expr.index.0 as u64, 0, 0, 0) }; - Expression::new(self, ExpressionIndex(expr_idx)) + LowLevelILExpression::new(self, LowLevelILExpressionIndex(expr_idx)) } }; } @@ -773,7 +883,7 @@ macro_rules! sized_unary_op_lifter { ($name:ident, $op:ident, $result:ty) => { pub fn $name<'a, E>(&'a self, size: usize, expr: E) -> ExpressionBuilder<'a, A, $result> where - E: LiftableWithSize<'a, A>, + E: LiftableLowLevelILWithSize<'a, A>, { use binaryninjacore_sys::BNLowLevelILOperation::$op; @@ -798,7 +908,7 @@ macro_rules! size_changing_unary_op_lifter { ($name:ident, $op:ident, $result:ty) => { pub fn $name<'a, E>(&'a self, size: usize, expr: E) -> ExpressionBuilder<'a, A, $result> where - E: LiftableWithSize<'a, A>, + E: LiftableLowLevelILWithSize<'a, A>, { use binaryninjacore_sys::BNLowLevelILOperation::$op; @@ -828,8 +938,8 @@ macro_rules! binary_op_lifter { right: R, ) -> ExpressionBuilder<'a, A, ValueExpr> where - L: LiftableWithSize<'a, A>, - R: LiftableWithSize<'a, A>, + L: LiftableLowLevelILWithSize<'a, A>, + R: LiftableLowLevelILWithSize<'a, A>, { use binaryninjacore_sys::BNLowLevelILOperation::$op; @@ -861,9 +971,9 @@ macro_rules! binary_op_carry_lifter { carry: C, ) -> ExpressionBuilder<'a, A, ValueExpr> where - L: LiftableWithSize<'a, A>, - R: LiftableWithSize<'a, A>, - C: LiftableWithSize<'a, A>, + L: LiftableLowLevelILWithSize<'a, A>, + R: LiftableLowLevelILWithSize<'a, A>, + C: LiftableLowLevelILWithSize<'a, A>, { use binaryninjacore_sys::BNLowLevelILOperation::$op; @@ -891,16 +1001,16 @@ where A: Architecture, { pub const NO_INPUTS: [ExpressionBuilder<'static, A, ValueExpr>; 0] = []; - pub const NO_OUTPUTS: [Register; 0] = []; + pub const NO_OUTPUTS: [LowLevelILRegister; 0] = []; - pub fn expression<'a, E: Liftable<'a, A>>( + pub fn expression<'a, E: LiftableLowLevelIL<'a, A>>( &'a self, expr: E, - ) -> Expression<'a, A, Mutable, NonSSA, E::Result> { + ) -> LowLevelILExpression<'a, A, Mutable, NonSSA, E::Result> { E::lift(self, expr) } - pub fn instruction<'a, E: Liftable<'a, A>>(&'a self, expr: E) { + pub fn add_instruction<'a, E: LiftableLowLevelIL<'a, A>>(&'a self, expr: E) { let expr = self.expression(expr); unsafe { @@ -909,65 +1019,66 @@ where } } - pub unsafe fn replace_expression<'a, E: Liftable<'a, A>>( + pub unsafe fn replace_expression<'a, E: LiftableLowLevelIL<'a, A>>( &'a self, - replaced_expr_index: ExpressionIndex, + replaced_expr_index: LowLevelILExpressionIndex, replacement: E, - ) { - use binaryninjacore_sys::BNGetLowLevelILExprCount; + ) -> bool { use binaryninjacore_sys::BNReplaceLowLevelILExpr; - - // Return false instead? - if replaced_expr_index.0 >= BNGetLowLevelILExprCount(self.handle) { - panic!( - "bad expr idx used: {} exceeds function bounds", - replaced_expr_index - ); + if replaced_expr_index.0 >= self.expression_count() { + // Invalid expression index, cant replace expression. + return false; } - let expr = self.expression(replacement); BNReplaceLowLevelILExpr(self.handle, replaced_expr_index.0, expr.index.0); + true } pub fn const_int( &self, size: usize, val: u64, - ) -> Expression, ValueExpr> { + ) -> LowLevelILExpression, ValueExpr> { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::LLIL_CONST; let expr_idx = unsafe { BNLowLevelILAddExpr(self.handle, LLIL_CONST, size, 0, val, 0, 0, 0) }; - Expression::new(self, ExpressionIndex(expr_idx)) + LowLevelILExpression::new(self, LowLevelILExpressionIndex(expr_idx)) } pub fn const_ptr_sized( &self, size: usize, val: u64, - ) -> Expression, ValueExpr> { + ) -> LowLevelILExpression, ValueExpr> { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::LLIL_CONST_PTR; let expr_idx = unsafe { BNLowLevelILAddExpr(self.handle, LLIL_CONST_PTR, size, 0, val, 0, 0, 0) }; - Expression::new(self, ExpressionIndex(expr_idx)) + LowLevelILExpression::new(self, LowLevelILExpressionIndex(expr_idx)) } - pub fn const_ptr(&self, val: u64) -> Expression, ValueExpr> { + pub fn const_ptr( + &self, + val: u64, + ) -> LowLevelILExpression, ValueExpr> { self.const_ptr_sized(self.arch().address_size(), val) } - pub fn trap(&self, val: u64) -> Expression, VoidExpr> { + pub fn trap( + &self, + val: u64, + ) -> LowLevelILExpression, VoidExpr> { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::LLIL_TRAP; let expr_idx = unsafe { BNLowLevelILAddExpr(self.handle, LLIL_TRAP, 0, 0, val, 0, 0, 0) }; - Expression::new(self, ExpressionIndex(expr_idx)) + LowLevelILExpression::new(self, LowLevelILExpressionIndex(expr_idx)) } no_arg_lifter!(unimplemented, LLIL_UNIMPL, ValueExpr); @@ -988,9 +1099,9 @@ where cond: C, true_label: &'b mut Label, false_label: &'b mut Label, - ) -> Expression<'a, A, Mutable, NonSSA, VoidExpr> + ) -> LowLevelILExpression<'a, A, Mutable, NonSSA, VoidExpr> where - C: Liftable<'b, A, Result = ValueExpr>, + C: LiftableLowLevelIL<'b, A, Result = ValueExpr>, { use binaryninjacore_sys::BNLowLevelILIf; @@ -1011,14 +1122,14 @@ where *true_label = Label::from(raw_true_label); *false_label = Label::from(raw_false_label); - Expression::new(self, ExpressionIndex(expr_idx)) + LowLevelILExpression::new(self, LowLevelILExpressionIndex(expr_idx)) } // TODO: Wtf are these lifetimes?? pub fn goto<'a: 'b, 'b>( &'a self, label: &'b mut Label, - ) -> Expression<'a, A, Mutable, NonSSA, VoidExpr> { + ) -> LowLevelILExpression<'a, A, Mutable, NonSSA, VoidExpr> { use binaryninjacore_sys::BNLowLevelILGoto; let mut raw_label = BNLowLevelILLabel::from(*label); @@ -1027,14 +1138,14 @@ where // Update the labels after they have been resolved. *label = Label::from(raw_label); - Expression::new(self, ExpressionIndex(expr_idx)) + LowLevelILExpression::new(self, LowLevelILExpressionIndex(expr_idx)) } - pub fn reg>>( + pub fn reg>>( &self, size: usize, reg: R, - ) -> Expression, ValueExpr> { + ) -> LowLevelILExpression, ValueExpr> { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::LLIL_REG; @@ -1044,15 +1155,18 @@ where let expr_idx = unsafe { BNLowLevelILAddExpr(self.handle, LLIL_REG, size, 0, reg.0 as u64, 0, 0, 0) }; - Expression::new(self, ExpressionIndex(expr_idx)) + LowLevelILExpression::new(self, LowLevelILExpressionIndex(expr_idx)) } - pub fn reg_split>, L: Into>>( + pub fn reg_split< + H: Into>, + L: Into>, + >( &self, size: usize, hi_reg: H, lo_reg: L, - ) -> Expression, ValueExpr> { + ) -> LowLevelILExpression, ValueExpr> { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::LLIL_REG_SPLIT; @@ -1073,7 +1187,7 @@ where ) }; - Expression::new(self, ExpressionIndex(expr_idx)) + LowLevelILExpression::new(self, LowLevelILExpressionIndex(expr_idx)) } pub fn set_reg<'a, R, E>( @@ -1083,8 +1197,8 @@ where expr: E, ) -> ExpressionBuilder<'a, A, VoidExpr> where - R: Into>, - E: LiftableWithSize<'a, A>, + R: Into>, + E: LiftableLowLevelILWithSize<'a, A>, { use binaryninjacore_sys::BNLowLevelILOperation::LLIL_SET_REG; @@ -1115,9 +1229,9 @@ where expr: E, ) -> ExpressionBuilder<'a, A, VoidExpr> where - H: Into>, - L: Into>, - E: LiftableWithSize<'a, A>, + H: Into>, + L: Into>, + E: LiftableLowLevelILWithSize<'a, A>, { use binaryninjacore_sys::BNLowLevelILOperation::LLIL_SET_REG_SPLIT; @@ -1141,7 +1255,10 @@ where } } - pub fn flag(&self, flag: A::Flag) -> Expression, ValueExpr> { + pub fn flag( + &self, + flag: A::Flag, + ) -> LowLevelILExpression, ValueExpr> { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG; @@ -1150,13 +1267,13 @@ where BNLowLevelILAddExpr(self.handle, LLIL_FLAG, 0, 0, flag.id().0 as u64, 0, 0, 0) }; - Expression::new(self, ExpressionIndex(expr_idx)) + LowLevelILExpression::new(self, LowLevelILExpressionIndex(expr_idx)) } pub fn flag_cond( &self, cond: FlagCondition, - ) -> Expression, ValueExpr> { + ) -> LowLevelILExpression, ValueExpr> { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG_COND; @@ -1164,13 +1281,13 @@ where let expr_idx = unsafe { BNLowLevelILAddExpr(self.handle, LLIL_FLAG_COND, 0, 0, cond as u64, 0, 0, 0) }; - Expression::new(self, ExpressionIndex(expr_idx)) + LowLevelILExpression::new(self, LowLevelILExpressionIndex(expr_idx)) } pub fn flag_group( &self, group: A::FlagGroup, - ) -> Expression, ValueExpr> { + ) -> LowLevelILExpression, ValueExpr> { use binaryninjacore_sys::BNLowLevelILAddExpr; use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG_GROUP; @@ -1188,7 +1305,7 @@ where ) }; - Expression::new(self, ExpressionIndex(expr_idx)) + LowLevelILExpression::new(self, LowLevelILExpressionIndex(expr_idx)) } pub fn set_flag<'a, E>( @@ -1197,7 +1314,7 @@ where expr: E, ) -> ExpressionBuilder<'a, A, VoidExpr> where - E: LiftableWithSize<'a, A>, + E: LiftableLowLevelILWithSize<'a, A>, { use binaryninjacore_sys::BNLowLevelILOperation::LLIL_SET_FLAG; @@ -1225,7 +1342,7 @@ where pub fn load<'a, E>(&'a self, size: usize, source_mem: E) -> ExpressionBuilder<'a, A, ValueExpr> where - E: Liftable<'a, A, Result = ValueExpr>, + E: LiftableLowLevelIL<'a, A, Result = ValueExpr>, { use binaryninjacore_sys::BNLowLevelILOperation::LLIL_LOAD; @@ -1251,8 +1368,8 @@ where value: V, ) -> ExpressionBuilder<'a, A, VoidExpr> where - D: Liftable<'a, A, Result = ValueExpr>, - V: LiftableWithSize<'a, A>, + D: LiftableLowLevelIL<'a, A, Result = ValueExpr>, + V: LiftableLowLevelILWithSize<'a, A>, { use binaryninjacore_sys::BNLowLevelILOperation::LLIL_STORE; @@ -1279,10 +1396,10 @@ where inputs: PL, ) -> ExpressionBuilder<'a, A, VoidExpr> where - O: Into>, + O: Into>, OL: IntoIterator, I: Into, - P: Liftable<'a, A, Result = ValueExpr>, + P: LiftableLowLevelIL<'a, A, Result = ValueExpr>, PL: IntoIterator, { use binaryninjacore_sys::BNLowLevelILOperation::{LLIL_CALL_PARAM, LLIL_INTRINSIC}; @@ -1445,6 +1562,7 @@ where } } + // TODO: Make this private and then force the updates in the expressions. /// Call this after updating the label through an il operation or via [`Self::mark_label`]. /// /// If you retrieved a label via [`Self::label_for_address`] than you very likely want to use this. @@ -1473,12 +1591,13 @@ where } } +// TODO: Rename to LowLevelILLabel #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Label { pub resolved: bool, // TODO: This expr_ref is not actually a valid one sometimes... // TODO: We should make these non public and only accessible if resolved is true. - pub expr_ref: ExpressionIndex, + pub expr_ref: LowLevelILExpressionIndex, // TODO: If this is 7 this label is not valid. pub operand: usize, } @@ -1497,7 +1616,7 @@ impl From for Label { fn from(value: BNLowLevelILLabel) -> Self { Self { resolved: value.resolved, - expr_ref: ExpressionIndex(value.ref_), + expr_ref: LowLevelILExpressionIndex(value.ref_), operand: value.operand, } } diff --git a/rust/src/llil/operation.rs b/rust/src/lowlevelil/operation.rs similarity index 80% rename from rust/src/llil/operation.rs rename to rust/src/lowlevelil/operation.rs index 71dc6a710..a1cc1a927 100644 --- a/rust/src/llil/operation.rs +++ b/rust/src/lowlevelil/operation.rs @@ -169,29 +169,32 @@ where self.op.size } - pub fn dest_reg(&self) -> Register { + pub fn dest_reg(&self) -> LowLevelILRegister { let raw_id = self.op.operands[0] as u32; if raw_id >= 0x8000_0000 { - Register::Temp(raw_id & 0x7fff_ffff) + LowLevelILRegister::Temp(raw_id & 0x7fff_ffff) } else { self.function .arch() .register_from_id(RegisterId(raw_id)) - .map(Register::ArchReg) + .map(LowLevelILRegister::ArchReg) .unwrap_or_else(|| { log::error!( "got garbage register from LLIL_SET_REG @ 0x{:x}", self.op.address ); - Register::Temp(0) + LowLevelILRegister::Temp(0) }) } } - pub fn source_expr(&self) -> Expression<'func, A, M, F, ValueExpr> { - Expression::new(self.function, ExpressionIndex(self.op.operands[1] as usize)) + pub fn source_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelILExpressionIndex(self.op.operands[1] as usize), + ) } } @@ -224,50 +227,53 @@ where self.op.size } - pub fn dest_reg_high(&self) -> Register { + pub fn dest_reg_high(&self) -> LowLevelILRegister { let raw_id = self.op.operands[0] as u32; if raw_id >= 0x8000_0000 { - Register::Temp(raw_id & 0x7fff_ffff) + LowLevelILRegister::Temp(raw_id & 0x7fff_ffff) } else { self.function .arch() .register_from_id(RegisterId(raw_id)) - .map(Register::ArchReg) + .map(LowLevelILRegister::ArchReg) .unwrap_or_else(|| { log::error!( "got garbage register from LLIL_SET_REG_SPLIT @ 0x{:x}", self.op.address ); - Register::Temp(0) + LowLevelILRegister::Temp(0) }) } } - pub fn dest_reg_low(&self) -> Register { + pub fn dest_reg_low(&self) -> LowLevelILRegister { let raw_id = self.op.operands[1] as u32; if raw_id >= 0x8000_0000 { - Register::Temp(raw_id & 0x7fff_ffff) + LowLevelILRegister::Temp(raw_id & 0x7fff_ffff) } else { self.function .arch() .register_from_id(RegisterId(raw_id)) - .map(Register::ArchReg) + .map(LowLevelILRegister::ArchReg) .unwrap_or_else(|| { log::error!( "got garbage register from LLIL_SET_REG_SPLIT @ 0x{:x}", self.op.address ); - Register::Temp(0) + LowLevelILRegister::Temp(0) }) } } - pub fn source_expr(&self) -> Expression<'func, A, M, F, ValueExpr> { - Expression::new(self.function, ExpressionIndex(self.op.operands[2] as usize)) + pub fn source_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelILExpressionIndex(self.op.operands[2] as usize), + ) } } @@ -306,8 +312,11 @@ where .unwrap() } - pub fn source_expr(&self) -> Expression<'func, A, M, F, ValueExpr> { - Expression::new(self.function, ExpressionIndex(self.op.operands[1] as usize)) + pub fn source_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelILExpressionIndex(self.op.operands[1] as usize), + ) } } @@ -339,8 +348,11 @@ where self.op.size } - pub fn source_mem_expr(&self) -> Expression<'func, A, M, F, ValueExpr> { - Expression::new(self.function, ExpressionIndex(self.op.operands[0] as usize)) + pub fn source_mem_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelILExpressionIndex(self.op.operands[0] as usize), + ) } } @@ -372,12 +384,18 @@ where self.op.size } - pub fn dest_mem_expr(&self) -> Expression<'func, A, M, F, ValueExpr> { - Expression::new(self.function, ExpressionIndex(self.op.operands[0] as usize)) + pub fn dest_mem_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelILExpressionIndex(self.op.operands[0] as usize), + ) } - pub fn source_expr(&self) -> Expression<'func, A, M, F, ValueExpr> { - Expression::new(self.function, ExpressionIndex(self.op.operands[1] as usize)) + pub fn source_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelILExpressionIndex(self.op.operands[1] as usize), + ) } } @@ -410,23 +428,23 @@ where self.op.size } - pub fn source_reg(&self) -> Register { + pub fn source_reg(&self) -> LowLevelILRegister { let raw_id = self.op.operands[0] as u32; if raw_id >= 0x8000_0000 { - Register::Temp(raw_id & 0x7fff_ffff) + LowLevelILRegister::Temp(raw_id & 0x7fff_ffff) } else { self.function .arch() .register_from_id(RegisterId(raw_id)) - .map(Register::ArchReg) + .map(LowLevelILRegister::ArchReg) .unwrap_or_else(|| { log::error!( "got garbage register from LLIL_REG @ 0x{:x}", self.op.address ); - Register::Temp(0) + LowLevelILRegister::Temp(0) }) } } @@ -460,44 +478,44 @@ where self.op.size } - pub fn low_reg(&self) -> Register { + pub fn low_reg(&self) -> LowLevelILRegister { let raw_id = self.op.operands[0] as u32; if raw_id >= 0x8000_0000 { - Register::Temp(raw_id & 0x7fff_ffff) + LowLevelILRegister::Temp(raw_id & 0x7fff_ffff) } else { self.function .arch() .register_from_id(RegisterId(raw_id)) - .map(Register::ArchReg) + .map(LowLevelILRegister::ArchReg) .unwrap_or_else(|| { log::error!( "got garbage register from LLIL_REG @ 0x{:x}", self.op.address ); - Register::Temp(0) + LowLevelILRegister::Temp(0) }) } } - pub fn high_reg(&self) -> Register { + pub fn high_reg(&self) -> LowLevelILRegister { let raw_id = self.op.operands[1] as u32; if raw_id >= 0x8000_0000 { - Register::Temp(raw_id & 0x7fff_ffff) + LowLevelILRegister::Temp(raw_id & 0x7fff_ffff) } else { self.function .arch() .register_from_id(RegisterId(raw_id)) - .map(Register::ArchReg) + .map(LowLevelILRegister::ArchReg) .unwrap_or_else(|| { log::error!( "got garbage register from LLIL_REG @ 0x{:x}", self.op.address ); - Register::Temp(0) + LowLevelILRegister::Temp(0) }) } } @@ -556,8 +574,11 @@ where M: FunctionMutability, F: FunctionForm, { - pub fn target(&self) -> Expression<'func, A, M, F, ValueExpr> { - Expression::new(self.function, ExpressionIndex(self.op.operands[0] as usize)) + pub fn target(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelILExpressionIndex(self.op.operands[0] as usize), + ) } } @@ -613,11 +634,14 @@ where M: FunctionMutability, F: FunctionForm, { - pub fn target(&self) -> Expression<'func, A, M, F, ValueExpr> { - Expression::new(self.function, ExpressionIndex(self.op.operands[0] as usize)) + pub fn target(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelILExpressionIndex(self.op.operands[0] as usize), + ) } - pub fn target_list(&self) -> BTreeMap { + pub fn target_list(&self) -> BTreeMap { let mut result = BTreeMap::new(); let count = self.op.operands[1] as usize / 2; let mut list = TargetListIter { @@ -630,7 +654,7 @@ where for _ in 0..count { let value = list.next(); - let target = InstructionIndex(list.next() as usize); + let target = LowLevelInstructionIndex(list.next() as usize); result.insert(value, target); } @@ -661,8 +685,11 @@ where M: FunctionMutability, F: FunctionForm, { - pub fn target(&self) -> Expression<'func, A, M, F, ValueExpr> { - Expression::new(self.function, ExpressionIndex(self.op.operands[0] as usize)) + pub fn target(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelILExpressionIndex(self.op.operands[0] as usize), + ) } pub fn stack_adjust(&self) -> Option { @@ -699,8 +726,11 @@ where M: FunctionMutability, F: FunctionForm, { - pub fn target(&self) -> Expression<'func, A, M, F, ValueExpr> { - Expression::new(self.function, ExpressionIndex(self.op.operands[0] as usize)) + pub fn target(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelILExpressionIndex(self.op.operands[0] as usize), + ) } } @@ -726,21 +756,24 @@ where M: FunctionMutability, F: FunctionForm, { - pub fn condition(&self) -> Expression<'func, A, M, F, ValueExpr> { - Expression::new(self.function, ExpressionIndex(self.op.operands[0] as usize)) + pub fn condition(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelILExpressionIndex(self.op.operands[0] as usize), + ) } - pub fn true_target(&self) -> Instruction<'func, A, M, F> { - Instruction::new( + pub fn true_target(&self) -> LowLevelILInstruction<'func, A, M, F> { + LowLevelILInstruction::new( self.function, - InstructionIndex(self.op.operands[1] as usize), + LowLevelInstructionIndex(self.op.operands[1] as usize), ) } - pub fn false_target(&self) -> Instruction<'func, A, M, F> { - Instruction::new( + pub fn false_target(&self) -> LowLevelILInstruction<'func, A, M, F> { + LowLevelILInstruction::new( self.function, - InstructionIndex(self.op.operands[2] as usize), + LowLevelInstructionIndex(self.op.operands[2] as usize), ) } } @@ -769,10 +802,10 @@ where M: FunctionMutability, F: FunctionForm, { - pub fn target(&self) -> Instruction<'func, A, M, F> { - Instruction::new( + pub fn target(&self) -> LowLevelILInstruction<'func, A, M, F> { + LowLevelILInstruction::new( self.function, - InstructionIndex(self.op.operands[0] as usize), + LowLevelInstructionIndex(self.op.operands[0] as usize), ) } } @@ -1047,12 +1080,18 @@ where self.op.size } - pub fn left(&self) -> Expression<'func, A, M, F, ValueExpr> { - Expression::new(self.function, ExpressionIndex(self.op.operands[0] as usize)) + pub fn left(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelILExpressionIndex(self.op.operands[0] as usize), + ) } - pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> { - Expression::new(self.function, ExpressionIndex(self.op.operands[1] as usize)) + pub fn right(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelILExpressionIndex(self.op.operands[1] as usize), + ) } } @@ -1084,16 +1123,25 @@ where self.op.size } - pub fn left(&self) -> Expression<'func, A, M, F, ValueExpr> { - Expression::new(self.function, ExpressionIndex(self.op.operands[0] as usize)) + pub fn left(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelILExpressionIndex(self.op.operands[0] as usize), + ) } - pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> { - Expression::new(self.function, ExpressionIndex(self.op.operands[1] as usize)) + pub fn right(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelILExpressionIndex(self.op.operands[1] as usize), + ) } - pub fn carry(&self) -> Expression<'func, A, M, F, ValueExpr> { - Expression::new(self.function, ExpressionIndex(self.op.operands[2] as usize)) + pub fn carry(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelILExpressionIndex(self.op.operands[2] as usize), + ) } } @@ -1126,17 +1174,26 @@ where self.op.size } - pub fn high(&self) -> Expression<'func, A, M, F, ValueExpr> { - Expression::new(self.function, ExpressionIndex(self.op.operands[0] as usize)) + pub fn high(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelILExpressionIndex(self.op.operands[0] as usize), + ) } - pub fn low(&self) -> Expression<'func, A, M, F, ValueExpr> { - Expression::new(self.function, ExpressionIndex(self.op.operands[1] as usize)) + pub fn low(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelILExpressionIndex(self.op.operands[1] as usize), + ) } // TODO: I don't think this actually exists? - pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> { - Expression::new(self.function, ExpressionIndex(self.op.operands[2] as usize)) + pub fn right(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelILExpressionIndex(self.op.operands[2] as usize), + ) } } @@ -1171,8 +1228,11 @@ where self.op.size } - pub fn operand(&self) -> Expression<'func, A, M, F, ValueExpr> { - Expression::new(self.function, ExpressionIndex(self.op.operands[0] as usize)) + pub fn operand(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelILExpressionIndex(self.op.operands[0] as usize), + ) } } @@ -1203,12 +1263,18 @@ where self.op.size } - pub fn left(&self) -> Expression<'func, A, M, F, ValueExpr> { - Expression::new(self.function, ExpressionIndex(self.op.operands[0] as usize)) + pub fn left(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelILExpressionIndex(self.op.operands[0] as usize), + ) } - pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> { - Expression::new(self.function, ExpressionIndex(self.op.operands[1] as usize)) + pub fn right(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelILExpressionIndex(self.op.operands[1] as usize), + ) } } @@ -1240,8 +1306,11 @@ where self.op.size } - pub fn mem_expr(&self) -> Expression<'func, A, M, F, ValueExpr> { - Expression::new(self.function, ExpressionIndex(self.op.operands[0] as usize)) + pub fn mem_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelILExpressionIndex(self.op.operands[0] as usize), + ) } } diff --git a/rust/src/mlil/block.rs b/rust/src/mlil/block.rs index 6c4415a26..219fa0a4a 100644 --- a/rust/src/mlil/block.rs +++ b/rust/src/mlil/block.rs @@ -2,7 +2,7 @@ use crate::basicblock::{BasicBlock, BlockContext}; use crate::rc::Ref; use std::ops::Range; -use super::{MediumLevelILFunction, MediumLevelILInstruction}; +use super::{MediumLevelILFunction, MediumLevelILInstruction, MediumLevelInstructionIndex}; pub struct MediumLevelILBlock { pub(crate) function: Ref, @@ -10,17 +10,21 @@ pub struct MediumLevelILBlock { impl BlockContext for MediumLevelILBlock { type Instruction = MediumLevelILInstruction; + type InstructionIndex = MediumLevelInstructionIndex; type Iter = MediumLevelILBlockIter; fn start(&self, block: &BasicBlock) -> MediumLevelILInstruction { + // TODO: instruction_from_index says that it is not mapped and will do the call + // TODO: What if this IS already MAPPED!?!?!? self.function - .instruction_from_instruction_idx(block.raw_start() as usize) + .instruction_from_index(block.start_index()) + .unwrap() } fn iter(&self, block: &BasicBlock) -> MediumLevelILBlockIter { MediumLevelILBlockIter { function: self.function.to_owned(), - range: block.raw_start()..block.raw_end(), + range: block.start_index().0..block.end_index().0, } } } @@ -42,7 +46,7 @@ impl Clone for MediumLevelILBlock { pub struct MediumLevelILBlockIter { function: Ref, - range: Range, + range: Range, } impl Iterator for MediumLevelILBlockIter { @@ -51,6 +55,8 @@ impl Iterator for MediumLevelILBlockIter { fn next(&mut self) -> Option { self.range .next() - .map(|i| self.function.instruction_from_instruction_idx(i as usize)) + .map(|i| MediumLevelInstructionIndex(i)) + // TODO: What if this is already mapped!?!?!? we will map twice!?!?!? + .and_then(|i| self.function.instruction_from_index(i)) } } diff --git a/rust/src/mlil/function.rs b/rust/src/mlil/function.rs index 4affd3300..aea2ffbb3 100644 --- a/rust/src/mlil/function.rs +++ b/rust/src/mlil/function.rs @@ -3,7 +3,7 @@ use std::ffi::c_char; use std::fmt::{Debug, Formatter}; use std::hash::{Hash, Hasher}; -use super::{MediumLevelILBlock, MediumLevelILInstruction, MediumLevelILLiftedInstruction}; +use super::{MediumLevelILBlock, MediumLevelILInstruction, MediumLevelInstructionIndex}; use crate::architecture::CoreArchitecture; use crate::basicblock::BasicBlock; use crate::confidence::Conf; @@ -34,51 +34,60 @@ impl MediumLevelILFunction { } pub fn instruction_at>(&self, loc: L) -> Option { - let loc: Location = loc.into(); - let arch_handle = loc.arch.unwrap(); - - let expr_idx = unsafe { - BNMediumLevelILGetInstructionStart(self.handle, arch_handle.handle, loc.addr) - }; + Some(MediumLevelILInstruction::new( + self.to_owned(), + self.instruction_index_at(loc)?, + )) + } - if expr_idx >= self.instruction_count() { + pub fn instruction_index_at>( + &self, + loc: L, + ) -> Option { + let loc: Location = loc.into(); + let arch = loc + .arch + .map(|a| a.handle) + .unwrap_or_else(std::ptr::null_mut); + let instr_idx = unsafe { BNMediumLevelILGetInstructionStart(self.handle, arch, loc.addr) }; + // `instr_idx` will equal self.instruction_count() if the instruction is not valid. + if instr_idx >= self.instruction_count() { None } else { - Some(MediumLevelILInstruction::new(self.to_owned(), expr_idx)) + Some(MediumLevelInstructionIndex(instr_idx)) } } - pub fn instruction_from_idx(&self, expr_idx: usize) -> MediumLevelILInstruction { - MediumLevelILInstruction::new(self.to_owned(), expr_idx) - } - - // TODO: Possibly remove this function. - pub fn lifted_instruction_from_idx(&self, expr_idx: usize) -> MediumLevelILLiftedInstruction { - self.instruction_from_idx(expr_idx).lift() - } - - // TODO: This naming is not clear, document the difference between: - // TODO: `instruction_from_instruction_idx` and `instruction_from_idx`. - pub fn instruction_from_instruction_idx(&self, instr_idx: usize) -> MediumLevelILInstruction { - self.instruction_from_idx(unsafe { - BNGetMediumLevelILIndexForInstruction(self.handle, instr_idx) - }) + // TODO: When to use this? + pub fn instruction_from_index( + &self, + index: MediumLevelInstructionIndex, + ) -> Option { + let mapped_index = unsafe { BNGetMediumLevelILIndexForInstruction(self.handle, index.0) }; + self.instruction_from_mapped_index(MediumLevelInstructionIndex(mapped_index)) } - // TODO: This naming is not clear, document the difference between: - // TODO: `lifted_instruction_from_instruction_idx` and `lifted_instruction_from_idx`. - // TODO: Possibly remove this function. - pub fn lifted_instruction_from_instruction_idx( + // TODO: What is a mapped index? + // TODO: When to use this? + pub fn instruction_from_mapped_index( &self, - instr_idx: usize, - ) -> MediumLevelILLiftedInstruction { - self.instruction_from_instruction_idx(instr_idx).lift() + mapped_index: MediumLevelInstructionIndex, + ) -> Option { + if mapped_index.0 >= self.instruction_count() { + None + } else { + Some(MediumLevelILInstruction::new(self.to_owned(), mapped_index)) + } } pub fn instruction_count(&self) -> usize { unsafe { BNGetMediumLevelILInstructionCount(self.handle) } } + pub fn expression_count(&self) -> usize { + unsafe { BNGetMediumLevelILExprCount(self.handle) } + } + pub fn ssa_form(&self) -> MediumLevelILFunction { let ssa = unsafe { BNGetMediumLevelILSSAForm(self.handle) }; assert!(!ssa.is_null()); @@ -98,7 +107,6 @@ impl MediumLevelILFunction { let context = MediumLevelILBlock { function: self.to_owned(), }; - unsafe { Array::new(blocks, count, context) } } @@ -389,17 +397,19 @@ impl MediumLevelILFunction { unsafe { BNMediumLevelILSetCurrentAddress(self.handle, arch, location.addr) } } - /// Returns the BasicBlock at the given MLIL `instruction`. - pub fn basic_block_containing( + /// Returns the [`BasicBlock`] at the given instruction `index`. + /// + /// You can also retrieve this using [`MediumLevelILInstruction::basic_block`]. + pub fn basic_block_containing_index( &self, - instruction: &MediumLevelILInstruction, + index: MediumLevelInstructionIndex, ) -> Option>> { - let index = instruction.index; let context = MediumLevelILBlock { function: self.to_owned(), }; + // TODO: If we can guarantee self.index is valid we can omit the wrapped Option. let basic_block_ptr = - unsafe { BNGetMediumLevelILBasicBlockForInstruction(self.handle, index) }; + unsafe { BNGetMediumLevelILBasicBlockForInstruction(self.handle, index.0) }; match basic_block_ptr.is_null() { false => Some(unsafe { BasicBlock::ref_from_raw(basic_block_ptr, context) }), true => None, @@ -456,14 +466,14 @@ impl MediumLevelILFunction { let result = unsafe { BNGetMediumLevelILSSAVarDefinition(self.handle, &raw_var, ssa_variable.version) }; - (result < self.instruction_count()) - .then(|| MediumLevelILInstruction::new(self.to_owned(), result)) + // TODO: Is this mapped? + self.instruction_from_mapped_index(MediumLevelInstructionIndex(result)) } pub fn ssa_memory_definition(&self, version: usize) -> Option { let result = unsafe { BNGetMediumLevelILSSAMemoryDefinition(self.handle, version) }; - (result < self.instruction_count()) - .then(|| MediumLevelILInstruction::new(self.to_owned(), result)) + // TODO: Is this mapped? + self.instruction_from_mapped_index(MediumLevelInstructionIndex(result)) } /// Gets all the instructions that use the given SSA variable. diff --git a/rust/src/mlil/instruction.rs b/rust/src/mlil/instruction.rs index f022c6eee..f54bb1fa9 100644 --- a/rust/src/mlil/instruction.rs +++ b/rust/src/mlil/instruction.rs @@ -1,9 +1,12 @@ use binaryninjacore_sys::*; +use std::fmt; +use std::fmt::{Display, Formatter}; use super::lift::*; use super::operation::*; -use super::MediumLevelILFunction; +use super::{MediumLevelILBlock, MediumLevelILFunction}; use crate::architecture::{CoreIntrinsic, IntrinsicId}; +use crate::basicblock::BasicBlock; use crate::confidence::Conf; use crate::disassembly::InstructionTextToken; use crate::operand_iter::OperandIter; @@ -12,15 +15,50 @@ use crate::types::Type; use crate::variable::{ConstantData, PossibleValueSet, RegisterValue, SSAVariable, Variable}; use crate::{DataFlowQueryOption, ILBranchDependence}; +#[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(Clone)] pub struct MediumLevelILInstruction { pub function: Ref, pub address: u64, - pub index: usize, + pub index: MediumLevelInstructionIndex, pub size: usize, pub kind: MediumLevelILInstructionKind, } +impl MediumLevelILInstruction { + /// 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.index) + } +} + #[derive(Copy, Clone)] pub enum MediumLevelILInstructionKind { Nop, @@ -167,9 +205,12 @@ impl core::fmt::Debug for MediumLevelILInstruction { } impl MediumLevelILInstruction { - pub(crate) fn new(function: Ref, index: usize) -> Self { + pub(crate) fn new( + function: Ref, + index: MediumLevelInstructionIndex, + ) -> Self { // TODO: If op.sourceOperation == BN_INVALID_OPERAND && op.operation == MLIL_NOP return None - let op = unsafe { BNGetMediumLevelILByIndex(function.handle, index) }; + let op = unsafe { BNGetMediumLevelILByIndex(function.handle, index.0) }; use BNMediumLevelILOperation::*; use MediumLevelILInstructionKind as Op; let kind = match op.operation { @@ -1054,7 +1095,7 @@ impl MediumLevelILInstruction { BNGetMediumLevelILExprText( self.function.handle, self.function.function().arch().handle, - self.index, + self.index.0, &mut tokens, &mut count, core::ptr::null_mut(), @@ -1065,7 +1106,7 @@ impl MediumLevelILInstruction { /// Value of expression if constant or a known value pub fn value(&self) -> RegisterValue { - unsafe { BNGetMediumLevelILExprValue(self.function.handle, self.index) }.into() + unsafe { BNGetMediumLevelILExprValue(self.function.handle, self.index.0) }.into() } /// Possible values of expression using path-sensitive static data flow analysis @@ -1078,7 +1119,7 @@ impl MediumLevelILInstruction { let value = unsafe { BNGetMediumLevelILPossibleExprValues( self.function.handle, - self.index, + self.index.0, options.as_ptr() as *mut _, options.len(), ) @@ -1101,7 +1142,7 @@ impl MediumLevelILInstruction { self.function.handle, &raw_var, ssa_var.version, - self.index, + self.index.0, options.as_ptr() as *mut _, options.len(), ) @@ -1116,7 +1157,7 @@ impl MediumLevelILInstruction { BNGetMediumLevelILSSAVarVersionAtILInstruction( self.function.handle, &raw_var, - self.index, + self.index.0, ) }; SSAVariable::new(var, version) @@ -1126,7 +1167,7 @@ impl MediumLevelILInstruction { pub fn branch_dependencies(&self) -> Array { let mut count = 0; let deps = unsafe { - BNGetAllMediumLevelILBranchDependence(self.function.handle, self.index, &mut count) + BNGetAllMediumLevelILBranchDependence(self.function.handle, self.index.0, &mut count) }; assert!(!deps.is_null()); unsafe { Array::new(deps, count, self.function.clone()) } @@ -1134,7 +1175,11 @@ impl MediumLevelILInstruction { pub fn branch_dependence_at(&self, instruction: MediumLevelILInstruction) -> BranchDependence { let deps = unsafe { - BNGetMediumLevelILBranchDependence(self.function.handle, self.index, instruction.index) + BNGetMediumLevelILBranchDependence( + self.function.handle, + self.index.0, + instruction.index.0, + ) }; BranchDependence { instruction, @@ -1145,13 +1190,13 @@ impl MediumLevelILInstruction { /// 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) + BNGetMediumLevelILSSAMemoryVersionAtILInstruction(self.function.handle, self.index.0) } } /// Type of expression pub fn expr_type(&self) -> Option>> { - let result = unsafe { BNGetMediumLevelILExprType(self.function.handle, self.index) }; + let result = unsafe { BNGetMediumLevelILExprType(self.function.handle, self.index.0) }; (!result.type_.is_null()).then(|| { Conf::new( unsafe { Type::ref_from_raw(result.type_) }, @@ -1172,7 +1217,7 @@ impl MediumLevelILInstruction { type_: type_.contents.handle, confidence: type_.confidence, }; - unsafe { BNSetMediumLevelILExprType(self.function.handle, self.index, &mut type_raw) } + unsafe { BNSetMediumLevelILExprType(self.function.handle, self.index.0, &mut type_raw) } } pub fn variable_for_register(&self, reg_id: u32) -> Variable { @@ -1180,7 +1225,7 @@ impl MediumLevelILInstruction { BNGetMediumLevelILVariableForRegisterAtInstruction( self.function.handle, reg_id, - self.index, + self.index.0, ) }; Variable::from(result) @@ -1191,7 +1236,7 @@ impl MediumLevelILInstruction { BNGetMediumLevelILVariableForFlagAtInstruction( self.function.handle, flag_id, - self.index, + self.index.0, ) }; Variable::from(result) @@ -1202,7 +1247,7 @@ impl MediumLevelILInstruction { BNGetMediumLevelILVariableForStackLocationAtInstruction( self.function.handle, offset, - self.index, + self.index.0, ) }; Variable::from(result) @@ -1210,7 +1255,7 @@ impl MediumLevelILInstruction { pub fn register_value(&self, reg_id: u32) -> RegisterValue { unsafe { - BNGetMediumLevelILRegisterValueAtInstruction(self.function.handle, reg_id, self.index) + BNGetMediumLevelILRegisterValueAtInstruction(self.function.handle, reg_id, self.index.0) } .into() } @@ -1220,7 +1265,7 @@ impl MediumLevelILInstruction { BNGetMediumLevelILRegisterValueAfterInstruction( self.function.handle, reg_id, - self.index, + self.index.0, ) } .into() @@ -1239,7 +1284,7 @@ impl MediumLevelILInstruction { BNGetMediumLevelILPossibleRegisterValuesAtInstruction( self.function.handle, reg_id, - self.index, + self.index.0, options.as_ptr() as *mut _, options.len(), ) @@ -1260,7 +1305,7 @@ impl MediumLevelILInstruction { BNGetMediumLevelILPossibleRegisterValuesAfterInstruction( self.function.handle, reg_id, - self.index, + self.index.0, options.as_ptr() as *mut _, options.len(), ) @@ -1270,14 +1315,14 @@ impl MediumLevelILInstruction { pub fn flag_value(&self, flag_id: u32) -> RegisterValue { unsafe { - BNGetMediumLevelILFlagValueAtInstruction(self.function.handle, flag_id, self.index) + BNGetMediumLevelILFlagValueAtInstruction(self.function.handle, flag_id, self.index.0) } .into() } pub fn flag_value_after(&self, flag_id: u32) -> RegisterValue { unsafe { - BNGetMediumLevelILFlagValueAfterInstruction(self.function.handle, flag_id, self.index) + BNGetMediumLevelILFlagValueAfterInstruction(self.function.handle, flag_id, self.index.0) } .into() } @@ -1295,7 +1340,7 @@ impl MediumLevelILInstruction { BNGetMediumLevelILPossibleFlagValuesAtInstruction( self.function.handle, flag_id, - self.index, + self.index.0, options.as_ptr() as *mut _, options.len(), ) @@ -1312,7 +1357,7 @@ impl MediumLevelILInstruction { BNGetMediumLevelILPossibleFlagValuesAfterInstruction( self.function.handle, flag_id, - self.index, + self.index.0, options.as_ptr() as *mut _, options.len(), ) @@ -1326,7 +1371,7 @@ impl MediumLevelILInstruction { self.function.handle, offset, size, - self.index, + self.index.0, ) } .into() @@ -1338,7 +1383,7 @@ impl MediumLevelILInstruction { self.function.handle, offset, size, - self.index, + self.index.0, ) } .into() @@ -1355,7 +1400,7 @@ impl MediumLevelILInstruction { self.function.handle, offset, size, - self.index, + self.index.0, options.as_ptr() as *mut _, options.len(), ) @@ -1374,7 +1419,7 @@ impl MediumLevelILInstruction { self.function.handle, offset, size, - self.index, + self.index.0, options.as_ptr() as *mut _, options.len(), ) @@ -1393,7 +1438,7 @@ impl MediumLevelILInstruction { BNGetDefaultIndexForMediumLevelILVariableDefinition( self.function.handle, &raw_var, - self.index, + self.index.0, ) }; Variable::new(var.ty, index, var.storage) @@ -1406,7 +1451,15 @@ impl MediumLevelILInstruction { } fn lift_operand(&self, expr_idx: usize) -> Box { - Box::new(self.function.lifted_instruction_from_idx(expr_idx)) + // TODO: UGH, if your gonna call it expr_idx, call the instruction and expression!!!!! + // TODO: We dont even need to say instruction in the type! + // TODO: IF you want to have an instruction type, there needs to be a seperate expression type + // TODO: See the lowlevelil module. + let expr_idx_is_really_instr_idx = MediumLevelInstructionIndex(expr_idx); + let operand_instr = self + .function + .instruction_from_mapped_index(expr_idx_is_really_instr_idx); + Box::new(operand_instr.unwrap().lift()) } fn lift_binary_op(&self, op: BinaryOp) -> LiftedBinaryOp { @@ -1490,7 +1543,11 @@ unsafe impl CoreArrayProviderInner for MediumLevelILInstruction { } unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { - context.instruction_from_idx(*raw) + // TODO: This needs to be tested!!!! + // TODO: What if this does not need to be mapped!!!! + context + .instruction_from_index(MediumLevelInstructionIndex(*raw)) + .unwrap() } } @@ -1567,7 +1624,10 @@ unsafe impl CoreArrayProviderInner for BranchDependence { unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { Self { - instruction: MediumLevelILInstruction::new(context.clone(), raw.branch), + instruction: MediumLevelILInstruction::new( + context.clone(), + MediumLevelInstructionIndex(raw.branch), + ), dependence: raw.dependence, } } diff --git a/rust/src/mlil/lift.rs b/rust/src/mlil/lift.rs index 8de5e57e9..031775380 100644 --- a/rust/src/mlil/lift.rs +++ b/rust/src/mlil/lift.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use super::operation::*; -use super::MediumLevelILFunction; +use super::{MediumLevelILFunction, MediumLevelInstructionIndex}; use crate::architecture::CoreIntrinsic; use crate::rc::Ref; use crate::variable::{ConstantData, SSAVariable, Variable}; @@ -26,7 +26,7 @@ pub enum MediumLevelILLiftedOperand { pub struct MediumLevelILLiftedInstruction { pub function: Ref, pub address: u64, - pub index: usize, + pub index: MediumLevelInstructionIndex, pub size: usize, pub kind: MediumLevelILLiftedInstructionKind, } diff --git a/rust/src/operand_iter.rs b/rust/src/operand_iter.rs index d2db9a6b9..115cb5368 100644 --- a/rust/src/operand_iter.rs +++ b/rust/src/operand_iter.rs @@ -3,27 +3,33 @@ use binaryninjacore_sys::BNGetMediumLevelILByIndex; use binaryninjacore_sys::BNHighLevelILOperation; use binaryninjacore_sys::BNMediumLevelILOperation; -use crate::hlil::{HighLevelILFunction, HighLevelILInstruction}; -use crate::mlil::{MediumLevelILFunction, MediumLevelILInstruction}; +use crate::hlil::{HighLevelILFunction, HighLevelILInstruction, HighLevelInstructionIndex}; +use crate::mlil::{MediumLevelILFunction, MediumLevelILInstruction, MediumLevelInstructionIndex}; use crate::rc::{Ref, RefCountable}; use crate::variable::{SSAVariable, Variable}; +// TODO: This code needs to go away IMO, we have the facilities to do this for each IL already! + pub trait ILFunction { type Instruction; + type InstructionIndex: From; - fn il_instruction_from_idx(&self, expr_idx: usize) -> Self::Instruction; - fn operands_from_idx(&self, expr_idx: usize) -> [u64; 5]; + fn il_instruction_from_index(&self, instr_index: Self::InstructionIndex) -> Self::Instruction; + fn operands_from_index(&self, instr_index: Self::InstructionIndex) -> [u64; 5]; } impl ILFunction for MediumLevelILFunction { type Instruction = MediumLevelILInstruction; + type InstructionIndex = MediumLevelInstructionIndex; - fn il_instruction_from_idx(&self, expr_idx: usize) -> Self::Instruction { - self.instruction_from_idx(expr_idx) + fn il_instruction_from_index(&self, instr_index: Self::InstructionIndex) -> Self::Instruction { + self.instruction_from_index(instr_index) + .expect("Invalid instruction index") } - fn operands_from_idx(&self, expr_idx: usize) -> [u64; 5] { - let node = unsafe { BNGetMediumLevelILByIndex(self.handle, expr_idx) }; + fn operands_from_index(&self, instr_index: Self::InstructionIndex) -> [u64; 5] { + // TODO: WTF?!?!?! + let node = unsafe { BNGetMediumLevelILByIndex(self.handle, instr_index.0) }; assert_eq!(node.operation, BNMediumLevelILOperation::MLIL_UNDEF); node.operands } @@ -31,13 +37,15 @@ impl ILFunction for MediumLevelILFunction { impl ILFunction for HighLevelILFunction { type Instruction = HighLevelILInstruction; + type InstructionIndex = HighLevelInstructionIndex; - fn il_instruction_from_idx(&self, expr_idx: usize) -> Self::Instruction { - self.instruction_from_idx(expr_idx) + fn il_instruction_from_index(&self, instr_index: Self::InstructionIndex) -> Self::Instruction { + self.instruction_from_index(instr_index) + .expect("Invalid instruction index") } - fn operands_from_idx(&self, expr_idx: usize) -> [u64; 5] { - let node = unsafe { BNGetHighLevelILByIndex(self.handle, expr_idx, self.full_ast) }; + fn operands_from_index(&self, instr_index: Self::InstructionIndex) -> [u64; 5] { + let node = unsafe { BNGetHighLevelILByIndex(self.handle, instr_index.0, self.full_ast) }; assert_eq!(node.operation, BNHighLevelILOperation::HLIL_UNDEF); node.operands } @@ -90,7 +98,8 @@ impl Iterator for OperandIter { } else { // Will short-circuit and return `None` once iter is exhausted let iter_idx = self.next_iter_idx?; - let operands = self.function.operands_from_idx(iter_idx); + let iter_idx = F::InstructionIndex::from(iter_idx as u64); + let operands = self.function.operands_from_index(iter_idx); let next = if self.remaining > 4 { self.next_iter_idx = Some(operands[4] as usize); @@ -179,7 +188,8 @@ impl Iterator for OperandExprIter { fn next(&mut self) -> Option { self.0 .next() - .map(|idx| self.0.function.il_instruction_from_idx(idx as usize)) + .map(|i| F::InstructionIndex::from(i)) + .map(|idx| self.0.function.il_instruction_from_index(idx)) } } diff --git a/rust/src/relocation.rs b/rust/src/relocation.rs index 38325ddd9..9bc52d23d 100644 --- a/rust/src/relocation.rs +++ b/rust/src/relocation.rs @@ -1,9 +1,9 @@ +use crate::lowlevelil::RegularLowLevelILFunction; use crate::rc::Guard; use crate::string::BnStrCompatible; use crate::{ architecture::CoreArchitecture, binaryview::BinaryView, - llil, rc::{CoreArrayProvider, CoreArrayProviderInner, Ref, RefCountable}, symbol::Symbol, }; @@ -168,6 +168,7 @@ impl RelocationInfo { target: self.target, dataRelocation: self.data_relocation, relocationDataCache: self.relocation_data_cache, + // TODO: How to handle this? prev: core::ptr::null_mut(), next: core::ptr::null_mut(), } @@ -180,6 +181,9 @@ impl Default for RelocationInfo { } } +// TODO: There is NO freeing of the relocation +// TODO: A quick look it seem that the relocation ptr is always not owned so this is _fine_ +// TODO: REALLY need to come back to this at some point. pub struct Relocation(*mut BNRelocation); impl Relocation { @@ -260,7 +264,8 @@ pub trait RelocationHandler: 'static + Sized + AsRef { &self, _data: &[u8], _addr: u64, - _il: &llil::RegularFunction, + // TODO: Are we sure this is not a liftedilfunction? + _il: &RegularLowLevelILFunction, _reloc: &Relocation, ) -> RelocationOperand { RelocationOperand::AutocoerceExternPtr @@ -358,7 +363,7 @@ impl RelocationHandler for CoreRelocationHandler { &self, data: &[u8], addr: u64, - il: &llil::RegularFunction, + il: &RegularLowLevelILFunction, reloc: &Relocation, ) -> RelocationOperand { unsafe { @@ -491,8 +496,7 @@ where return RelocationOperand::Invalid.into(); } let arch = unsafe { CoreArchitecture::from_raw(arch) }; - - let il = unsafe { llil::RegularFunction::from_raw(arch, il) }; + let il = unsafe { RegularLowLevelILFunction::from_raw(arch, il) }; custom_handler .get_operand_for_external_relocation(data, addr, &il, &reloc) diff --git a/rust/src/workflow.rs b/rust/src/workflow.rs index 6c0a3d821..16eccf09d 100644 --- a/rust/src/workflow.rs +++ b/rust/src/workflow.rs @@ -7,10 +7,12 @@ use crate::basicblock::BasicBlock; use crate::binaryview::BinaryView; use crate::flowgraph::FlowGraph; use crate::function::{Function, NativeBlock}; -use crate::llil::{self, FunctionForm, Mutable}; +use crate::hlil::HighLevelILFunction; +use crate::lowlevelil::function::{LowLevelILFunction, Mutable, NonSSA, NonSSAVariant}; +use crate::lowlevelil::MutableLiftedILFunction; +use crate::mlil::MediumLevelILFunction; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; use crate::string::{BnStrCompatible, BnString}; -use crate::{hlil, mlil}; #[repr(transparent)] /// The AnalysisContext struct is used to represent the current state of @@ -37,75 +39,76 @@ impl AnalysisContext { unsafe { BinaryView::ref_from_raw(result) } } - /// Function for the current AnalysisContext + /// [`Function`] for the current AnalysisContext pub fn function(&self) -> Ref { let result = unsafe { BNAnalysisContextGetFunction(self.handle.as_ptr()) }; assert!(!result.is_null()); unsafe { Function::ref_from_raw(result) } } - /// LowLevelILFunction used to represent Low Level IL - pub unsafe fn lifted_il_function( + /// [`LowLevelILFunction`] used to represent Low Level IL + pub unsafe fn lifted_il_function( &self, - ) -> Option>> { + ) -> Option>> { let func = self.function(); let result = unsafe { BNGetFunctionLiftedIL(func.handle) }; let arch = self.function().arch(); unsafe { - Some(llil::LowLevelILFunction::ref_from_raw( + Some(LowLevelILFunction::ref_from_raw( arch, NonNull::new(result)?.as_ptr(), )) } } - pub fn set_lifted_il_function( - &self, - value: &llil::LowLevelILFunction, - ) { + pub fn set_lifted_il_function(&self, value: &MutableLiftedILFunction) { unsafe { BNSetLiftedILFunction(self.handle.as_ptr(), value.handle) } } - /// LowLevelILFunction used to represent Low Level IL - pub unsafe fn llil_function( + // TODO: This returns LiftedNonSSA because the lifting code was written before we could patch the IL + // TODO: At some point we need to take the lifting code and make it available to regular IL. + /// [`LowLevelILFunction`] used to represent Low Level IL + pub unsafe fn llil_function( &self, - ) -> Option>> { + ) -> Option>>> { let result = unsafe { BNAnalysisContextGetLowLevelILFunction(self.handle.as_ptr()) }; let arch = self.function().arch(); unsafe { - Some(llil::LowLevelILFunction::ref_from_raw( + Some(LowLevelILFunction::ref_from_raw( arch, NonNull::new(result)?.as_ptr(), )) } } - pub fn set_llil_function( + // TODO: This returns LiftedNonSSA because the lifting code was written before we could patch the IL + // TODO: At some point we need to take the lifting code and make it available to regular IL. + pub fn set_llil_function( &self, - value: &llil::LowLevelILFunction, + value: &LowLevelILFunction>, ) { unsafe { BNSetLowLevelILFunction(self.handle.as_ptr(), value.handle) } } - /// MediumLevelILFunction used to represent Medium Level IL - pub fn mlil_function(&self) -> Option> { + /// [`MediumLevelILFunction`] used to represent Medium Level IL + pub fn mlil_function(&self) -> Option> { let result = unsafe { BNAnalysisContextGetMediumLevelILFunction(self.handle.as_ptr()) }; unsafe { - Some(mlil::MediumLevelILFunction::ref_from_raw( + Some(MediumLevelILFunction::ref_from_raw( NonNull::new(result)?.as_ptr(), )) } } - pub fn set_mlil_function(&self, value: &mlil::MediumLevelILFunction) { + pub fn set_mlil_function(&self, value: &MediumLevelILFunction) { unsafe { BNSetMediumLevelILFunction(self.handle.as_ptr(), value.handle) } } - /// HighLevelILFunction used to represent High Level IL - pub fn hlil_function(&self, full_ast: bool) -> Option> { + /// [`HighLevelILFunction`] used to represent High Level IL + pub fn hlil_function(&self, full_ast: bool) -> Option> { let result = unsafe { BNAnalysisContextGetHighLevelILFunction(self.handle.as_ptr()) }; unsafe { - Some(hlil::HighLevelILFunction::ref_from_raw( + Some(HighLevelILFunction::ref_from_raw( NonNull::new(result)?.as_ptr(), full_ast, )) diff --git a/rust/tests/llil.rs b/rust/tests/llil.rs new file mode 100644 index 000000000..a9d172372 --- /dev/null +++ b/rust/tests/llil.rs @@ -0,0 +1,161 @@ +use binaryninja::architecture::Register; +use binaryninja::binaryview::{BinaryView, BinaryViewExt}; +use binaryninja::headless::Session; +use binaryninja::lowlevelil::expression::LowLevelILExpressionIndex; +use binaryninja::lowlevelil::instruction::{ + InstructionHandler, LowLevelILInstructionKind, LowLevelInstructionIndex, +}; +use binaryninja::lowlevelil::LowLevelILRegister; +use binaryninja::rc::Ref; +use rstest::*; +use std::path::PathBuf; + +#[fixture] +#[once] +fn session() -> Session { + Session::new().expect("Failed to initialize session") +} + +#[fixture] +#[once] +fn view() -> Ref { + let out_dir = env!("OUT_DIR").parse::().unwrap(); + binaryninja::load(out_dir.join("atox.obj")).expect("Failed to create view") +} + +#[rstest] +fn test_llil_info(_session: &Session, view: &BinaryView) { + let entry_function = view.entry_point_function().unwrap(); + let llil_function = entry_function.low_level_il().unwrap(); + let llil_basic_blocks = llil_function.basic_blocks(); + let mut llil_basic_block_iter = llil_basic_blocks.iter(); + let first_basic_block = llil_basic_block_iter.next().unwrap(); + let mut llil_instr_iter = first_basic_block.iter(); + + // 0 @ 00025f10 (LLIL_SET_REG.d edi = (LLIL_REG.d edi)) + let instr_0 = llil_instr_iter.next().unwrap(); + assert_eq!(instr_0.index, LowLevelInstructionIndex(0)); + assert_eq!(instr_0.address(), 0x00025f10); + println!("{:?}", instr_0.kind()); + match instr_0.kind() { + LowLevelILInstructionKind::SetReg(op) => { + assert_eq!(op.size(), 4); + match op.dest_reg() { + LowLevelILRegister::ArchReg(reg) => assert_eq!(reg.name(), "edi"), + _ => panic!("Expected Register::ArchReg"), + } + assert_eq!(op.source_expr().index, LowLevelILExpressionIndex(0)); + } + _ => panic!("Expected SetReg"), + } + // 1 @ 00025f12 (LLIL_PUSH.d push((LLIL_REG.d ebp))) + let instr_1 = llil_instr_iter.next().unwrap(); + assert_eq!(instr_1.index, LowLevelInstructionIndex(1)); + assert_eq!(instr_1.address(), 0x00025f12); + println!("{:?}", instr_1.kind()); + match instr_1.kind() { + LowLevelILInstructionKind::Push(op) => { + assert_eq!(op.size(), 4); + assert_eq!(op.operand().index, LowLevelILExpressionIndex(2)); + } + _ => panic!("Expected Push"), + } + // 2 @ 00025f13 (LLIL_SET_REG.d ebp = (LLIL_REG.d esp) {__saved_ebp}) + let instr_2 = llil_instr_iter.next().unwrap(); + assert_eq!(instr_2.index, LowLevelInstructionIndex(2)); + assert_eq!(instr_2.address(), 0x00025f13); + println!("{:?}", instr_2.kind()); + match instr_2.kind() { + LowLevelILInstructionKind::SetReg(op) => { + assert_eq!(op.size(), 4); + match op.dest_reg() { + LowLevelILRegister::ArchReg(reg) => assert_eq!(reg.name(), "ebp"), + _ => panic!("Expected Register::ArchReg"), + } + assert_eq!(op.source_expr().index, LowLevelILExpressionIndex(4)); + } + _ => panic!("Expected SetReg"), + } + // 3 @ 00025f15 (LLIL_SET_REG.d eax = (LLIL_LOAD.d [(LLIL_ADD.d (LLIL_REG.d ebp) + (LLIL_CONST.d 8)) {arg1}].d)) + let instr_3 = llil_instr_iter.next().unwrap(); + assert_eq!(instr_3.index, LowLevelInstructionIndex(3)); + assert_eq!(instr_3.address(), 0x00025f15); + println!("{:?}", instr_3.kind()); + match instr_3.kind() { + LowLevelILInstructionKind::SetReg(op) => { + assert_eq!(op.size(), 4); + match op.dest_reg() { + LowLevelILRegister::ArchReg(reg) => assert_eq!(reg.name(), "eax"), + _ => panic!("Expected Register::ArchReg"), + } + assert_eq!(op.source_expr().index, LowLevelILExpressionIndex(9)); + } + _ => panic!("Expected SetReg"), + } + // 4 @ 00025f18 (LLIL_PUSH.d push((LLIL_REG.d eax))) + let instr_4 = llil_instr_iter.next().unwrap(); + assert_eq!(instr_4.index, LowLevelInstructionIndex(4)); + assert_eq!(instr_4.address(), 0x00025f18); + println!("{:?}", instr_4.kind()); + match instr_4.kind() { + LowLevelILInstructionKind::Push(op) => { + assert_eq!(op.size(), 4); + assert_eq!(op.operand().index, LowLevelILExpressionIndex(11)); + } + _ => panic!("Expected Push"), + } + // 5 @ 00025f19 (LLIL_CALL call((LLIL_CONST_PTR.d __crt_interlocked_read_32))) + let instr_5 = llil_instr_iter.next().unwrap(); + assert_eq!(instr_5.index, LowLevelInstructionIndex(5)); + assert_eq!(instr_5.address(), 0x00025f19); + println!("{:?}", instr_5.kind()); + match instr_5.kind() { + LowLevelILInstructionKind::Call(op) => { + assert_eq!(op.target().index, LowLevelILExpressionIndex(13)); + } + _ => panic!("Expected Call"), + } + // 6 @ 00025f1e (LLIL_SET_REG.d esp = (LLIL_ADD.d (LLIL_REG.d esp) + (LLIL_CONST.d 4))) + let instr_6 = llil_instr_iter.next().unwrap(); + assert_eq!(instr_6.index, LowLevelInstructionIndex(6)); + assert_eq!(instr_6.address(), 0x00025f1e); + println!("{:?}", instr_6.kind()); + match instr_6.kind() { + LowLevelILInstructionKind::SetReg(op) => { + assert_eq!(op.size(), 4); + match op.dest_reg() { + LowLevelILRegister::ArchReg(reg) => assert_eq!(reg.name(), "esp"), + _ => panic!("Expected Register::ArchReg"), + } + assert_eq!(op.source_expr().index, LowLevelILExpressionIndex(17)); + } + _ => panic!("Expected SetReg"), + } + // 7 @ 00025f21 (LLIL_SET_REG.d ebp = (LLIL_POP.d pop)) + let instr_7 = llil_instr_iter.next().unwrap(); + assert_eq!(instr_7.index, LowLevelInstructionIndex(7)); + assert_eq!(instr_7.address(), 0x00025f21); + println!("{:?}", instr_7.kind()); + match instr_7.kind() { + LowLevelILInstructionKind::SetReg(op) => { + assert_eq!(op.size(), 4); + match op.dest_reg() { + LowLevelILRegister::ArchReg(reg) => assert_eq!(reg.name(), "ebp"), + _ => panic!("Expected Register::ArchReg"), + } + assert_eq!(op.source_expr().index, LowLevelILExpressionIndex(19)); + } + _ => panic!("Expected SetReg"), + } + // 8 @ 00025f22 (LLIL_RET jump((LLIL_POP.d pop))) + let instr_8 = llil_instr_iter.next().unwrap(); + assert_eq!(instr_8.index, LowLevelInstructionIndex(8)); + assert_eq!(instr_8.address(), 0x00025f22); + println!("{:?}", instr_8.kind()); + match instr_8.kind() { + LowLevelILInstructionKind::Ret(op) => { + assert_eq!(op.target().index, LowLevelILExpressionIndex(21)); + } + _ => panic!("Expected Ret"), + } +}