use goblin::Object;
#[derive(Debug, Clone)]
pub struct HandlerEncoding {
pub addr: u64,
pub operand_offsets: Vec<u32>,
pub instr_len: u32,
}
fn va_to_file_offset(obj: &Object<'_>, va: u64) -> Option<usize> {
if let Object::PE(pe) = obj {
for sec in &pe.sections {
let svaddr = pe.image_base as u64 + sec.virtual_address as u64;
let sv = sec.virtual_size as u64;
if va >= svaddr && va < svaddr + sv {
let raddr = sec.pointer_to_raw_data as usize;
let rsize = sec.size_of_raw_data as usize;
let off_in_section = (va - svaddr) as usize;
if off_in_section < rsize {
return Some(raddr + off_in_section);
}
}
}
}
None
}
pub fn classify_handler(obj: &Object<'_>, data: &[u8], addr: u64) -> Option<HandlerEncoding> {
let off = va_to_file_offset(obj, addr)?;
let scan_len = 0x100.min(data.len() - off);
let body = &data[off..off + scan_len];
let mut operand_offsets: Vec<u32> = Vec::new();
let mut pc_advance: Option<u32> = None;
let mut k = 0;
while k + 4 <= body.len() {
let rex = if (body[k] == 0x48
|| body[k] == 0x44
|| body[k] == 0x4c
|| body[k] == 0x4d
|| body[k] == 0x41
|| body[k] == 0x45)
&& k + 1 < body.len()
&& body[k + 1] == 0x0f
{
1
} else {
0
};
if k + rex + 3 < body.len() && body[k + rex] == 0x0f && body[k + rex + 1] == 0xb6 {
let modrm = body[k + rex + 2];
let mod_field = modrm >> 6;
let rm = modrm & 0x07;
let mut disp_off = 0i32;
let mut consumed = 3 + rex;
if rm == 0x04 {
if k + consumed >= body.len() {
break;
}
consumed += 1; }
if mod_field == 0x01 {
if k + consumed >= body.len() {
break;
}
disp_off = body[k + consumed] as i8 as i32;
consumed += 1;
operand_offsets.push(disp_off as u32);
} else if mod_field == 0x02 {
if k + consumed + 4 > body.len() {
break;
}
disp_off = i32::from_le_bytes([
body[k + consumed],
body[k + consumed + 1],
body[k + consumed + 2],
body[k + consumed + 3],
]);
consumed += 4;
if disp_off >= 0 && disp_off < 64 {
operand_offsets.push(disp_off as u32);
}
} else if mod_field == 0x00 && rm == 0x04 {
operand_offsets.push(0);
}
k += consumed;
continue;
}
if body[k] == 0x48 && body[k + 1] == 0x8d {
let modrm = body[k + 2];
let mod_field = modrm >> 6;
let rm = modrm & 0x07;
if mod_field == 0x01 && rm != 0x04 {
if k + 4 <= body.len() {
let disp = body[k + 3] as i8 as i32;
if disp > 0 && disp < 16 {
let v = disp as u32;
pc_advance = Some(match pc_advance {
Some(prev) => prev.max(v),
None => v,
});
}
k += 4;
continue;
}
}
}
if body[k] == 0xc3 {
break;
}
k += 1;
}
let max_op = operand_offsets.iter().copied().max();
operand_offsets.sort();
operand_offsets.dedup();
let instr_len = if let Some(adv) = pc_advance {
1 + adv
} else if let Some(maxo) = max_op {
1 + maxo + 1
} else {
1
};
Some(HandlerEncoding {
addr,
operand_offsets,
instr_len,
})
}
pub fn classify_all(obj: &Object<'_>, data: &[u8], addrs: &[u64]) -> Vec<HandlerEncoding> {
addrs
.iter()
.filter_map(|a| classify_handler(obj, data, *a))
.collect()
}
pub fn render(encs: &[HandlerEncoding]) -> Vec<String> {
encs.iter()
.map(|e| {
format!(
"{:#x} instr_len={} operand_offsets={:?}",
e.addr, e.instr_len, e.operand_offsets
)
})
.collect()
}