retour\arch\x86/
mod.rs

1pub use self::patcher::Patcher;
2pub use self::trampoline::Trampoline;
3
4pub mod meta;
5mod patcher;
6mod thunk;
7mod trampoline;
8
9// TODO: Add test for targets further away than DETOUR_RANGE
10// TODO: Add test for unsupported branches
11// TODO: Add test for negative branch displacements
12#[cfg(all(feature = "nightly", test))]
13mod tests {
14  use crate::error::{Error, Result};
15  use crate::RawDetour;
16  use matches::assert_matches;
17  use std::arch::asm;
18  use std::mem;
19
20  /// Default test case function definition.
21  type CRet = unsafe extern "C" fn() -> i32;
22
23  /// Detours a C function returning an integer, and asserts its return value.
24  #[inline(never)]
25  unsafe fn detour_test(target: CRet, result: i32) -> Result<()> {
26    let hook = RawDetour::new(target as *const (), ret10 as *const ())?;
27
28    assert_eq!(target(), result);
29    hook.enable()?;
30    {
31      assert_eq!(target(), 10);
32      let original: CRet = mem::transmute(hook.trampoline());
33      assert_eq!(original(), result);
34    }
35    hook.disable()?;
36    assert_eq!(target(), result);
37    Ok(())
38  }
39
40  #[test]
41  fn detour_relative_branch() -> Result<()> {
42    #[naked]
43    unsafe extern "C" fn branch_ret5() -> i32 {
44      asm!(
45        "
46            xor eax, eax
47            je 5f
48            mov eax, 2
49            jmp 2f
50          5:
51            mov eax, 5
52          2:
53            ret",
54        options(noreturn)
55      );
56    }
57
58    unsafe { detour_test(mem::transmute(branch_ret5 as usize), 5) }
59  }
60
61  #[test]
62  fn detour_hotpatch() -> Result<()> {
63    #[naked]
64    unsafe extern "C" fn hotpatch_ret0() -> i32 {
65      asm!(
66        "
67            nop
68            nop
69            nop
70            nop
71            nop
72            xor eax, eax
73            ret
74            mov eax, 5",
75        options(noreturn)
76      );
77    }
78
79    unsafe { detour_test(mem::transmute(hotpatch_ret0 as usize + 5), 0) }
80  }
81
82  #[test]
83  fn detour_padding_after() -> Result<()> {
84    #[naked]
85    unsafe extern "C" fn padding_after_ret0() -> i32 {
86      asm!(
87        "
88            mov edi, edi
89            xor eax, eax
90            ret
91            nop
92            nop",
93        options(noreturn)
94      );
95    }
96
97    unsafe { detour_test(mem::transmute(padding_after_ret0 as usize + 2), 0) }
98  }
99
100  #[test]
101  fn detour_external_loop() {
102    #[naked]
103    unsafe extern "C" fn external_loop() -> i32 {
104      asm!(
105        "
106            loop 2f
107            nop
108            nop
109            nop
110            2:",
111        options(noreturn)
112      );
113    }
114
115    let error =
116      unsafe { RawDetour::new(external_loop as *const (), ret10 as *const ()) }.unwrap_err();
117    assert_matches!(error, Error::UnsupportedInstruction);
118  }
119
120  #[test]
121  #[cfg(target_arch = "x86_64")]
122  fn detour_rip_relative_pos() -> Result<()> {
123    #[naked]
124    unsafe extern "C" fn rip_relative_ret195() -> i32 {
125      asm!(
126        "
127            xor eax, eax
128            mov al, [rip+0x3]
129            nop
130            nop
131            nop
132            ret",
133        options(noreturn)
134      );
135    }
136
137    unsafe { detour_test(rip_relative_ret195, 195) }
138  }
139
140  #[test]
141  #[cfg(target_arch = "x86_64")]
142  fn detour_rip_relative_neg() -> Result<()> {
143    #[naked]
144    unsafe extern "C" fn rip_relative_prolog_ret49() -> i32 {
145      asm!(
146        "
147            xor eax, eax
148            mov al, [rip-0x8]
149            ret",
150        options(noreturn)
151      );
152    }
153
154    unsafe { detour_test(rip_relative_prolog_ret49, 49) }
155  }
156
157  /// Default detour target.
158  unsafe extern "C" fn ret10() -> i32 {
159    10
160  }
161}