diff options
| author | Joe Rozner <joe@deadbytes.net> | 2024-08-20 21:55:05 -0700 |
|---|---|---|
| committer | Alexander Taylor <alex@vector35.com> | 2024-08-22 19:35:30 -0400 |
| commit | f038c7c9a9646c2a126d693eab7d297cd25565e3 (patch) | |
| tree | 51b1366dabb78fad2e2a7cc0518b3604620f66d8 /arch/msp430/src/lift.rs | |
| parent | 2a9d07d7fefe5a6d7f7a482397060a96782142ac (diff) | |
Add msp430 architecture
Diffstat (limited to 'arch/msp430/src/lift.rs')
| -rw-r--r-- | arch/msp430/src/lift.rs | 670 |
1 files changed, 670 insertions, 0 deletions
diff --git a/arch/msp430/src/lift.rs b/arch/msp430/src/lift.rs new file mode 100644 index 00000000..85d204e1 --- /dev/null +++ b/arch/msp430/src/lift.rs @@ -0,0 +1,670 @@ +use crate::architecture::offset_to_absolute; +use crate::flag::{Flag, FlagWrite}; +use crate::register::Register; +use crate::Msp430; + +use binaryninja::{ + architecture::FlagCondition, + llil::{Label, LiftedNonSSA, Lifter, Mutable, NonSSA}, +}; + +use msp430_asm::emulate::Emulated; +use msp430_asm::instruction::Instruction; +use msp430_asm::jxx::Jxx; +use msp430_asm::operand::{Operand, OperandWidth}; +use msp430_asm::single_operand::SingleOperand; +use msp430_asm::two_operand::TwoOperand; + +use log::info; + +macro_rules! auto_increment { + ($src:expr, $il:ident) => { + if let Operand::RegisterIndirectAutoIncrement(r) = $src { + $il.set_reg( + 2, + Register::try_from(*r as u32).unwrap(), + $il.add( + 2, + $il.reg(2, Register::try_from(*r as u32).unwrap()), + $il.const_int(2, 2), + ), + ) + .append(); + } + }; +} + +macro_rules! one_operand { + ($source:expr, $il:ident, $op:ident) => { + match $source { + Operand::RegisterDirect(r) => $il + .set_reg(2, Register::try_from(*r as u32).unwrap(), $op) + .append(), + Operand::Indexed((r, offset)) => $il + .store( + 2, + $il.add( + 2, + $il.reg(2, Register::try_from(*r as u32).unwrap()), + $il.const_int(2, *offset as u64), + ), + $op, + ) + .append(), + Operand::Symbolic(offset) => $il + .store(2, $il.add(2, $il.reg(2, Register::Pc), *offset as u64), $op) + .append(), + Operand::Absolute(val) => $il.store(2, $il.const_ptr(*val as u64), $op).append(), + Operand::Immediate(_) => $op.append(), + Operand::RegisterIndirect(r) => $il + .store(2, $il.reg(2, Register::try_from(*r as u32).unwrap()), $op) + .append(), + Operand::RegisterIndirectAutoIncrement(r) => { + $il.store(2, $il.reg(2, Register::try_from(*r as u32).unwrap()), $op) + .append(); + $il.set_reg( + 2, + Register::try_from(*r as u32).unwrap(), + $il.add( + 2, + $il.reg(2, Register::try_from(*r as u32).unwrap()), + $il.const_int(2, 2), + ), + ) + .append() + } + _ => { + unreachable!() + } + }; + }; +} + +macro_rules! two_operand { + ($destination:expr, $il:ident, $op:ident) => { + match $destination { + Operand::RegisterDirect(r) => $il + .set_reg(2, Register::try_from(*r as u32).unwrap(), $op) + .append(), + Operand::Indexed((r, offset)) => $il + .store( + 2, + $il.add( + 2, + $il.reg(2, Register::try_from(*r as u32).unwrap()), + $il.const_int(2, *offset as u64), + ), + $op, + ) + .append(), + Operand::Symbolic(offset) => $il + .store(2, $il.add(2, $il.reg(2, Register::Pc), *offset as u64), $op) + .append(), + Operand::Absolute(val) => $il.store(2, $il.const_ptr(*val as u64), $op).append(), + _ => { + unreachable!() + } + }; + }; +} + +macro_rules! emulated { + ($inst:ident, $il:ident, $op:ident) => { + match $inst.destination() { + Some(Operand::RegisterDirect(r)) => $il + .set_reg(2, Register::try_from(*r as u32).unwrap(), $op) + .append(), + Some(Operand::Indexed((r, offset))) => $il + .store( + 2, + $il.add( + 2, + $il.reg(2, Register::try_from(*r as u32).unwrap()), + $il.const_int(2, *offset as u64), + ), + $op, + ) + .append(), + Some(Operand::Symbolic(offset)) => $il + .store(2, $il.add(2, $il.reg(2, Register::Pc), *offset as u64), $op) + .append(), + Some(Operand::Absolute(val)) => $il.store(2, $il.const_ptr(*val as u64), $op).append(), + _ => { + unreachable!() + } + }; + }; +} + +macro_rules! conditional_jump { + ($addr:ident, $inst:ident, $cond:ident, $il:ident) => { + let true_addr = offset_to_absolute($addr, $inst.offset()); + let false_addr = $addr + $inst.size() as u64; + let mut new_true = Label::new(); + let mut new_false = Label::new(); + + let true_label = $il.label_for_address(true_addr); + let false_label = $il.label_for_address(false_addr); + + $il.if_expr( + $cond, + true_label.unwrap_or_else(|| &new_true), + false_label.unwrap_or_else(|| &new_false), + ) + .append(); + + if true_label.is_none() { + $il.mark_label(&mut new_true); + } + + $il.goto(true_label.unwrap_or_else(|| &new_true)).append(); + + if false_label.is_none() { + $il.mark_label(&mut new_false); + } + }; +} + +pub(crate) fn lift_instruction(inst: &Instruction, addr: u64, il: &Lifter<Msp430>) { + match inst { + Instruction::Rrc(inst) => { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + let src = il.const_int(size, 1); + let dest = lift_source_operand(inst.source(), size, il); + let op = match inst.operand_width() { + Some(OperandWidth::Byte) => { + il.sx(2, il.rrc(size, dest, src).with_flag_write(FlagWrite::All)) + } + Some(OperandWidth::Word) | None => { + il.rrc(size, dest, src).with_flag_write(FlagWrite::All) + } + }; + one_operand!(inst.source(), il, op); + } + Instruction::Swpb(inst) => { + let src = lift_source_operand(inst.source(), 2, il); + let op = il.rol(2, src, il.const_int(2, 8)); + one_operand!(inst.source(), il, op); + } + Instruction::Rra(inst) => { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + let src = il.const_int(size, 1); + let dest = lift_source_operand(inst.source(), size, il); + let op = match inst.operand_width() { + Some(OperandWidth::Byte) => { + il.sx(2, il.ror(size, dest, src).with_flag_write(FlagWrite::Cnz)) + } + Some(OperandWidth::Word) | None => { + il.ror(size, dest, src).with_flag_write(FlagWrite::Cnz) + } + }; + one_operand!(inst.source(), il, op); + il.set_flag(Flag::V, il.const_int(0, 0)).append(); + } + Instruction::Sxt(inst) => { + // source is always 1 byte and instruction is always 2 bytes for sxt because we're sign + // extending the low byte into the high and the result is always 2 bytes + let src = lift_source_operand(inst.source(), 1, il); + let op = il.sx(2, src).with_flag_write(FlagWrite::Nz); + one_operand!(inst.source(), il, op); + il.set_flag(Flag::V, il.const_int(0, 0)).append(); + il.set_flag(Flag::C, il.not(0, il.flag(Flag::Z))).append(); + } + Instruction::Push(inst) => { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + let src = lift_source_operand(inst.source(), size, il); + il.push(2, src).append(); + auto_increment!(inst.source(), il); + } + Instruction::Call(inst) => { + // TODO: verify the special autoincrement behavior Josh implemented? + let src = if let Operand::Immediate(src) = inst.source() { + il.const_ptr(*src as u64) + } else { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + + lift_source_operand(inst.source(), size, il) + }; + il.call(src).append(); + auto_increment!(inst.source(), il); + } + Instruction::Reti(_) => { + il.set_reg(2, Register::Sr, il.pop(2)) + .with_flag_write(FlagWrite::All) + .append(); + il.ret(il.pop(2)).append(); + } + + // Jxx instructions + Instruction::Jnz(inst) => { + let cond = il.flag_cond(FlagCondition::LLFC_NE); + conditional_jump!(addr, inst, cond, il); + } + Instruction::Jz(inst) => { + let cond = il.flag_cond(FlagCondition::LLFC_E); + conditional_jump!(addr, inst, cond, il); + } + Instruction::Jlo(inst) => { + let cond = il.flag_cond(FlagCondition::LLFC_ULT); + conditional_jump!(addr, inst, cond, il); + } + Instruction::Jc(inst) => { + let cond = il.flag_cond(FlagCondition::LLFC_UGE); + conditional_jump!(addr, inst, cond, il); + } + Instruction::Jn(inst) => { + let cond = il.flag_cond(FlagCondition::LLFC_NEG); + conditional_jump!(addr, inst, cond, il); + } + Instruction::Jge(inst) => { + let cond = il.flag_cond(FlagCondition::LLFC_SGE); + conditional_jump!(addr, inst, cond, il); + } + Instruction::Jl(inst) => { + let cond = il.flag_cond(FlagCondition::LLFC_SLT); + conditional_jump!(addr, inst, cond, il); + } + Instruction::Jmp(inst) => { + let fixed_addr = offset_to_absolute(addr, inst.offset()); + let label = il.label_for_address(fixed_addr); + match label { + Some(label) => { + il.goto(label).append(); + } + None => { + il.jump(il.const_ptr(fixed_addr)).append(); + } + } + } + + // two operand instructions + Instruction::Mov(inst) => { + let size = width_to_size(inst.operand_width()); + let src = match inst.operand_width() { + OperandWidth::Byte => il + .sx(2, lift_source_operand(inst.source(), size, il)) + .build(), + OperandWidth::Word => lift_source_operand(inst.source(), size, il), + }; + two_operand!(inst.destination(), il, src); + auto_increment!(inst.source(), il); + } + Instruction::Add(inst) => { + let size = width_to_size(inst.operand_width()); + let src = lift_source_operand(inst.source(), size, il); + let dest = lift_source_operand(inst.destination(), size, il); + let op = match inst.operand_width() { + OperandWidth::Byte => { + il.sx(2, il.add(size, src, dest).with_flag_write(FlagWrite::All)) + } + OperandWidth::Word => il.add(size, src, dest).with_flag_write(FlagWrite::All), + }; + two_operand!(inst.destination(), il, op); + auto_increment!(inst.source(), il); + } + Instruction::Addc(_) => { + il.unimplemented().append(); + } + Instruction::Subc(_) => { + il.unimplemented().append(); + } + Instruction::Sub(inst) => { + let size = width_to_size(inst.operand_width()); + let src = lift_source_operand(inst.source(), size, il); + let dest = lift_source_operand(inst.destination(), size, il); + let op = match inst.operand_width() { + OperandWidth::Byte => { + il.sx(2, il.sub(size, src, dest).with_flag_write(FlagWrite::All)) + } + OperandWidth::Word => il.sub(size, src, dest).with_flag_write(FlagWrite::All), + }; + two_operand!(inst.destination(), il, op); + auto_increment!(inst.source(), il); + } + Instruction::Cmp(inst) => { + let size = width_to_size(inst.operand_width()); + let src = lift_source_operand(inst.source(), size, il); + let dest = lift_source_operand(inst.destination(), size, il); + il.sub(size, dest, src) + .with_flag_write(FlagWrite::All) + .append(); + auto_increment!(inst.source(), il); + } + Instruction::Dadd(_) => { + il.unimplemented().append(); + } + Instruction::Bit(inst) => { + let size = width_to_size(inst.operand_width()); + let src = lift_source_operand(inst.source(), size, il); + let dest = lift_source_operand(inst.destination(), size, il); + il.and(size, src, dest) + .with_flag_write(FlagWrite::Nz) + .append(); + il.set_flag(Flag::V, il.const_int(0, 0)).append(); + il.set_flag(Flag::C, il.not(0, il.flag(Flag::Z))).append(); + auto_increment!(inst.source(), il); + } + Instruction::Bic(inst) => { + let size = width_to_size(inst.operand_width()); + let src = lift_source_operand(inst.source(), size, il); + let dest = lift_source_operand(inst.destination(), size, il); + let op = match inst.operand_width() { + OperandWidth::Byte => il.sx(2, il.and(size, il.not(size, src), dest)), + OperandWidth::Word => il.and(size, il.not(size, src), dest), + }; + two_operand!(inst.destination(), il, op); + auto_increment!(inst.source(), il); + } + Instruction::Bis(inst) => { + let size = width_to_size(inst.operand_width()); + let src = lift_source_operand(inst.source(), size, il); + let dest = lift_source_operand(inst.destination(), size, il); + let op = match inst.operand_width() { + OperandWidth::Byte => il.sx(2, il.or(size, src, dest)), + OperandWidth::Word => il.or(size, src, dest), + }; + two_operand!(inst.destination(), il, op); + auto_increment!(inst.source(), il); + } + Instruction::Xor(inst) => { + let size = width_to_size(inst.operand_width()); + let src = lift_source_operand(inst.source(), size, il); + let dest = lift_source_operand(inst.destination(), size, il); + let op = match inst.operand_width() { + OperandWidth::Byte => { + il.sx(2, il.xor(size, src, dest).with_flag_write(FlagWrite::Nvz)) + } + OperandWidth::Word => il.xor(size, src, dest).with_flag_write(FlagWrite::Nvz), + }; + two_operand!(inst.destination(), il, op); + il.set_flag(Flag::C, il.not(0, il.flag(Flag::Z))).append(); + auto_increment!(inst.source(), il); + } + Instruction::And(inst) => { + let size = width_to_size(inst.operand_width()); + let src = lift_source_operand(inst.source(), size, il); + let dest = lift_source_operand(inst.destination(), size, il); + let op = match inst.operand_width() { + OperandWidth::Byte => { + il.sx(2, il.and(size, src, dest).with_flag_write(FlagWrite::Nz)) + } + OperandWidth::Word => il.and(size, src, dest).with_flag_write(FlagWrite::Nz), + }; + two_operand!(inst.destination(), il, op); + il.set_flag(Flag::V, il.const_int(0, 0)).append(); + il.set_flag(Flag::C, il.not(0, il.flag(Flag::Z))).append(); + auto_increment!(inst.source(), il); + } + + // emulated + Instruction::Adc(_) => { + il.unimplemented().append(); + } + Instruction::Br(inst) => { + let dest = if let Some(Operand::Immediate(dest)) = inst.destination() { + if let Some(label) = il.label_for_address(*dest as u64) { + il.goto(label).append(); + return; + } else { + il.const_ptr(*dest as u64) + } + } else { + lift_source_operand(&inst.destination().unwrap(), 2, il) + }; + + il.jump(dest).append(); + } + Instruction::Clr(inst) => { + let op = il.const_int(2, 0); + emulated!(inst, il, op); + } + Instruction::Clrc(_) => { + // TODO: should we lift clearing the C bit in the SR register as well? + il.set_flag(Flag::C, il.const_int(0, 0)).append(); + } + Instruction::Clrn(_) => { + // TODO: should we lift clearing the N bit in the SR register as well? + il.set_flag(Flag::N, il.const_int(0, 0)).append(); + } + Instruction::Clrz(_) => { + // TODO: should we lift clearing the Z bit in the SR register as well? + il.set_flag(Flag::Z, il.const_int(0, 0)).append(); + } + Instruction::Dadc(_) => { + il.unimplemented().append(); + } + Instruction::Dec(inst) => { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + let dest = lift_source_operand(&inst.destination().unwrap(), size, il); + let op = match inst.operand_width() { + Some(OperandWidth::Byte) => il.sx( + 2, + il.sub(size, dest, il.const_int(size, 1)) + .with_flag_write(FlagWrite::All), + ), + Some(OperandWidth::Word) | None => il + .sub(size, dest, il.const_int(size, 1)) + .with_flag_write(FlagWrite::All), + }; + emulated!(inst, il, op); + } + Instruction::Decd(inst) => { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + let dest = lift_source_operand(&inst.destination().unwrap(), size, il); + let op = match inst.operand_width() { + Some(OperandWidth::Byte) => il.sx( + 2, + il.sub(size, dest, il.const_int(size, 2)) + .with_flag_write(FlagWrite::All), + ), + Some(OperandWidth::Word) | None => il + .sub(size, dest, il.const_int(size, 2)) + .with_flag_write(FlagWrite::All), + }; + emulated!(inst, il, op); + } + Instruction::Dint(_) => { + // If GIE flag is ever exposed this should clear it + } + Instruction::Eint(_) => { + // If GIE flag is ever exposed this should set it + } + Instruction::Inc(inst) => { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + let dest = lift_source_operand(&inst.destination().unwrap(), size, il); + let op = match inst.operand_width() { + Some(OperandWidth::Byte) => il.sx( + 2, + il.add(size, dest, il.const_int(size, 1)) + .with_flag_write(FlagWrite::All), + ), + Some(OperandWidth::Word) | None => il + .add(size, dest, il.const_int(size, 1)) + .with_flag_write(FlagWrite::All), + }; + emulated!(inst, il, op); + } + Instruction::Incd(inst) => { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + let dest = lift_source_operand(&inst.destination().unwrap(), size, il); + let op = match inst.operand_width() { + Some(OperandWidth::Byte) => il.sx( + 2, + il.add(size, dest, il.const_int(size, 2)) + .with_flag_write(FlagWrite::All), + ), + Some(OperandWidth::Word) | None => il + .add(size, dest, il.const_int(size, 2)) + .with_flag_write(FlagWrite::All), + }; + emulated!(inst, il, op); + } + Instruction::Inv(inst) => { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + let dest = lift_source_operand(&inst.destination().unwrap(), size, il); + let op = match inst.operand_width() { + Some(OperandWidth::Byte) => { + il.sx(2, il.not(size, dest).with_flag_write(FlagWrite::Nvz)) + } + Some(OperandWidth::Word) | None => { + il.not(size, dest).with_flag_write(FlagWrite::Nvz) + } + }; + emulated!(inst, il, op); + il.set_flag(Flag::C, il.not(0, il.flag(Flag::Z))).append(); + } + Instruction::Nop(_) => { + il.nop().append(); + } + Instruction::Pop(inst) => { + if let Some(Operand::RegisterDirect(r)) = inst.destination() { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + il.set_reg(size, Register::try_from(*r as u32).unwrap(), il.pop(2)) + .append(); + } else { + info!("pop: invalid destination operand"); + } + } + Instruction::Ret(_) => { + il.ret(il.pop(2)).append(); + } + Instruction::Rla(inst) => { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + let src = il.const_int(size, 1); + let dest = lift_source_operand(&inst.destination().unwrap(), size, il); + let op = match inst.operand_width() { + Some(OperandWidth::Byte) => { + il.sx(2, il.rol(size, dest, src).with_flag_write(FlagWrite::All)) + } + Some(OperandWidth::Word) | None => { + il.rol(size, dest, src).with_flag_write(FlagWrite::All) + } + }; + emulated!(inst, il, op); + } + Instruction::Rlc(inst) => { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + let src = il.const_int(size, 1); + let dest = lift_source_operand(&inst.destination().unwrap(), size, il); + let op = match inst.operand_width() { + Some(OperandWidth::Byte) => { + il.sx(2, il.rlc(size, dest, src).with_flag_write(FlagWrite::All)) + } + Some(OperandWidth::Word) | None => { + il.rlc(size, dest, src).with_flag_write(FlagWrite::All) + } + }; + emulated!(inst, il, op); + } + Instruction::Sbc(_) => { + il.unimplemented().append(); + } + Instruction::Setc(_) => { + // TODO: should we lift setting the C bit in the SR register as well? + il.set_flag(Flag::C, il.const_int(0, 1)).append(); + } + Instruction::Setn(_) => { + // TODO: should we lift setting the N bit in the SR register as well? + il.set_flag(Flag::N, il.const_int(0, 1)).append(); + } + Instruction::Setz(_) => { + // TODO: should we lift setting the Z bit in the SR register as well? + il.set_flag(Flag::Z, il.const_int(0, 1)).append(); + } + Instruction::Tst(inst) => { + let size = match inst.operand_width() { + Some(width) => width_to_size(width), + None => 2, + }; + let dest = lift_source_operand(&inst.destination().unwrap(), size, il); + il.sub(size, dest, il.const_int(size, 0)) + .with_flag_write(FlagWrite::Nz) + .append(); + il.set_flag(Flag::V, il.const_int(0, 0)).append(); + il.set_flag(Flag::C, il.const_int(0, 1)).append(); + } + } +} + +fn lift_source_operand<'a>( + operand: &Operand, + size: usize, + il: &'a Lifter<Msp430>, +) -> binaryninja::llil::Expression< + 'a, + Msp430, + Mutable, + NonSSA<LiftedNonSSA>, + binaryninja::llil::ValueExpr, +> { + match operand { + Operand::RegisterDirect(r) => il.reg(size, Register::try_from(*r as u32).unwrap()), + Operand::Indexed((r, offset)) => il + .load( + size, + il.add( + 2, + il.reg(2, Register::try_from(*r as u32).unwrap()), + il.const_int(2, *offset as u64), + ), + ) + .build(), + // should we add offset to addr here rather than lifting to the register since we know where PC is? + Operand::Symbolic(offset) => il + .load( + size, + il.add(2, il.reg(2, Register::Pc), il.const_int(2, *offset as u64)), + ) + .build(), + Operand::Absolute(addr) => il.load(size, il.const_ptr(*addr as u64)).build(), + // these are the same, we need to autoincrement in a separate il instruction + Operand::RegisterIndirect(r) | Operand::RegisterIndirectAutoIncrement(r) => il + .load(size, il.reg(2, Register::try_from(*r as u32).unwrap())) + .build(), + Operand::Immediate(val) => il.const_int(size, *val as u64), + Operand::Constant(val) => il.const_int(size, *val as u64), + } +} + +fn width_to_size(width: &OperandWidth) -> usize { + match width { + OperandWidth::Byte => 1, + OperandWidth::Word => 2, + } +} |
