use std::slice;
pub struct Disassembler(udis::ud);
impl Disassembler {
pub fn new(target: *const ()) -> Disassembler {
unsafe {
let mut ud = ::std::mem::zeroed();
udis::ud_init(&mut ud);
udis::ud_set_user_opaque_data(&mut ud, target as *mut _);
udis::ud_set_input_hook(&mut ud, Some(Self::udis_read_address));
udis::ud_set_mode(&mut ud, (::std::mem::size_of::<usize>() * 8) as u8);
Disassembler(ud)
}
}
unsafe extern "C" fn udis_read_address(ud: *mut udis::ud) -> libc::c_int {
let pointer = udis::ud_get_user_opaque_data(ud) as *mut u8;
let result = *pointer;
udis::ud_set_user_opaque_data(ud, pointer.offset(1) as *mut _);
libc::c_int::from(result)
}
}
pub struct Instruction {
address: usize,
mnemonic: udis::ud_mnemonic_code,
operands: Vec<udis::ud_operand>,
bytes: &'static [u8],
}
impl Instruction {
pub unsafe fn new(disasm: &mut Disassembler, address: *const ()) -> Option<Self> {
let instruction_bytes = udis::ud_disassemble(&mut disasm.0) as usize;
if instruction_bytes > 0 {
Some(Instruction {
address: address as usize,
mnemonic: udis::ud_insn_mnemonic(&disasm.0),
operands: disasm.0.operand.to_vec(),
bytes: slice::from_raw_parts(address as *const _, instruction_bytes),
})
} else {
None
}
}
pub fn address(&self) -> usize {
self.address
}
pub fn next_instruction_address(&self) -> usize {
self.address() + self.len()
}
pub fn relative_branch_displacement(&self) -> Option<isize> {
unsafe {
self
.operands
.iter()
.find(|op| op.otype == udis::ud_type::UD_OP_JIMM)
.map(|op| match op.size {
8 => op.lval.sbyte as isize,
32 => op.lval.sdword as isize,
_ => unreachable!("Operand size: {}", op.size),
})
}
}
pub fn rip_operand_displacement(&self) -> Option<isize> {
unsafe {
self
.operands
.iter()
.find(|op| op.otype == udis::ud_type::UD_OP_MEM && op.base == udis::ud_type::UD_R_RIP)
.map(|op| op.lval.sdword as isize)
}
}
pub fn is_loop(&self) -> bool {
matches!(
self.mnemonic,
udis::ud_mnemonic_code::UD_Iloop
| udis::ud_mnemonic_code::UD_Iloope
| udis::ud_mnemonic_code::UD_Iloopne
| udis::ud_mnemonic_code::UD_Ijecxz
| udis::ud_mnemonic_code::UD_Ijcxz
)
}
pub fn is_unconditional_jump(&self) -> bool {
self.mnemonic == udis::ud_mnemonic_code::UD_Ijmp
}
pub fn is_call(&self) -> bool {
self.mnemonic == udis::ud_mnemonic_code::UD_Icall
}
pub fn is_return(&self) -> bool {
self.mnemonic == udis::ud_mnemonic_code::UD_Iret
}
pub unsafe fn as_slice(&self) -> &[u8] {
self.bytes
}
pub fn len(&self) -> usize {
self.bytes.len()
}
}