use crate::arch::x86::{DecodedInsn, Mnemonic, format_intel};
use crate::ast::Stmt;
use super::{Candidate, Pattern, PatternCtx};
pub struct Mov;
impl Pattern for Mov {
fn name(&self) -> &'static str {
"mov"
}
fn tentative(
&self,
ctx: &PatternCtx,
insns: &[DecodedInsn],
start: usize,
) -> Option<Candidate> {
let ins = insns.get(start)?;
if ins.iced.mnemonic() != Mnemonic::Mov {
return None;
}
let (dst, src) = split_two_operands(&format_intel(&ins.iced), "mov ")?;
let sp = ctx.sp_delta_at.get(&ins.iced.ip()).copied();
let dst = crate::arch::x86::rename_operand_in_ctx(&dst, sp);
let src = crate::arch::x86::rename_operand_in_ctx(&src, sp);
Some(Candidate {
pattern: self.name(),
start,
consumed: 1,
priority: 50,
stmts: vec![Stmt::Move {
dst,
src,
bytes: ins.original_bytes.clone(),
}],
})
}
}
pub(super) fn split_two_operands(full: &str, prefix: &str) -> Option<(String, String)> {
let rest = full.strip_prefix(prefix)?;
let (dst, src) = rest.split_once(',')?;
Some((dst.trim().to_string(), src.trim().to_string()))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::arch::x86::{Bitness, decode};
use std::collections::HashMap;
fn ctx() -> PatternCtx<'static> {
let map: &'static HashMap<u64, String> = Box::leak(Box::new(HashMap::new()));
let sp_map: &'static HashMap<u64, i64> = Box::leak(Box::new(HashMap::new()));
PatternCtx {
fn_addr_start: 0,
fn_addr_end: u64::MAX,
name_at: map,
sp_delta_at: sp_map,
}
}
#[test]
fn lifts_mov_reg_reg() {
let bytes = [0x89, 0xc3]; let insns = decode(Bitness::Bits32, &bytes, 0).unwrap();
let c = Mov.tentative(&ctx(), &insns, 0).expect("match");
if let Some(Stmt::Move { dst, src, .. }) = c.stmts.first() {
assert_eq!(dst, "ebx");
assert_eq!(src, "eax");
} else {
panic!("expected Move");
}
}
#[test]
fn lifts_mov_mem_imm() {
let bytes = [0xc7, 0x45, 0xfc, 0x05, 0x00, 0x00, 0x00];
let insns = decode(Bitness::Bits32, &bytes, 0).unwrap();
let c = Mov.tentative(&ctx(), &insns, 0).expect("match");
if let Some(Stmt::Move { dst, src, .. }) = c.stmts.first() {
assert_eq!(dst, "var_4");
assert_eq!(src, "5");
}
}
#[test]
fn skips_non_mov() {
let bytes = [0x53]; let insns = decode(Bitness::Bits32, &bytes, 0).unwrap();
assert!(Mov.tentative(&ctx(), &insns, 0).is_none());
}
}