parent
fe6a937f51
commit
70db095765
@ -0,0 +1,7 @@
|
||||
# bddisasm-sys changelog
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Changed
|
||||
|
||||
- the crate is now a `no_std` crate (this adds a dependency on [cty](https://crates.io/crates/cty))
|
@ -1,9 +1,13 @@
|
||||
# bddisasm-sys
|
||||
|
||||
Rust bindings for [bddisasm](https://github.com/bitdefender/bddisasm).
|
||||
`no_std` Rust bindings for [bddisasm](https://github.com/bitdefender/bddisasm).
|
||||
|
||||
See [bddisasm](https://crates.io/crates/bddisasm) if you're looking for a Rust wrapper for these bindings.
|
||||
|
||||
## Requirements
|
||||
|
||||
[bindgen](https://crates.io/crates/bindgen) is used to generate the bindings at build time. Because of this, users need to have `clang` installed. Check the [bindgen documentation](https://rust-lang.github.io/rust-bindgen/requirements.html) for more information.
|
||||
|
||||
## Notes
|
||||
|
||||
While this crate is `no_std`, the `bddisasm` library it links against depends on a C library because it needs `vsnprintf` and `memset`. It is possible to [work around this limitation](https://github.com/bitdefender/bddisasm#nd_vsnprintf_s-and-nd_memset), but this is not currently done for these bindings.
|
||||
|
@ -0,0 +1,17 @@
|
||||
# bddisasm changelog
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
|
||||
- implement `Decoder::decode_next_with_offset`
|
||||
- implement ``Decoder::decode_next_with_ip`
|
||||
- `OperandLookup` struct which makes working with operands easier
|
||||
- re-export `bddisasm_sys` as `ffi`
|
||||
- re-export commonly used items
|
||||
- Implement `as_*` and `is_*` accessors for the `OpInfo` enum
|
||||
|
||||
### Changed
|
||||
|
||||
- the crate is now a `no_std` crate
|
||||
- public types no longer implement `From` and no longer `panic!` when an unexpected value is encountered
|
@ -0,0 +1,159 @@
|
||||
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(())
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
cd bddisasm-sys
|
||||
rm -rf LICENSE
|
||||
ln -s ../../../LICENSE .
|
||||
|
||||
cd csrc
|
||||
rm -rf bddisasm
|
||||
ln -s ../../../../bddisasm/ .
|
||||
rm -rf inc
|
||||
ln -s ../../../../inc/ .
|
Loading…
Reference in new issue