From 2a9beeec15d3f32312a0c4f8ad5ddbcfcbc97a89 Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Fri, 9 May 2025 14:00:38 -0400 Subject: [Rust] Support SSA form properly in low level IL --- arch/riscv/src/lib.rs | 2 +- rust/src/low_level_il.rs | 54 ++- rust/src/low_level_il/expression.rs | 280 +++++++++------ rust/src/low_level_il/function.rs | 26 +- rust/src/low_level_il/instruction.rs | 108 ++++-- rust/src/low_level_il/operation.rs | 669 ++++++++++++++++++++++++++++++++++- rust/tests/low_level_il.rs | 85 ++++- 7 files changed, 1053 insertions(+), 171 deletions(-) diff --git a/arch/riscv/src/lib.rs b/arch/riscv/src/lib.rs index da4c3115..f43f9bd3 100644 --- a/arch/riscv/src/lib.rs +++ b/arch/riscv/src/lib.rs @@ -2884,7 +2884,7 @@ impl FunctionRecognizer for RiscVELFPLTRecognizer { LowLevelILInstructionKind::SetReg(r) => match r.source_expr().kind() { LowLevelILExpressionKind::Load(l) => { let target_reg = r.dest_reg(); - let entry = match l.source_mem_expr().kind() { + let entry = match l.source_expr().kind() { LowLevelILExpressionKind::Reg(lr) if lr.source_reg() == auipc_dest => { plt_base } diff --git a/rust/src/low_level_il.rs b/rust/src/low_level_il.rs index 453e0546..4c52e313 100644 --- a/rust/src/low_level_il.rs +++ b/rust/src/low_level_il.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::borrow::Cow; use std::fmt; // TODO : provide some way to forbid emitting register reads for certain registers @@ -20,7 +21,7 @@ use std::fmt; // 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::{Architecture, RegisterId}; +use crate::architecture::{Architecture, Flag, RegisterId}; use crate::architecture::{CoreRegister, Register as ArchReg}; use crate::function::Location; @@ -125,12 +126,19 @@ impl LowLevelILRegisterKind { LowLevelILRegisterKind::Temp(temp.into()) } - fn id(&self) -> RegisterId { + pub fn id(&self) -> RegisterId { match *self { LowLevelILRegisterKind::Arch(ref r) => r.id(), LowLevelILRegisterKind::Temp(temp) => temp.id(), } } + + pub fn name(&self) -> Cow { + match *self { + LowLevelILRegisterKind::Arch(ref r) => r.name(), + LowLevelILRegisterKind::Temp(temp) => Cow::Owned(format!("temp{}", temp.temp_id)), + } + } } impl fmt::Debug for LowLevelILRegisterKind { @@ -149,19 +157,51 @@ impl From for LowLevelILRegisterKind { } #[derive(Copy, Clone, Debug)] -pub enum LowLevelILSSARegister { - Full(LowLevelILRegisterKind, 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 +pub enum LowLevelILSSARegisterKind { + Full { + kind: LowLevelILRegisterKind, + version: u32, + }, + Partial { + full_reg: CoreRegister, + partial_reg: CoreRegister, + version: u32, + }, } -impl LowLevelILSSARegister { +impl LowLevelILSSARegisterKind { + pub fn new_full(kind: LowLevelILRegisterKind, version: u32) -> Self { + Self::Full { kind, version } + } + + pub fn new_partial(full_reg: CoreRegister, partial_reg: CoreRegister, version: u32) -> Self { + Self::Partial { + full_reg, + partial_reg, + version, + } + } + pub fn version(&self) -> u32 { match *self { - LowLevelILSSARegister::Full(_, ver) | LowLevelILSSARegister::Partial(_, ver, _) => ver, + LowLevelILSSARegisterKind::Full { version, .. } + | LowLevelILSSARegisterKind::Partial { version, .. } => version, } } } +#[derive(Copy, Clone, Debug)] +pub struct LowLevelILSSAFlag { + pub flag: F, + pub version: u32, +} + +impl LowLevelILSSAFlag { + pub fn new(flag: F, version: u32) -> Self { + Self { flag, version } + } +} + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] pub enum VisitorAction { Descend, diff --git a/rust/src/low_level_il/expression.rs b/rust/src/low_level_il/expression.rs index 6addb7e1..73bec38d 100644 --- a/rust/src/low_level_il/expression.rs +++ b/rust/src/low_level_il/expression.rs @@ -99,8 +99,7 @@ where { fn fmt(&self, f: &mut Formatter) -> fmt::Result { let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, self.index.0) }; - // SAFETY: This is safe we are not exposing the expression kind to the caller. - let kind = unsafe { LowLevelILExpressionKind::from_raw(self.function, op) }; + let kind = LowLevelILExpressionKind::from_raw(self.function, op, self.index); kind.fmt(f) } } @@ -117,7 +116,7 @@ where match op.operation { // Any invalid ops for SSA will be checked here. // SAFETY: We have checked for illegal operations. - _ => unsafe { LowLevelILExpressionKind::from_raw(self.function, op) }, + _ => LowLevelILExpressionKind::from_raw(self.function, op, self.index), } } @@ -149,7 +148,7 @@ where match op.operation { // Any invalid ops for Lifted IL will be checked here. // SAFETY: We have checked for illegal operations. - _ => unsafe { LowLevelILExpressionKind::from_raw(self.function, op) }, + _ => LowLevelILExpressionKind::from_raw(self.function, op, self.index), } } @@ -181,7 +180,7 @@ 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 { LowLevelILExpressionKind::from_raw(self.function, op) }, + _ => LowLevelILExpressionKind::from_raw(self.function, op, self.index), } } @@ -216,9 +215,13 @@ where F: FunctionForm, { Load(Operation<'func, M, F, operation::Load>), + LoadSsa(Operation<'func, M, F, operation::LoadSsa>), Pop(Operation<'func, M, F, operation::Pop>), Reg(Operation<'func, M, F, operation::Reg>), + RegSsa(Operation<'func, M, F, operation::RegSsa>), + RegPartialSsa(Operation<'func, M, F, operation::RegPartialSsa>), RegSplit(Operation<'func, M, F, operation::RegSplit>), + RegSplitSsa(Operation<'func, M, F, operation::RegSplitSsa>), Const(Operation<'func, M, F, operation::Const>), ConstPtr(Operation<'func, M, F, operation::Const>), Flag(Operation<'func, M, F, operation::Flag>), @@ -227,6 +230,10 @@ where RegStackPop(Operation<'func, M, F, operation::RegStackPop>), + CallOutputSsa(Operation<'func, M, F, operation::CallOutputSsa>), + CallParamSsa(Operation<'func, M, F, operation::CallParamSsa>), + CallStackSsa(Operation<'func, M, F, operation::CallStackSsa>), + Add(Operation<'func, M, F, operation::BinaryOp>), Adc(Operation<'func, M, F, operation::BinaryOpCarry>), Sub(Operation<'func, M, F, operation::BinaryOp>), @@ -319,116 +326,146 @@ where M: FunctionMutability, F: FunctionForm, { - // TODO: Document what "unchecked" means and how to consume this safely. - pub(crate) unsafe fn from_raw( + pub(crate) fn from_raw( function: &'func LowLevelILFunction, op: BNLowLevelILInstruction, + index: LowLevelExpressionIndex, ) -> Self { use binaryninjacore_sys::BNLowLevelILOperation::*; match op.operation { - LLIL_LOAD | LLIL_LOAD_SSA => { - LowLevelILExpressionKind::Load(Operation::new(function, op)) + LLIL_LOAD => LowLevelILExpressionKind::Load(Operation::new(function, op, index)), + LLIL_LOAD_SSA => LowLevelILExpressionKind::LoadSsa(Operation::new(function, op, index)), + LLIL_POP => LowLevelILExpressionKind::Pop(Operation::new(function, op, index)), + LLIL_REG => LowLevelILExpressionKind::Reg(Operation::new(function, op, index)), + LLIL_REG_SSA => LowLevelILExpressionKind::RegSsa(Operation::new(function, op, index)), + LLIL_REG_SSA_PARTIAL => { + LowLevelILExpressionKind::RegPartialSsa(Operation::new(function, op, index)) } - LLIL_POP => LowLevelILExpressionKind::Pop(Operation::new(function, op)), - LLIL_REG | LLIL_REG_SSA | LLIL_REG_SSA_PARTIAL => { - LowLevelILExpressionKind::Reg(Operation::new(function, op)) + LLIL_REG_SPLIT => { + LowLevelILExpressionKind::RegSplit(Operation::new(function, op, index)) } - LLIL_REG_SPLIT | LLIL_REG_SPLIT_SSA => { - LowLevelILExpressionKind::RegSplit(Operation::new(function, op)) + LLIL_REG_SPLIT_SSA => { + LowLevelILExpressionKind::RegSplitSsa(Operation::new(function, op, index)) + } + LLIL_CONST => LowLevelILExpressionKind::Const(Operation::new(function, op, index)), + LLIL_CONST_PTR => { + LowLevelILExpressionKind::ConstPtr(Operation::new(function, op, index)) } - 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)) + LowLevelILExpressionKind::Flag(Operation::new(function, op, index)) } LLIL_FLAG_BIT | LLIL_FLAG_BIT_SSA => { - LowLevelILExpressionKind::FlagBit(Operation::new(function, op)) + LowLevelILExpressionKind::FlagBit(Operation::new(function, op, index)) + } + LLIL_EXTERN_PTR => { + LowLevelILExpressionKind::ExternPtr(Operation::new(function, op, index)) } - LLIL_EXTERN_PTR => LowLevelILExpressionKind::ExternPtr(Operation::new(function, op)), LLIL_REG_STACK_POP => { - LowLevelILExpressionKind::RegStackPop(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)), + LowLevelILExpressionKind::RegStackPop(Operation::new(function, op, index)) + } + + LLIL_CALL_OUTPUT_SSA => { + LowLevelILExpressionKind::CallOutputSsa(Operation::new(function, op, index)) + } + LLIL_CALL_PARAM => { + LowLevelILExpressionKind::CallParamSsa(Operation::new(function, op, index)) + } + LLIL_CALL_STACK_SSA => { + LowLevelILExpressionKind::CallStackSsa(Operation::new(function, op, index)) + } + + LLIL_ADD => LowLevelILExpressionKind::Add(Operation::new(function, op, index)), + LLIL_ADC => LowLevelILExpressionKind::Adc(Operation::new(function, op, index)), + LLIL_SUB => LowLevelILExpressionKind::Sub(Operation::new(function, op, index)), + LLIL_SBB => LowLevelILExpressionKind::Sbb(Operation::new(function, op, index)), + LLIL_AND => LowLevelILExpressionKind::And(Operation::new(function, op, index)), + LLIL_OR => LowLevelILExpressionKind::Or(Operation::new(function, op, index)), + LLIL_XOR => LowLevelILExpressionKind::Xor(Operation::new(function, op, index)), + LLIL_LSL => LowLevelILExpressionKind::Lsl(Operation::new(function, op, index)), + LLIL_LSR => LowLevelILExpressionKind::Lsr(Operation::new(function, op, index)), + LLIL_ASR => LowLevelILExpressionKind::Asr(Operation::new(function, op, index)), + LLIL_ROL => LowLevelILExpressionKind::Rol(Operation::new(function, op, index)), + LLIL_RLC => LowLevelILExpressionKind::Rlc(Operation::new(function, op, index)), + LLIL_ROR => LowLevelILExpressionKind::Ror(Operation::new(function, op, index)), + LLIL_RRC => LowLevelILExpressionKind::Rrc(Operation::new(function, op, index)), + LLIL_MUL => LowLevelILExpressionKind::Mul(Operation::new(function, op, index)), + + LLIL_MULU_DP => LowLevelILExpressionKind::MuluDp(Operation::new(function, op, index)), + LLIL_MULS_DP => LowLevelILExpressionKind::MulsDp(Operation::new(function, op, index)), + + LLIL_DIVU => LowLevelILExpressionKind::Divu(Operation::new(function, op, index)), + LLIL_DIVS => LowLevelILExpressionKind::Divs(Operation::new(function, op, index)), + + LLIL_DIVU_DP => LowLevelILExpressionKind::DivuDp(Operation::new(function, op, index)), + LLIL_DIVS_DP => LowLevelILExpressionKind::DivsDp(Operation::new(function, op, index)), + + LLIL_MODU => LowLevelILExpressionKind::Modu(Operation::new(function, op, index)), + LLIL_MODS => LowLevelILExpressionKind::Mods(Operation::new(function, op, index)), + + LLIL_MODU_DP => LowLevelILExpressionKind::ModuDp(Operation::new(function, op, index)), + LLIL_MODS_DP => LowLevelILExpressionKind::ModsDp(Operation::new(function, op, index)), + + LLIL_NEG => LowLevelILExpressionKind::Neg(Operation::new(function, op, index)), + LLIL_NOT => LowLevelILExpressionKind::Not(Operation::new(function, op, index)), + + LLIL_SX => LowLevelILExpressionKind::Sx(Operation::new(function, op, index)), + LLIL_ZX => LowLevelILExpressionKind::Zx(Operation::new(function, op, index)), + LLIL_LOW_PART => LowLevelILExpressionKind::LowPart(Operation::new(function, op, index)), + + LLIL_CMP_E => LowLevelILExpressionKind::CmpE(Operation::new(function, op, index)), + LLIL_CMP_NE => LowLevelILExpressionKind::CmpNe(Operation::new(function, op, index)), + LLIL_CMP_SLT => LowLevelILExpressionKind::CmpSlt(Operation::new(function, op, index)), + LLIL_CMP_ULT => LowLevelILExpressionKind::CmpUlt(Operation::new(function, op, index)), + LLIL_CMP_SLE => LowLevelILExpressionKind::CmpSle(Operation::new(function, op, index)), + LLIL_CMP_ULE => LowLevelILExpressionKind::CmpUle(Operation::new(function, op, index)), + LLIL_CMP_SGE => LowLevelILExpressionKind::CmpSge(Operation::new(function, op, index)), + LLIL_CMP_UGE => LowLevelILExpressionKind::CmpUge(Operation::new(function, op, index)), + LLIL_CMP_SGT => LowLevelILExpressionKind::CmpSgt(Operation::new(function, op, index)), + LLIL_CMP_UGT => LowLevelILExpressionKind::CmpUgt(Operation::new(function, op, index)), + + LLIL_BOOL_TO_INT => { + LowLevelILExpressionKind::BoolToInt(Operation::new(function, op, index)) + } + + LLIL_FADD => LowLevelILExpressionKind::Fadd(Operation::new(function, op, index)), + LLIL_FSUB => LowLevelILExpressionKind::Fsub(Operation::new(function, op, index)), + LLIL_FMUL => LowLevelILExpressionKind::Fmul(Operation::new(function, op, index)), + LLIL_FDIV => LowLevelILExpressionKind::Fdiv(Operation::new(function, op, index)), + + LLIL_FSQRT => LowLevelILExpressionKind::Fsqrt(Operation::new(function, op, index)), + LLIL_FNEG => LowLevelILExpressionKind::Fneg(Operation::new(function, op, index)), + LLIL_FABS => LowLevelILExpressionKind::Fabs(Operation::new(function, op, index)), + LLIL_FLOAT_TO_INT => { + LowLevelILExpressionKind::FloatToInt(Operation::new(function, op, index)) + } + LLIL_INT_TO_FLOAT => { + LowLevelILExpressionKind::IntToFloat(Operation::new(function, op, index)) + } + LLIL_FLOAT_CONV => { + LowLevelILExpressionKind::FloatConv(Operation::new(function, op, index)) + } + LLIL_ROUND_TO_INT => { + LowLevelILExpressionKind::RoundToInt(Operation::new(function, op, index)) + } + LLIL_FLOOR => LowLevelILExpressionKind::Floor(Operation::new(function, op, index)), + LLIL_CEIL => LowLevelILExpressionKind::Ceil(Operation::new(function, op, index)), + LLIL_FTRUNC => LowLevelILExpressionKind::Ftrunc(Operation::new(function, op, index)), + + LLIL_FCMP_E => LowLevelILExpressionKind::FcmpE(Operation::new(function, op, index)), + LLIL_FCMP_NE => LowLevelILExpressionKind::FcmpNE(Operation::new(function, op, index)), + LLIL_FCMP_LT => LowLevelILExpressionKind::FcmpLT(Operation::new(function, op, index)), + LLIL_FCMP_LE => LowLevelILExpressionKind::FcmpLE(Operation::new(function, op, index)), + LLIL_FCMP_GT => LowLevelILExpressionKind::FcmpGT(Operation::new(function, op, index)), + LLIL_FCMP_GE => LowLevelILExpressionKind::FcmpGE(Operation::new(function, op, index)), + LLIL_FCMP_O => LowLevelILExpressionKind::FcmpO(Operation::new(function, op, index)), + LLIL_FCMP_UO => LowLevelILExpressionKind::FcmpUO(Operation::new(function, op, index)), + + LLIL_UNIMPL => LowLevelILExpressionKind::Unimpl(Operation::new(function, op, index)), + LLIL_UNIMPL_MEM => { + LowLevelILExpressionKind::UnimplMem(Operation::new(function, op, index)) + } // TODO TEST_BIT ADD_OVERFLOW LLIL_REG_STACK_PUSH LLIL_REG_STACK_POP _ => { @@ -439,7 +476,7 @@ where op.address ); - LowLevelILExpressionKind::Undef(Operation::new(function, op)) + LowLevelILExpressionKind::Undef(Operation::new(function, op, index)) } } } @@ -585,12 +622,21 @@ where visit!(op.mem_expr()); } Load(ref op) => { - visit!(op.source_mem_expr()); + visit!(op.source_expr()); } - // Do not have any sub expressions. - Pop(_) | Reg(_) | RegSplit(_) | Const(_) | ConstPtr(_) | Flag(_) | FlagBit(_) - | ExternPtr(_) | FlagCond(_) | FlagGroup(_) | Unimpl(_) | Undef(_) | RegStackPop(_) => { + LoadSsa(ref op) => { + visit!(op.source_expr()); + } + CallParamSsa(ref op) => { + for param_expr in op.param_exprs() { + visit!(param_expr); + } } + // Do not have any sub expressions. + Pop(_) | Reg(_) | RegSsa(_) | RegPartialSsa(_) | RegSplit(_) | RegSplitSsa(_) + | Const(_) | ConstPtr(_) | Flag(_) | FlagBit(_) | ExternPtr(_) | FlagCond(_) + | FlagGroup(_) | Unimpl(_) | Undef(_) | RegStackPop(_) | CallOutputSsa(_) + | CallStackSsa(_) => {} } VisitorAction::Sibling @@ -614,12 +660,20 @@ where Load(ref op) => &op.op, + LoadSsa(ref op) => &op.op, + Pop(ref op) => &op.op, Reg(ref op) => &op.op, + RegSsa(ref op) => &op.op, + + RegPartialSsa(ref op) => &op.op, + RegSplit(ref op) => &op.op, + RegSplitSsa(ref op) => &op.op, + Flag(ref op) => &op.op, FlagBit(ref op) => &op.op, @@ -630,6 +684,10 @@ where RegStackPop(ref op) => &op.op, + CallOutputSsa(ref op) => &op.op, + CallParamSsa(ref op) => &op.op, + CallStackSsa(ref op) => &op.op, + Adc(ref op) | Sbb(ref op) | Rlc(ref op) | Rrc(ref op) => &op.op, Add(ref op) | Sub(ref op) | And(ref op) | Or(ref op) | Xor(ref op) | Lsl(ref op) @@ -670,12 +728,20 @@ impl LowLevelILExpressionKind<'_, Mutable, NonSSA> { Load(ref op) => op.flag_write(), + LoadSsa(ref op) => op.flag_write(), + Pop(ref op) => op.flag_write(), Reg(ref op) => op.flag_write(), + RegSsa(ref op) => op.flag_write(), + + RegPartialSsa(ref op) => op.flag_write(), + RegSplit(ref op) => op.flag_write(), + RegSplitSsa(ref op) => op.flag_write(), + Flag(ref op) => op.flag_write(), FlagBit(ref op) => op.flag_write(), @@ -686,6 +752,10 @@ impl LowLevelILExpressionKind<'_, Mutable, NonSSA> { RegStackPop(ref op) => op.flag_write(), + CallOutputSsa(ref op) => op.flag_write(), + CallParamSsa(ref op) => op.flag_write(), + CallStackSsa(ref op) => op.flag_write(), + Adc(ref op) | Sbb(ref op) | Rlc(ref op) | Rrc(ref op) => op.flag_write(), Add(ref op) | Sub(ref op) | And(ref op) | Or(ref op) | Xor(ref op) | Lsl(ref op) diff --git a/rust/src/low_level_il/function.rs b/rust/src/low_level_il/function.rs index 21eebc3c..62811b1d 100644 --- a/rust/src/low_level_il/function.rs +++ b/rust/src/low_level_il/function.rs @@ -12,15 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -use binaryninjacore_sys::BNFreeLowLevelILFunction; -use binaryninjacore_sys::BNGetLowLevelILOwnerFunction; -use binaryninjacore_sys::BNLowLevelILFunction; -use binaryninjacore_sys::BNNewLowLevelILFunctionReference; - use std::fmt::Debug; use std::hash::{Hash, Hasher}; use std::marker::PhantomData; +use binaryninjacore_sys::*; + use crate::architecture::CoreArchitecture; use crate::basic_block::BasicBlock; use crate::function::Function; @@ -160,13 +157,8 @@ where Function::ref_from_raw(func) } } -} -// LLIL basic blocks are not available until the function object -// is finalized, so ensure we can't try requesting basic blocks -// during lifting -impl LowLevelILFunction { - pub fn basic_blocks(&self) -> Array>> { + pub fn basic_blocks(&self) -> Array>> { use binaryninjacore_sys::BNGetLowLevelILBasicBlockList; unsafe { @@ -178,6 +170,17 @@ impl LowLevelILFunction { } } +impl LowLevelILFunction> { + /// Retrieve the SSA form of the function. + pub fn ssa_form(&self) -> Option>> { + let handle = unsafe { BNGetLowLevelILSSAForm(self.handle) }; + if handle.is_null() { + return None; + } + Some(unsafe { LowLevelILFunction::ref_from_raw(handle) }) + } +} + // Allow instantiating Lifted IL functions for querying Lifted IL from Architectures impl LowLevelILFunction> { // TODO: Document what happens when you pass None for `source_func`. @@ -201,7 +204,6 @@ impl LowLevelILFunction> { pub fn generate_ssa_form(&self) { use binaryninjacore_sys::BNGenerateLowLevelILSSAForm; - unsafe { BNGenerateLowLevelILSSAForm(self.handle) }; } } diff --git a/rust/src/low_level_il/instruction.rs b/rust/src/low_level_il/instruction.rs index ded9c6f3..90d2f1d8 100644 --- a/rust/src/low_level_il/instruction.rs +++ b/rust/src/low_level_il/instruction.rs @@ -207,9 +207,14 @@ where { Nop(Operation<'func, M, F, operation::NoArgs>), SetReg(Operation<'func, M, F, operation::SetReg>), + SetRegSsa(Operation<'func, M, F, operation::SetRegSsa>), + SetRegPartialSsa(Operation<'func, M, F, operation::SetRegPartialSsa>), SetRegSplit(Operation<'func, M, F, operation::SetRegSplit>), + SetRegSplitSsa(Operation<'func, M, F, operation::SetRegSplitSsa>), SetFlag(Operation<'func, M, F, operation::SetFlag>), + SetFlagSsa(Operation<'func, M, F, operation::SetFlagSsa>), Store(Operation<'func, M, F, operation::Store>), + StoreSsa(Operation<'func, M, F, operation::StoreSsa>), // TODO needs a real op Push(Operation<'func, M, F, operation::UnaryOp>), @@ -219,7 +224,9 @@ where JumpTo(Operation<'func, M, F, operation::JumpTo>), Call(Operation<'func, M, F, operation::Call>), + CallSsa(Operation<'func, M, F, operation::CallSsa>), TailCall(Operation<'func, M, F, operation::Call>), + TailCallSsa(Operation<'func, M, F, operation::CallSsa>), Ret(Operation<'func, M, F, operation::Ret>), NoRet(Operation<'func, M, F, operation::NoArgs>), @@ -228,6 +235,7 @@ where Goto(Operation<'func, M, F, operation::Goto>), Syscall(Operation<'func, M, F, operation::Syscall>), + SyscallSsa(Operation<'func, M, F, operation::SyscallSsa>), Intrinsic(Operation<'func, M, F, operation::Intrinsic>), Bp(Operation<'func, M, F, operation::NoArgs>), Trap(Operation<'func, M, F, operation::Trap>), @@ -250,50 +258,80 @@ where use binaryninjacore_sys::BNLowLevelILOperation::*; match op.operation { - LLIL_NOP => LowLevelILInstructionKind::Nop(Operation::new(function, op)), - LLIL_SET_REG | LLIL_SET_REG_SSA => { - LowLevelILInstructionKind::SetReg(Operation::new(function, op)) + LLIL_NOP => LowLevelILInstructionKind::Nop(Operation::new(function, op, expr_index)), + LLIL_SET_REG => { + LowLevelILInstructionKind::SetReg(Operation::new(function, op, expr_index)) } - LLIL_SET_REG_SPLIT | LLIL_SET_REG_SPLIT_SSA => { - LowLevelILInstructionKind::SetRegSplit(Operation::new(function, op)) + LLIL_SET_REG_SSA => { + LowLevelILInstructionKind::SetRegSsa(Operation::new(function, op, expr_index)) } - LLIL_SET_FLAG | LLIL_SET_FLAG_SSA => { - LowLevelILInstructionKind::SetFlag(Operation::new(function, op)) + LLIL_SET_REG_SSA_PARTIAL => LowLevelILInstructionKind::SetRegPartialSsa( + Operation::new(function, op, expr_index), + ), + LLIL_SET_REG_SPLIT => { + LowLevelILInstructionKind::SetRegSplit(Operation::new(function, op, expr_index)) } - LLIL_STORE | LLIL_STORE_SSA => { - LowLevelILInstructionKind::Store(Operation::new(function, op)) + LLIL_SET_REG_SPLIT_SSA => { + LowLevelILInstructionKind::SetRegSplitSsa(Operation::new(function, op, expr_index)) } - LLIL_PUSH => LowLevelILInstructionKind::Push(Operation::new(function, op)), + LLIL_SET_FLAG => { + LowLevelILInstructionKind::SetFlag(Operation::new(function, op, expr_index)) + } + LLIL_SET_FLAG_SSA => { + LowLevelILInstructionKind::SetFlagSsa(Operation::new(function, op, expr_index)) + } + LLIL_STORE => { + LowLevelILInstructionKind::Store(Operation::new(function, op, expr_index)) + } + LLIL_STORE_SSA => { + LowLevelILInstructionKind::StoreSsa(Operation::new(function, op, expr_index)) + } + LLIL_PUSH => LowLevelILInstructionKind::Push(Operation::new(function, op, expr_index)), LLIL_REG_STACK_PUSH => { - LowLevelILInstructionKind::RegStackPush(Operation::new(function, op)) + LowLevelILInstructionKind::RegStackPush(Operation::new(function, op, expr_index)) } - LLIL_JUMP => LowLevelILInstructionKind::Jump(Operation::new(function, op)), - LLIL_JUMP_TO => LowLevelILInstructionKind::JumpTo(Operation::new(function, op)), + LLIL_JUMP => LowLevelILInstructionKind::Jump(Operation::new(function, op, expr_index)), + LLIL_JUMP_TO => { + LowLevelILInstructionKind::JumpTo(Operation::new(function, op, expr_index)) + } - LLIL_CALL | LLIL_CALL_STACK_ADJUST | LLIL_CALL_SSA => { - LowLevelILInstructionKind::Call(Operation::new(function, op)) + LLIL_CALL | LLIL_CALL_STACK_ADJUST => { + LowLevelILInstructionKind::Call(Operation::new(function, op, expr_index)) + } + LLIL_CALL_SSA => { + LowLevelILInstructionKind::CallSsa(Operation::new(function, op, expr_index)) + } + LLIL_TAILCALL => { + LowLevelILInstructionKind::TailCall(Operation::new(function, op, expr_index)) } - LLIL_TAILCALL | LLIL_TAILCALL_SSA => { - LowLevelILInstructionKind::TailCall(Operation::new(function, op)) + LLIL_TAILCALL_SSA => { + LowLevelILInstructionKind::TailCallSsa(Operation::new(function, op, expr_index)) } - LLIL_RET => LowLevelILInstructionKind::Ret(Operation::new(function, op)), - LLIL_NORET => LowLevelILInstructionKind::NoRet(Operation::new(function, op)), + LLIL_RET => LowLevelILInstructionKind::Ret(Operation::new(function, op, expr_index)), + LLIL_NORET => { + LowLevelILInstructionKind::NoRet(Operation::new(function, op, expr_index)) + } - LLIL_IF => LowLevelILInstructionKind::If(Operation::new(function, op)), - LLIL_GOTO => LowLevelILInstructionKind::Goto(Operation::new(function, op)), + LLIL_IF => LowLevelILInstructionKind::If(Operation::new(function, op, expr_index)), + LLIL_GOTO => LowLevelILInstructionKind::Goto(Operation::new(function, op, expr_index)), - LLIL_SYSCALL | LLIL_SYSCALL_SSA => { - LowLevelILInstructionKind::Syscall(Operation::new(function, op)) + LLIL_SYSCALL => { + LowLevelILInstructionKind::Syscall(Operation::new(function, op, expr_index)) + } + LLIL_SYSCALL_SSA => { + LowLevelILInstructionKind::SyscallSsa(Operation::new(function, op, expr_index)) } LLIL_INTRINSIC | LLIL_INTRINSIC_SSA => { - LowLevelILInstructionKind::Intrinsic(Operation::new(function, op)) + LowLevelILInstructionKind::Intrinsic(Operation::new(function, op, expr_index)) + } + LLIL_BP => LowLevelILInstructionKind::Bp(Operation::new(function, op, expr_index)), + LLIL_TRAP => LowLevelILInstructionKind::Trap(Operation::new(function, op, expr_index)), + LLIL_UNDEF => { + LowLevelILInstructionKind::Undef(Operation::new(function, op, expr_index)) } - LLIL_BP => LowLevelILInstructionKind::Bp(Operation::new(function, op)), - LLIL_TRAP => LowLevelILInstructionKind::Trap(Operation::new(function, op)), - LLIL_UNDEF => LowLevelILInstructionKind::Undef(Operation::new(function, op)), _ => LowLevelILInstructionKind::Value(LowLevelILExpression::new(function, expr_index)), } } @@ -314,17 +352,31 @@ where match self { SetReg(ref op) => visit!(&op.source_expr()), + SetRegSsa(ref op) => visit!(&op.source_expr()), + SetRegPartialSsa(ref op) => visit!(&op.source_expr()), SetRegSplit(ref op) => visit!(&op.source_expr()), + SetRegSplitSsa(ref op) => visit!(&op.source_expr()), SetFlag(ref op) => visit!(&op.source_expr()), + SetFlagSsa(ref op) => visit!(&op.source_expr()), Store(ref op) => { - visit!(&op.dest_mem_expr()); + visit!(&op.dest_expr()); + visit!(&op.source_expr()); + } + StoreSsa(ref op) => { + visit!(&op.dest_expr()); visit!(&op.source_expr()); } Push(ref op) => visit!(&op.operand()), RegStackPush(ref op) => visit!(&op.source_expr()), Jump(ref op) => visit!(&op.target()), JumpTo(ref op) => visit!(&op.target()), + SyscallSsa(ref op) => { + visit!(&op.output_expr()); + visit!(&op.param_expr()); + visit!(&op.stack_expr()); + } Call(ref op) | TailCall(ref op) => visit!(&op.target()), + CallSsa(ref op) | TailCallSsa(ref op) => visit!(&op.target()), Ret(ref op) => visit!(&op.target()), If(ref op) => visit!(&op.condition()), Intrinsic(ref _op) => { diff --git a/rust/src/low_level_il/operation.rs b/rust/src/low_level_il/operation.rs index bd68bbb1..7f31f62b 100644 --- a/rust/src/low_level_il/operation.rs +++ b/rust/src/low_level_il/operation.rs @@ -12,7 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -use binaryninjacore_sys::{BNGetLowLevelILByIndex, BNLowLevelILInstruction}; +use binaryninjacore_sys::{ + BNGetLowLevelILByIndex, BNLowLevelILFreeOperandList, BNLowLevelILGetOperandList, + BNLowLevelILInstruction, +}; use super::*; use crate::architecture::{ @@ -32,6 +35,7 @@ where { pub(crate) function: &'func LowLevelILFunction, pub(crate) op: BNLowLevelILInstruction, + pub(crate) expr_idx: LowLevelExpressionIndex, _args: PhantomData, } @@ -44,10 +48,12 @@ where pub(crate) fn new( function: &'func LowLevelILFunction, op: BNLowLevelILInstruction, + expr_idx: LowLevelExpressionIndex, ) -> Self { Self { function, op, + expr_idx, _args: PhantomData, } } @@ -55,6 +61,22 @@ where pub fn address(&self) -> u64 { self.op.address } + + pub fn get_operand_list(&self, operand_idx: usize) -> Vec { + let mut count = 0; + let raw_list_ptr = unsafe { + BNLowLevelILGetOperandList( + self.function.handle, + self.expr_idx.0, + operand_idx, + &mut count, + ) + }; + assert!(!raw_list_ptr.is_null()); + let list = unsafe { std::slice::from_raw_parts(raw_list_ptr, count).to_vec() }; + unsafe { BNLowLevelILFreeOperandList(raw_list_ptr) }; + list + } } impl Operation<'_, M, NonSSA, O> @@ -109,7 +131,7 @@ where } } -// LLIL_SYSCALL, LLIL_SYSCALL_SSA +// LLIL_SYSCALL pub struct Syscall; impl Debug for Operation<'_, M, F, Syscall> @@ -122,6 +144,59 @@ where } } +// LLIL_SYSCALL_SSA +pub struct SyscallSsa; + +impl<'func, M, F> Operation<'func, M, F, SyscallSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + /// Get the output expression of the call. + /// + /// NOTE: This is currently always [`CallOutputSsa`]. + pub fn output_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[0] as usize), + ) + } + + /// Get the parameter expression of the call. + /// + /// NOTE: This is currently always [`CallParamSsa`]. + pub fn param_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[2] as usize), + ) + } + + /// Get the stack expression of the call. + /// + /// NOTE: This is currently always [`CallStackSsa`]. + pub fn stack_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[1] as usize), + ) + } +} + +impl Debug for Operation<'_, M, F, SyscallSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("SyscallSsa") + .field("output_expr", &self.output_expr()) + .field("param_expr", &self.param_expr()) + .field("stack_expr", &self.stack_expr()) + .finish() + } +} + // LLIL_INTRINSIC, LLIL_INTRINSIC_SSA pub struct Intrinsic; @@ -150,7 +225,7 @@ where } } -// LLIL_SET_REG, LLIL_SET_REG_SSA, LLIL_SET_REG_PARTIAL_SSA +// LLIL_SET_REG pub struct SetReg; impl<'func, M, F> Operation<'func, M, F, SetReg> @@ -190,7 +265,96 @@ where } } -// LLIL_SET_REG_SPLIT, LLIL_SET_REG_SPLIT_SSA +// LLIL_SET_REG_SSA +pub struct SetRegSsa; + +impl<'func, M, F> Operation<'func, M, F, SetRegSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn dest_reg(&self) -> LowLevelILSSARegisterKind { + let raw_id = RegisterId(self.op.operands[0] as u32); + let reg_kind = LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id) + .expect("Bad register ID"); + let version = self.op.operands[1] as u32; + LowLevelILSSARegisterKind::new_full(reg_kind, version) + } + + pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[2] as usize), + ) + } +} + +impl Debug for Operation<'_, M, F, SetRegSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("SetRegSsa") + .field("address", &self.address()) + .field("size", &self.size()) + .field("dest_reg", &self.dest_reg()) + .field("source_expr", &self.source_expr()) + .finish() + } +} + +// LLIL_SET_REG_PARTIAL_SSA +pub struct SetRegPartialSsa; + +impl<'func, M, F> Operation<'func, M, F, SetRegPartialSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn dest_reg(&self) -> LowLevelILSSARegisterKind { + let full_raw_id = RegisterId(self.op.operands[0] as u32); + let version = self.op.operands[1] as u32; + let partial_raw_id = RegisterId(self.op.operands[2] as u32); + let full_reg = + CoreRegister::new(self.function.arch(), full_raw_id).expect("Bad register ID"); + let partial_reg = + CoreRegister::new(self.function.arch(), partial_raw_id).expect("Bad register ID"); + LowLevelILSSARegisterKind::new_partial(full_reg, partial_reg, version) + } + + pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[3] as usize), + ) + } +} + +impl Debug for Operation<'_, M, F, SetRegPartialSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("SetRegPartialSsa") + .field("address", &self.address()) + .field("size", &self.size()) + .field("dest_reg", &self.dest_reg()) + .field("source_expr", &self.source_expr()) + .finish() + } +} + +// LLIL_SET_REG_SPLIT pub struct SetRegSplit; impl<'func, M, F> Operation<'func, M, F, SetRegSplit> @@ -236,7 +400,63 @@ where } } -// LLIL_SET_FLAG, LLIL_SET_FLAG_SSA +// LLIL_SET_REG_SPLIT_SSA +pub struct SetRegSplitSsa; + +impl<'func, M, F> Operation<'func, M, F, SetRegSplitSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + /// Because of the fixed operand list size we use another expression for the dest high register. + /// + /// NOTE: This should always be an expression of [`RegSsa`]. + pub fn dest_expr_high(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[0] as usize), + ) + } + + /// Because of the fixed operand list size we use another expression for the dest low register. + /// + /// NOTE: This should always be an expression of [`RegSsa`]. + pub fn dest_expr_low(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[1] as usize), + ) + } + + pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[2] as usize), + ) + } +} + +impl Debug for Operation<'_, M, F, SetRegSplitSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("SetRegSplitSsa") + .field("address", &self.address()) + .field("size", &self.size()) + .field("dest_expr_high", &self.dest_expr_high()) + .field("dest_expr_low", &self.dest_expr_low()) + .field("source_expr", &self.source_expr()) + .finish() + } +} + +// LLIL_SET_FLAG pub struct SetFlag; impl<'func, M, F> Operation<'func, M, F, SetFlag> @@ -245,12 +465,10 @@ where F: FunctionForm, { pub fn dest_flag(&self) -> CoreFlag { - // TODO: Error handling? - // TODO: Test this. self.function .arch() .flag_from_id(FlagId(self.op.operands[0] as u32)) - .unwrap() + .expect("Bad flag ID") } pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { @@ -275,7 +493,46 @@ where } } -// LLIL_LOAD, LLIL_LOAD_SSA +// LLIL_SET_FLAG_SSA +pub struct SetFlagSsa; + +impl<'func, M, F> Operation<'func, M, F, SetFlagSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn dest_flag(&self) -> LowLevelILSSAFlag { + let flag = self + .function + .arch() + .flag_from_id(FlagId(self.op.operands[0] as u32)) + .expect("Bad flag ID"); + let version = self.op.operands[1] as u32; + LowLevelILSSAFlag::new(flag, version) + } + + pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[2] as usize), + ) + } +} + +impl Debug for Operation<'_, M, F, SetFlagSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("SetFlagSsa") + .field("address", &self.address()) + .field("dest_flag", &self.dest_flag()) + .field("source_expr", &self.source_expr()) + .finish() + } +} +// LLIL_LOAD pub struct Load; impl<'func, M, F> Operation<'func, M, F, Load> @@ -287,7 +544,7 @@ where self.op.size } - pub fn source_mem_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { LowLevelILExpression::new( self.function, LowLevelExpressionIndex(self.op.operands[0] as usize), @@ -304,12 +561,50 @@ where f.debug_struct("Load") .field("address", &self.address()) .field("size", &self.size()) - .field("source_mem_expr", &self.source_mem_expr()) + .field("source_expr", &self.source_expr()) + .finish() + } +} + +// LLIL_LOAD_SSA +pub struct LoadSsa; + +impl<'func, M, F> Operation<'func, M, F, LoadSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[0] as usize), + ) + } + + pub fn source_memory_version(&self) -> u64 { + self.op.operands[1] + } +} + +impl Debug for Operation<'_, M, F, LoadSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("LoadSsa") + .field("address", &self.address()) + .field("size", &self.size()) + .field("source_expr", &self.source_expr()) .finish() } } -// LLIL_STORE, LLIL_STORE_SSA +// LLIL_STORE pub struct Store; impl<'func, M, F> Operation<'func, M, F, Store> @@ -321,7 +616,7 @@ where self.op.size } - pub fn dest_mem_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + pub fn dest_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { LowLevelILExpression::new( self.function, LowLevelExpressionIndex(self.op.operands[0] as usize), @@ -345,13 +640,63 @@ where f.debug_struct("Store") .field("address", &self.address()) .field("size", &self.size()) - .field("dest_mem_expr", &self.dest_mem_expr()) + .field("dest_expr", &self.dest_expr()) + .field("source_expr", &self.source_expr()) + .finish() + } +} + +// LLIL_STORE_SSA +pub struct StoreSsa; + +impl<'func, M, F> Operation<'func, M, F, StoreSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn dest_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[0] as usize), + ) + } + + pub fn dest_memory_version(&self) -> u64 { + self.op.operands[1] + } + + pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[3] as usize), + ) + } + + pub fn source_memory_version(&self) -> u64 { + self.op.operands[2] + } +} + +impl Debug for Operation<'_, M, F, StoreSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("StoreSsa") + .field("address", &self.address()) + .field("size", &self.size()) + .field("dest_expr", &self.dest_expr()) .field("source_expr", &self.source_expr()) .finish() } } -// LLIL_REG, LLIL_REG_SSLLIL_REG_SSA_PARTIAL +// LLIL_REG pub struct Reg; impl Operation<'_, M, F, Reg> @@ -383,7 +728,80 @@ where } } -// LLIL_REG_SPLIT, LLIL_REG_SPLIT_SSA +// LLIL_REG_SSA +pub struct RegSsa; + +impl Operation<'_, M, F, RegSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn source_reg(&self) -> LowLevelILSSARegisterKind { + let raw_id = RegisterId(self.op.operands[0] as u32); + let reg_kind = LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id) + .expect("Bad register ID"); + let version = self.op.operands[1] as u32; + LowLevelILSSARegisterKind::new_full(reg_kind, version) + } +} + +impl Debug for Operation<'_, M, F, RegSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("RegSsa") + .field("address", &self.address()) + .field("size", &self.size()) + .field("source_reg", &self.source_reg()) + .finish() + } +} + +// LLIL_REG_SSA_PARTIAL +pub struct RegPartialSsa; + +impl Operation<'_, M, F, RegPartialSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn source_reg(&self) -> LowLevelILSSARegisterKind { + let full_raw_id = RegisterId(self.op.operands[0] as u32); + let version = self.op.operands[1] as u32; + let partial_raw_id = RegisterId(self.op.operands[2] as u32); + let full_reg = + CoreRegister::new(self.function.arch(), full_raw_id).expect("Bad register ID"); + let partial_reg = + CoreRegister::new(self.function.arch(), partial_raw_id).expect("Bad register ID"); + LowLevelILSSARegisterKind::new_partial(full_reg, partial_reg, version) + } +} + +impl Debug for Operation<'_, M, F, RegPartialSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("RegPartialSsa") + .field("address", &self.address()) + .field("size", &self.size()) + .field("source_reg", &self.source_reg()) + .finish() + } +} + +// LLIL_REG_SPLIT pub struct RegSplit; impl Operation<'_, M, F, RegSplit> @@ -421,6 +839,50 @@ where } } +// LLIL_REG_SPLIT_SSA +pub struct RegSplitSsa; + +impl Operation<'_, M, F, RegSplitSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn size(&self) -> usize { + self.op.size + } + + pub fn low_reg(&self) -> LowLevelILSSARegisterKind { + let raw_id = RegisterId(self.op.operands[0] as u32); + let reg_kind = LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id) + .expect("Bad register ID"); + let version = self.op.operands[1] as u32; + LowLevelILSSARegisterKind::new_full(reg_kind, version) + } + + pub fn high_reg(&self) -> LowLevelILSSARegisterKind { + let raw_id = RegisterId(self.op.operands[2] as u32); + let reg_kind = LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id) + .expect("Bad register ID"); + let version = self.op.operands[3] as u32; + LowLevelILSSARegisterKind::new_full(reg_kind, version) + } +} + +impl Debug for Operation<'_, M, F, RegSplitSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("RegSplitSsa") + .field("address", &self.address()) + .field("size", &self.size()) + .field("low_reg", &self.low_reg()) + .field("high_reg", &self.high_reg()) + .finish() + } +} + // LLIL_REG_STACK_PUSH pub struct RegStackPush; @@ -630,7 +1092,7 @@ where } } -// LLIL_CALL, LLIL_CALL_SSA +// LLIL_CALL, LLIL_CALL_STACK_ADJUST pub struct Call; impl<'func, M, F> Operation<'func, M, F, Call> @@ -669,6 +1131,165 @@ where } } +// LLIL_CALL_SSA +pub struct CallSsa; + +impl<'func, M, F> Operation<'func, M, F, CallSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn target(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[1] as usize), + ) + } + + /// Get the output expression of the call. + /// + /// NOTE: This is currently always [`CallOutputSsa`]. + pub fn output_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[0] as usize), + ) + } + + /// Get the parameter expression of the call. + /// + /// NOTE: This is currently always [`CallParamSsa`]. + pub fn param_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[3] as usize), + ) + } + + /// Get the stack expression of the call. + /// + /// NOTE: This is currently always [`CallStackSsa`]. + pub fn stack_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> { + LowLevelILExpression::new( + self.function, + LowLevelExpressionIndex(self.op.operands[2] as usize), + ) + } +} + +impl Debug for Operation<'_, M, F, CallSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("CallSsa") + .field("target", &self.target()) + .field("output_expr", &self.output_expr()) + .field("param_expr", &self.param_expr()) + .field("stack_expr", &self.stack_expr()) + .finish() + } +} + +// LLIL_CALL_OUTPUT_SSA +pub struct CallOutputSsa; + +impl Operation<'_, M, F, CallOutputSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn dest_regs(&self) -> Vec> { + let operand_list = self.get_operand_list(1); + + // The operand list contains a list of ([0: reg, 1: version], ...). + let paired_ssa_reg = |paired: &[u64]| { + let raw_id = RegisterId(paired[0] as u32); + let version = paired[1] as u32; + let reg_kind = LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id) + .expect("Bad register ID"); + LowLevelILSSARegisterKind::new_full(reg_kind, version) + }; + + operand_list.chunks_exact(2).map(paired_ssa_reg).collect() + } + + pub fn dest_memory_version(&self) -> u64 { + self.op.operands[0] + } +} + +impl Debug for Operation<'_, M, F, CallOutputSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("CallOutputSsa") + .field("dest_regs", &self.dest_regs()) + .finish() + } +} + +// LLIL_CALL_PARAM_SSA +pub struct CallParamSsa; + +impl<'func, M, F> Operation<'func, M, F, CallParamSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn param_exprs(&self) -> Vec> { + self.get_operand_list(0) + .into_iter() + .map(|val| LowLevelExpressionIndex(val as usize)) + .map(|expr_idx| LowLevelILExpression::new(self.function, expr_idx)) + .collect() + } +} + +impl Debug for Operation<'_, M, F, CallParamSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("CallParamSsa") + .field("param_exprs", &self.param_exprs()) + .finish() + } +} + +// LLIL_CALL_STACK_SSA +pub struct CallStackSsa; + +impl Operation<'_, M, F, CallStackSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + pub fn source_reg(&self) -> LowLevelILSSARegisterKind { + let raw_id = RegisterId(self.op.operands[0] as u32); + let reg_kind = LowLevelILRegisterKind::from_raw(&self.function.arch(), raw_id) + .expect("Bad register ID"); + let version = self.op.operands[1] as u32; + LowLevelILSSARegisterKind::new_full(reg_kind, version) + } +} + +impl Debug for Operation<'_, M, F, CallStackSsa> +where + M: FunctionMutability, + F: FunctionForm, +{ + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("CallStackSsa") + .field("source_reg", &self.source_reg()) + .finish() + } +} + // LLIL_RET pub struct Ret; @@ -1256,14 +1877,24 @@ pub trait OperationArguments: 'static {} impl OperationArguments for NoArgs {} impl OperationArguments for Pop {} impl OperationArguments for Syscall {} +impl OperationArguments for SyscallSsa {} impl OperationArguments for Intrinsic {} impl OperationArguments for SetReg {} +impl OperationArguments for SetRegSsa {} +impl OperationArguments for SetRegPartialSsa {} impl OperationArguments for SetRegSplit {} +impl OperationArguments for SetRegSplitSsa {} impl OperationArguments for SetFlag {} +impl OperationArguments for SetFlagSsa {} impl OperationArguments for Load {} +impl OperationArguments for LoadSsa {} impl OperationArguments for Store {} +impl OperationArguments for StoreSsa {} impl OperationArguments for Reg {} +impl OperationArguments for RegSsa {} +impl OperationArguments for RegPartialSsa {} impl OperationArguments for RegSplit {} +impl OperationArguments for RegSplitSsa {} impl OperationArguments for RegStackPush {} impl OperationArguments for RegStackPop {} impl OperationArguments for Flag {} @@ -1271,6 +1902,10 @@ impl OperationArguments for FlagBit {} impl OperationArguments for Jump {} impl OperationArguments for JumpTo {} impl OperationArguments for Call {} +impl OperationArguments for CallSsa {} +impl OperationArguments for CallOutputSsa {} +impl OperationArguments for CallParamSsa {} +impl OperationArguments for CallStackSsa {} impl OperationArguments for Ret {} impl OperationArguments for If {} impl OperationArguments for Goto {} diff --git a/rust/tests/low_level_il.rs b/rust/tests/low_level_il.rs index 88d915dc..db8cc549 100644 --- a/rust/tests/low_level_il.rs +++ b/rust/tests/low_level_il.rs @@ -7,7 +7,7 @@ use binaryninja::low_level_il::expression::{ use binaryninja::low_level_il::instruction::{ InstructionHandler, LowLevelILInstructionKind, LowLevelInstructionIndex, }; -use binaryninja::low_level_il::{LowLevelILRegisterKind, VisitorAction}; +use binaryninja::low_level_il::{LowLevelILRegisterKind, LowLevelILSSARegisterKind, VisitorAction}; use std::path::PathBuf; #[test] @@ -225,3 +225,86 @@ fn test_llil_visitor() { }; } } + +#[test] +fn test_llil_ssa() { + let _session = Session::new().expect("Failed to initialize session"); + let out_dir = env!("OUT_DIR").parse::().unwrap(); + let view = binaryninja::load(out_dir.join("atox.obj")).expect("Failed to create view"); + let image_base = view.original_image_base(); + let platform = view.default_platform().unwrap(); + + // Sample function: __crt_strtox::c_string_character_source::validate + let sample_function = view.function_at(&platform, image_base + 0x2bd80).unwrap(); + let llil_function = sample_function.low_level_il().unwrap(); + let llil_ssa_function = llil_function.ssa_form().expect("Valid SSA form"); + + let llil_ssa_basic_blocks = llil_ssa_function.basic_blocks(); + let mut llil_ssa_basic_block_iter = llil_ssa_basic_blocks.iter(); + let first_basic_block = llil_ssa_basic_block_iter.next().unwrap(); + let mut llil_instr_iter = first_basic_block.iter(); + + // 0 @ 0002bd80 (LLIL_SET_REG_SSA.d edi#1 = (LLIL_REG_SSA.d edi#0)) + let ssa_instr_0 = llil_instr_iter.next().unwrap(); + assert_eq!(ssa_instr_0.index, LowLevelInstructionIndex(0)); + assert_eq!(ssa_instr_0.address(), image_base + 0x0002bd80); + println!("{:?}", ssa_instr_0); + println!("{:?}", ssa_instr_0.kind()); + match ssa_instr_0.kind() { + LowLevelILInstructionKind::SetRegSsa(op) => { + assert_eq!(op.size(), 4); + match op.dest_reg() { + LowLevelILSSARegisterKind::Full { kind, version } => { + assert_eq!(kind.name(), "edi"); + assert_eq!(version, 1); + } + _ => panic!("Expected LowLevelILSSARegisterKind::Full"), + } + assert_eq!(op.source_expr().index, LowLevelExpressionIndex(0)); + } + _ => panic!("Expected SetRegSsa"), + } + + // 1 @ 0002bd82 (LLIL_STORE_SSA.d [(LLIL_SUB.d (LLIL_REG_SSA.d esp#0) - (LLIL_CONST.d 4)) {__saved_ebp}].d = (LLIL_REG_SSA.d ebp#0) @ mem#0 -> mem#1) + let ssa_instr_1 = llil_instr_iter.next().unwrap(); + assert_eq!(ssa_instr_1.index, LowLevelInstructionIndex(1)); + assert_eq!(ssa_instr_1.address(), image_base + 0x0002bd82); + println!("{:?}", ssa_instr_1); + println!("{:?}", ssa_instr_1.kind()); + match ssa_instr_1.kind() { + LowLevelILInstructionKind::StoreSsa(op) => { + assert_eq!(op.size(), 4); + let source_expr = op.source_expr(); + let source_memory_version = op.source_memory_version(); + assert_eq!(source_memory_version, 0); + assert_eq!(source_expr.index, LowLevelExpressionIndex(5)); + let dest_expr = op.dest_expr(); + let dest_memory_version = op.dest_memory_version(); + assert_eq!(dest_memory_version, 1); + assert_eq!(dest_expr.index, LowLevelExpressionIndex(4)); + } + _ => panic!("Expected StoreSsa"), + } + + // 34 @ 0002bdc7 (LLIL_CALL_SSA eax#8, edx#5, ecx#6, mem#23 = call((LLIL_EXTERN_PTR.d __CrtDbgReportW), stack = esp#18 @ mem#22)) + let ssa_instr_34 = llil_ssa_function + .instruction_from_index(LowLevelInstructionIndex(34)) + .expect("Valid instruction"); + assert_eq!(ssa_instr_34.index, LowLevelInstructionIndex(34)); + assert_eq!(ssa_instr_34.address(), image_base + 0x0002bdc7); + println!("{:?}", ssa_instr_34); + println!("{:?}", ssa_instr_34.kind()); + match ssa_instr_34.kind() { + LowLevelILInstructionKind::CallSsa(op) => match op.target().kind() { + LowLevelILExpressionKind::ExternPtr(extern_ptr) => { + assert_eq!(extern_ptr.size(), 4); + let extern_sym = view + .symbol_by_address(extern_ptr.value()) + .expect("Valid symbol"); + assert_eq!(extern_sym.short_name(), "__CrtDbgReportW".into()) + } + _ => panic!("Expected ExternPtr"), + }, + _ => panic!("Expected CallSsa"), + } +} -- cgit v1.3.1