use std::fmt::{Display, Write};
use crate::errors::{DebuggerError, Result};
use crate::Addr;
const CODE_BITNESS: u32 = 64;
use iced_x86::{
Decoder, DecoderOptions, Formatter, FormatterOutput, FormatterTextKind, Instruction,
NasmFormatter,
};
use serde::{Serialize, Serializer};
use tracing::warn;
pub type TextContent = (String, FormatterTextKind);
#[derive(Serialize)]
struct SerializableTextContent {
text: String,
kind: String,
}
struct DisassemblyOutput(Vec<TextContent>);
#[derive(Debug, Clone, Hash, Serialize)]
pub struct Disassembly {
#[serde(serialize_with = "serialize_disassembly_vec")]
vec: Vec<(Addr, Vec<u8>, Vec<TextContent>, bool)>,
}
impl DisassemblyOutput {
fn new() -> Self {
DisassemblyOutput(Vec::new())
}
fn inner(&mut self) -> &[TextContent] {
&self.0
}
fn clear(&mut self) {
self.0.clear();
}
}
impl Disassembly {
#[must_use]
pub fn empty() -> Self {
Self { vec: Vec::new() }
}
pub fn disassemble(data: &[u8], first_addr: Addr, bp_indexes: &[usize]) -> Result<Self> {
let mut decoder =
Decoder::with_ip(CODE_BITNESS, data, first_addr.into(), DecoderOptions::NONE);
let mut formatter = NasmFormatter::new();
formatter.options_mut().set_first_operand_char_index(16);
formatter.options_mut().set_hex_suffix("");
formatter.options_mut().set_hex_prefix("");
formatter.options_mut().set_uppercase_hex(false);
formatter.options_mut().set_decimal_suffix("");
formatter.options_mut().set_decimal_prefix("0d");
formatter.options_mut().set_octal_suffix("");
formatter.options_mut().set_octal_prefix("0o");
formatter.options_mut().set_binary_suffix("");
formatter.options_mut().set_binary_prefix("0b");
formatter.options_mut().set_show_symbol_address(true);
formatter.options_mut().set_rip_relative_addresses(false);
formatter
.options_mut()
.set_memory_size_options(iced_x86::MemorySizeOptions::Always);
let mut disassembly = Self::empty();
let mut instruction = Instruction::default();
let mut text_contents: DisassemblyOutput = DisassemblyOutput::new();
while decoder.can_decode() {
decoder.decode_out(&mut instruction);
text_contents.clear();
formatter.format(&instruction, &mut text_contents);
let start_index = (instruction.ip() - Into::<u64>::into(first_addr)) as usize;
let instr_bytes = &data[start_index..start_index + instruction.len()];
if let Err(e) = disassembly.write_to_line(
instruction.ip().into(),
instr_bytes,
text_contents.inner(),
bp_indexes.contains(&(instruction.ip() as usize - first_addr.usize())),
) {
warn!("Error while disassembling, skipping: {e}");
}
}
Ok(disassembly)
}
#[must_use]
pub fn inner(&self) -> &[(Addr, Vec<u8>, Vec<TextContent>, bool)] {
&self.vec
}
#[must_use]
pub fn inner_mut(&mut self) -> &mut Vec<(Addr, Vec<u8>, Vec<TextContent>, bool)> {
&mut self.vec
}
#[must_use]
pub fn has_entry_for(&self, addr: Addr) -> bool {
self.vec.iter().any(|(a, _raw, _val, _bp)| *a == addr)
}
pub fn write_to_line(
&mut self,
addr: Addr,
raw: &[u8],
content: &[TextContent],
has_bp: bool,
) -> Result<()> {
if self.has_entry_for(addr) {
return Err(DebuggerError::AlreadyDisassembled(addr));
}
self.vec
.push((addr, raw.to_vec(), content.to_vec(), has_bp));
Ok(())
}
}
impl FormatterOutput for DisassemblyOutput {
fn write(&mut self, text: &str, kind: FormatterTextKind) {
self.0.push((text.to_string(), kind));
}
}
impl Display for Disassembly {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut buf2 = String::new();
for (addr, raw, content, has_bp) in self.inner() {
write!(f, "{addr}")?;
for byte in raw {
write!(buf2, "{byte:02x} ")?;
}
if *has_bp {
write!(f, "{:<4}", "(*)")?;
} else {
write!(f, "{:<4}", "")?;
}
write!(f, "{buf2:<20}\t")?;
buf2.clear();
for (thing, _kind) in content {
write!(f, "{thing}")?;
}
writeln!(f)?;
}
Ok(())
}
}
impl From<&TextContent> for SerializableTextContent {
fn from(content: &TextContent) -> Self {
Self {
text: content.0.clone(),
kind: format!("{:?}", content.1),
}
}
}
fn serialize_disassembly_vec<S>(
data: &[(Addr, Vec<u8>, Vec<TextContent>, bool)],
serializer: S,
) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
let serializable_data: Vec<(Addr, Vec<u8>, Vec<SerializableTextContent>, bool)> = data
.iter()
.map(|(addr, raw, content, has_bp)| {
(
*addr,
raw.clone(),
content.iter().map(SerializableTextContent::from).collect(),
*has_bp,
)
})
.collect();
serializable_data.serialize(serializer)
}
#[cfg(test)]
mod test {
use crate::addr::Addr;
use crate::disassemble::Disassembly;
const SOME_MACHINE_CODE: &[u8] = &[
0x48, 0x83, 0xec, 0x08, 0x48, 0x8b, 0x05, 0xbd, 0x1f, 0x02, 0x00, 0x48, 0x85, 0xc0, 0x74,
0x02, 0xff, 0xd0, 0x48, 0x83, 0xc4, 0x08, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
];
const BP_INDEXES: &[usize] = &[22, 16, 18];
const SOME_ADDR: usize = 0x0000_55dd_73ea_200b_usize;
#[test]
fn test_disassemble_data() {
let addr = Addr::from(SOME_ADDR);
let _: Disassembly = Disassembly::disassemble(SOME_MACHINE_CODE, addr, BP_INDEXES).unwrap();
}
#[test]
fn test_disassemble_data_serialize() {
let addr = Addr::from(SOME_ADDR);
let dis: Disassembly =
Disassembly::disassemble(SOME_MACHINE_CODE, addr, BP_INDEXES).unwrap();
let json = serde_json::to_string(&dis).unwrap();
println!("{json}");
}
}