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
|
# 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.
|
See [bddisasm](https://crates.io/crates/bddisasm) if you're looking for a Rust wrapper for these bindings.
|
||||||
|
|
||||||
## Requirements
|
## 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.
|
[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