retour\arch\x86\trampoline/
disasm.rs

1//! The underlying disassembler should be opaque to the outside.
2use std::slice;
3
4/// A x86/x64 disassembler.
5pub struct Disassembler(udis::ud);
6
7impl Disassembler {
8  /// Creates a default x86 disassembler.
9  pub fn new(target: *const ()) -> Disassembler {
10    unsafe {
11      let mut ud = ::std::mem::zeroed();
12      udis::ud_init(&mut ud);
13      udis::ud_set_user_opaque_data(&mut ud, target as *mut _);
14      udis::ud_set_input_hook(&mut ud, Some(Self::udis_read_address));
15      udis::ud_set_mode(&mut ud, (::std::mem::size_of::<usize>() * 8) as u8);
16      Disassembler(ud)
17    }
18  }
19
20  /// Reads one byte from a pointer and advances it.
21  unsafe extern "C" fn udis_read_address(ud: *mut udis::ud) -> libc::c_int {
22    let pointer = udis::ud_get_user_opaque_data(ud) as *mut u8;
23    let result = *pointer;
24    udis::ud_set_user_opaque_data(ud, pointer.offset(1) as *mut _);
25    libc::c_int::from(result)
26  }
27}
28
29/// Safe wrapper around an instruction.
30pub struct Instruction {
31  address: usize,
32  mnemonic: udis::ud_mnemonic_code,
33  operands: Vec<udis::ud_operand>,
34  bytes: &'static [u8],
35}
36
37impl Instruction {
38  /// Disassembles a new instruction at the specified address.
39  pub unsafe fn new(disasm: &mut Disassembler, address: *const ()) -> Option<Self> {
40    let instruction_bytes = udis::ud_disassemble(&mut disasm.0) as usize;
41    if instruction_bytes > 0 {
42      Some(Instruction {
43        address: address as usize,
44        mnemonic: udis::ud_insn_mnemonic(&disasm.0),
45        operands: disasm.0.operand.to_vec(),
46        bytes: slice::from_raw_parts(address as *const _, instruction_bytes),
47      })
48    } else {
49      None
50    }
51  }
52
53  /// Returns the instruction's address.
54  pub fn address(&self) -> usize {
55    self.address
56  }
57
58  /// Returns the next instruction's address.
59  pub fn next_instruction_address(&self) -> usize {
60    self.address() + self.len()
61  }
62
63  /// Returns the instructions relative branch offset, if applicable.
64  pub fn relative_branch_displacement(&self) -> Option<isize> {
65    unsafe {
66      self
67        .operands
68        .iter()
69        .find(|op| op.otype == udis::ud_type::UD_OP_JIMM)
70        .map(|op| match op.size {
71          8 => op.lval.sbyte as isize,
72          32 => op.lval.sdword as isize,
73          _ => unreachable!("Operand size: {}", op.size),
74        })
75    }
76  }
77
78  /// Returns the instructions RIP operand displacement if applicable.
79  pub fn rip_operand_displacement(&self) -> Option<isize> {
80    unsafe {
81      // The operands displacement (e.g `mov eax, [rip+0x10]` ⟶ 0x10)
82      self
83        .operands
84        .iter()
85        .find(|op| op.otype == udis::ud_type::UD_OP_MEM && op.base == udis::ud_type::UD_R_RIP)
86        .map(|op| op.lval.sdword as isize)
87    }
88  }
89
90  /// Returns true if this instruction any type of a loop.
91  pub fn is_loop(&self) -> bool {
92    matches!(
93      self.mnemonic,
94      udis::ud_mnemonic_code::UD_Iloop
95        | udis::ud_mnemonic_code::UD_Iloope
96        | udis::ud_mnemonic_code::UD_Iloopne
97        | udis::ud_mnemonic_code::UD_Ijecxz
98        | udis::ud_mnemonic_code::UD_Ijcxz
99    )
100  }
101
102  /// Returns true if this instruction is an unconditional jump.
103  pub fn is_unconditional_jump(&self) -> bool {
104    self.mnemonic == udis::ud_mnemonic_code::UD_Ijmp
105  }
106
107  /// Returns true if this instruction is a function call.
108  pub fn is_call(&self) -> bool {
109    self.mnemonic == udis::ud_mnemonic_code::UD_Icall
110  }
111
112  /// Returns true if this instruction is a return.
113  pub fn is_return(&self) -> bool {
114    self.mnemonic == udis::ud_mnemonic_code::UD_Iret
115  }
116
117  /// Returns the instruction's bytes.
118  pub unsafe fn as_slice(&self) -> &[u8] {
119    self.bytes
120  }
121
122  /// Returns the size of the instruction in bytes.
123  pub fn len(&self) -> usize {
124    self.bytes.len()
125  }
126}