use iced_x86::{
Code, Decoder, DecoderOptions, Formatter, FormatterOutput, FormatterTextKind, Instruction,
MemorySizeOptions, NasmFormatter,
};
pub fn disasm_formatter() -> NasmFormatter {
let mut formatter = NasmFormatter::new();
let options = formatter.options_mut();
options.set_space_after_operand_separator(true);
options.set_hex_prefix("0x");
options.set_hex_suffix("");
options.set_first_operand_char_index(5);
options.set_memory_size_options(MemorySizeOptions::Always);
options.set_show_branch_size(false);
options.set_rip_relative_addresses(true);
formatter
}
#[derive(Clone, Copy)]
pub enum AsmKind {
Mnemonic,
Register,
Number,
Punctuation,
Keyword,
Text,
}
pub struct AsmToken {
pub text: String,
pub kind: AsmKind,
}
struct TokenSink<'a>(&'a mut Vec<AsmToken>);
impl FormatterOutput for TokenSink<'_> {
fn write(&mut self, text: &str, kind: FormatterTextKind) {
let kind = match kind {
FormatterTextKind::Mnemonic | FormatterTextKind::Prefix => AsmKind::Mnemonic,
FormatterTextKind::Register => AsmKind::Register,
FormatterTextKind::Number
| FormatterTextKind::LabelAddress
| FormatterTextKind::FunctionAddress
| FormatterTextKind::SelectorValue => AsmKind::Number,
FormatterTextKind::Punctuation | FormatterTextKind::Operator => AsmKind::Punctuation,
FormatterTextKind::Keyword
| FormatterTextKind::Directive
| FormatterTextKind::Decorator => AsmKind::Keyword,
_ => AsmKind::Text,
};
self.0.push(AsmToken {
text: text.to_string(),
kind,
});
}
}
pub struct DisasmRow {
pub ip: u64,
pub hex: String,
pub tokens: Vec<AsmToken>,
pub comment: Option<String>,
}
impl DisasmRow {
pub fn asm(&self) -> String {
self.tokens.iter().map(|t| t.text.as_str()).collect()
}
}
pub fn decode_rows(
bytes: &[u8],
start_addr: u64,
limit: Option<usize>,
formatter: &mut NasmFormatter,
resolve: impl Fn(u64) -> String,
) -> Vec<DisasmRow> {
let mut decoder = Decoder::with_ip(64, bytes, start_addr, DecoderOptions::NONE);
let mut instruction = Instruction::default();
let mut rows = Vec::new();
while decoder.can_decode() && limit.is_none_or(|n| rows.len() < n) {
decoder.decode_out(&mut instruction);
if instruction.code() == Code::INVALID {
continue;
}
let mut tokens = Vec::new();
formatter.format(&instruction, &mut TokenSink(&mut tokens));
let ip = instruction.ip();
let start_index = (ip - start_addr) as usize;
let instr_bytes = &bytes[start_index..start_index + instruction.len()];
let hex = instr_bytes
.iter()
.map(|b| format!("{:02x}", b))
.collect::<Vec<_>>()
.join(" ");
let comment = if instruction.is_ip_rel_memory_operand() {
Some(resolve(instruction.ip_rel_memory_address()))
} else if instruction.is_call_near()
|| instruction.is_jmp_near()
|| instruction.is_jcc_near()
{
Some(resolve(instruction.near_branch_target()))
} else {
None
};
rows.push(DisasmRow {
ip,
hex,
tokens,
comment,
});
}
rows
}