1pub use self::patcher::Patcher;
2pub use self::trampoline::Trampoline;
3
4pub mod meta;
5mod patcher;
6mod thunk;
7mod trampoline;
8
9#[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 type CRet = unsafe extern "C" fn() -> i32;
22
23 #[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 unsafe extern "C" fn ret10() -> i32 {
159 10
160 }
161}