retour\arch\x86\trampoline/
mod.rs1use self::disasm::*;
2use crate::arch::x86::thunk;
3use crate::error::{Error, Result};
4use crate::pic;
5use std::mem;
6
7mod disasm;
8
9pub struct Trampoline {
11 emitter: pic::CodeEmitter,
12 prolog_size: usize,
13}
14
15impl Trampoline {
16 pub unsafe fn new(target: *const (), margin: usize) -> Result<Trampoline> {
18 Builder::new(target, margin).build()
19 }
20
21 pub fn emitter(&self) -> &pic::CodeEmitter {
23 &self.emitter
24 }
25
26 pub fn prolog_size(&self) -> usize {
28 self.prolog_size
29 }
30}
31
32struct Builder {
34 disassembler: Disassembler,
36 branch_address: Option<usize>,
38 total_bytes_disassembled: usize,
40 margin: usize,
42 finished: bool,
44 target: *const (),
46}
47
48impl Builder {
49 pub fn new(target: *const (), margin: usize) -> Self {
51 Builder {
52 disassembler: Disassembler::new(target),
53 branch_address: None,
54 total_bytes_disassembled: 0,
55 finished: false,
56 target,
57 margin,
58 }
59 }
60
61 pub unsafe fn build(mut self) -> Result<Trampoline> {
65 let mut emitter = pic::CodeEmitter::new();
66
67 while !self.finished {
68 let instruction = self.next_instruction()?;
69 let thunk = self.process_instruction(&instruction)?;
70
71 if self.is_instruction_in_branch(&instruction) && instruction.len() != thunk.len() {
75 Err(Error::UnsupportedInstruction)?;
76 } else {
77 emitter.add_thunk(thunk);
78 }
79
80 if self.total_bytes_disassembled >= self.margin && !self.finished {
82 emitter.add_thunk(thunk::jmp(instruction.next_instruction_address()));
84 self.finished = true;
85 }
86 }
87
88 Ok(Trampoline {
89 prolog_size: self.total_bytes_disassembled,
90 emitter,
91 })
92 }
93
94 unsafe fn next_instruction(&mut self) -> Result<Instruction> {
96 let instruction_address = self.target as usize + self.total_bytes_disassembled;
97
98 match Instruction::new(&mut self.disassembler, instruction_address as *const _) {
100 None => Err(Error::InvalidCode)?,
101 Some(instruction) => {
102 self.total_bytes_disassembled += instruction.len();
104 Ok(instruction)
105 },
106 }
107 }
108
109 unsafe fn process_instruction(
111 &mut self,
112 instruction: &Instruction,
113 ) -> Result<Box<dyn pic::Thunkable>> {
114 if let Some(displacement) = instruction.rip_operand_displacement() {
115 return self.handle_rip_relative_instruction(instruction, displacement);
116 } else if let Some(displacement) = instruction.relative_branch_displacement() {
117 return self.handle_relative_branch(instruction, displacement);
118 } else if instruction.is_return() {
119 self.finished = !self.is_instruction_in_branch(instruction);
122 }
123
124 Ok(Box::new(instruction.as_slice().to_vec()))
127 }
128
129 unsafe fn handle_rip_relative_instruction(
138 &mut self,
139 instruction: &Instruction,
140 displacement: isize,
141 ) -> Result<Box<dyn pic::Thunkable>> {
142 self.finished = instruction.is_unconditional_jump();
144
145 if (-(self.total_bytes_disassembled as isize)..0).contains(&displacement) {
147 return Ok(Box::new(instruction.as_slice().to_vec()));
148 }
149
150 let instruction_address = instruction.address() as isize;
152 let instruction_bytes = instruction.as_slice().to_vec();
153
154 Ok(Box::new(pic::UnsafeThunk::new(
155 move |offset| {
156 let mut bytes = instruction_bytes.clone();
157
158 let adjusted_displacement = instruction_address
162 .wrapping_sub(offset as isize)
163 .wrapping_add(displacement);
164 assert!(crate::arch::is_within_range(adjusted_displacement));
165
166 let index = instruction_bytes.len() - mem::size_of::<u32>();
168
169 let as_bytes: [u8; 4] = (adjusted_displacement as u32).to_ne_bytes();
171 bytes[index..instruction_bytes.len()].copy_from_slice(&as_bytes);
172 bytes
173 },
174 instruction.len(),
175 )))
176 }
177
178 unsafe fn handle_relative_branch(
180 &mut self,
181 instruction: &Instruction,
182 displacement: isize,
183 ) -> Result<Box<dyn pic::Thunkable>> {
184 let destination_address_abs = instruction
186 .next_instruction_address()
187 .wrapping_add(displacement as usize);
188
189 if instruction.is_call() {
190 return Ok(thunk::call(destination_address_abs));
192 }
193
194 let prolog_range = (self.target as usize)..(self.target as usize + self.margin);
195
196 if prolog_range.contains(&destination_address_abs) {
200 self.branch_address = Some(destination_address_abs);
202 Ok(Box::new(instruction.as_slice().to_vec()))
203 } else if instruction.is_loop() {
204 Err(Error::UnsupportedInstruction)
206 } else if instruction.is_unconditional_jump() {
207 self.finished = !self.is_instruction_in_branch(instruction);
210 Ok(thunk::jmp(destination_address_abs))
211 } else {
212 let primary_opcode = instruction
216 .as_slice()
217 .iter()
218 .find(|op| **op != 0x0F)
219 .expect("retrieving conditional jump primary op code");
220
221 let condition = primary_opcode & 0x0F;
223 Ok(thunk::jcc(destination_address_abs, condition))
224 }
225 }
226
227 fn is_instruction_in_branch(&self, instruction: &Instruction) -> bool {
229 self
230 .branch_address
231 .map_or(false, |offset| instruction.address() < offset)
232 }
233}