You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
bddisasm/bindings/rsbddisasm/bddisasm/examples/emulator.rs

160 lines
4.9 KiB

extern crate bddisasm;
use std::fmt;
use bddisasm::{DecodeMode, DecodedInstruction, Decoder, Mnemonic, OpRegType, Operand};
use anyhow::Result;
use clap::{App, Arg};
static ABOUT: &str = "
Simple emulator example.
Supports only a few 64-bit instructions (MOV, INC, DEC) and operand types (register, and immediate).
All registers are initially set to 0.
";
/// Holds the reigster state.
#[derive(Debug, Default)]
struct Context {
/// Registers are in the Intel documented order (RAX, RCX, RDX, etc).
regs: [u64; 16],
}
impl Context {
/// Emulates a 64-bit instruction.
pub fn emulate(&mut self, ins: &DecodedInstruction) -> Result<()> {
let operands = ins.operands();
match ins.mnemonic() {
Mnemonic::MOV => {
self.set_operand_value(&operands[0], self.get_operand_value(&operands[1])?)?
}
Mnemonic::INC => self.set_operand_value(
&operands[0],
self.get_operand_value(&operands[0])?.wrapping_add(1),
)?,
Mnemonic::DEC => self.set_operand_value(
&operands[0],
self.get_operand_value(&operands[0])?.wrapping_sub(1),
)?,
_ => anyhow::bail!("Unsupported instruction: {}", ins),
}
Ok(())
}
fn get_operand_value(&self, op: &Operand) -> Result<u64> {
if let Some(reg) = op.info.as_reg() {
if reg.kind != OpRegType::Gpr {
anyhow::bail!("Unsupported register type: {}", reg.kind)
} else {
match reg.size {
1 => {
if reg.is_high8 {
// For `AH`, `BH`, etc.
Ok((self.regs[reg.index] & 0xFF00) >> 8)
} else {
Ok(self.regs[reg.index] & 0xFF)
}
}
2 => Ok(self.regs[reg.index] & 0xFFFF),
4 => Ok(self.regs[reg.index] & 0xFFFFFFFF),
8 => Ok(self.regs[reg.index]),
_ => unreachable!("Unexpected GPR size: {}", reg.size),
}
}
} else if let Some(imm) = op.info.as_imm() {
Ok(imm)
} else {
anyhow::bail!("Unsupported operand type: {}", op.info)
}
}
fn set_operand_value(&mut self, op: &Operand, value: u64) -> Result<()> {
if let Some(reg) = op.info.as_reg() {
if reg.kind != OpRegType::Gpr {
anyhow::bail!("Unsupported register type: {}", reg.kind)
} else {
let orig_value = self.regs[reg.index];
let value = match reg.size {
1 => {
if reg.is_high8 {
let value = value << 8;
value | (orig_value & 0xFFFFFFFFFFFF00FF)
} else {
value | (orig_value & 0xFFFFFFFFFFFFFF00)
}
}
2 => value | (orig_value & 0xFFFFFFFFFFFF0000),
4 => {
// The upper 32-bits are always set to 0.
value & 0xFFFFFFFF
}
8 => value,
_ => unreachable!("Unexpected GPR size: {}", reg.size),
};
self.regs[reg.index] = value;
Ok(())
}
} else {
anyhow::bail!("Unsupported operand type: {}", op.info)
}
}
}
impl fmt::Display for Context {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let names = [
"RAX", "RCX", "RDX", "RBX", "RSP", "RBP", "RSI", "RDI", " R8", " R9", "R10", "R11",
"R12", "R13", "R14", "R15",
];
for (i, name) in names.iter().enumerate() {
if let Err(e) = write!(f, "{}: {:#018x} ", name, self.regs[i]) {
return Err(e);
}
if (i + 1) % 4 == 0 {
if let Err(e) = writeln!(f) {
return Err(e);
}
}
}
Ok(())
}
}
fn main() -> Result<()> {
let matches = App::new("emulator")
.about(ABOUT)
.arg(
Arg::with_name("INPUT")
.short("f")
.long("file")
.value_name("PATH")
.help("Path to the binary file from which to load code")
.takes_value(true),
)
.get_matches();
let code = std::fs::read(matches.value_of("INPUT").unwrap())?;
let decoder = Decoder::new(&code, DecodeMode::Bits64, 0);
let mut ctx = Context::default();
for ins in decoder {
match ins {
Ok(ins) => {
ctx.emulate(&ins)?;
println!("{}\n{}", ins, ctx);
}
Err(e) => anyhow::bail!("Failed to decode: {}", e),
}
}
Ok(())
}