wraith/manipulation/inline_hook/hook/
inline.rs1use crate::error::{Result, WraithError};
7use crate::util::memory::ProtectionGuard;
8use crate::manipulation::inline_hook::arch::Architecture;
9use crate::manipulation::inline_hook::guard::{HookGuard, StatefulHookGuard};
10use crate::manipulation::inline_hook::trampoline::{ExecutableMemory, TrampolineBuilder};
11use super::Hook;
12use core::marker::PhantomData;
13
14const PAGE_EXECUTE_READWRITE: u32 = 0x40;
15
16pub struct InlineHook<A: Architecture> {
21 target: usize,
22 detour: usize,
23 _arch: PhantomData<A>,
24}
25
26impl<A: Architecture> InlineHook<A> {
27 pub fn new(target: usize, detour: usize) -> Self {
33 Self {
34 target,
35 detour,
36 _arch: PhantomData,
37 }
38 }
39
40 fn hook_size(&self) -> usize {
42 A::preferred_hook_size(self.target, self.detour)
43 }
44
45 fn generate_hook_stub(&self) -> Vec<u8> {
47 if let Some(bytes) = A::encode_jmp_rel(self.target, self.detour) {
49 bytes
50 } else {
51 A::encode_jmp_abs(self.detour)
53 }
54 }
55
56 pub fn install(self) -> Result<HookGuard<A>> {
58 let hook_size = self.hook_size();
59
60 let mut builder = TrampolineBuilder::<A>::new(self.target);
62 builder.analyze(hook_size)?;
63 builder.allocate()?;
64 builder.build()?;
65
66 let prologue_bytes = builder.prologue_bytes().to_vec();
67 let prologue_size = builder.prologue_size();
68 let trampoline_memory = builder.take_memory();
69
70 let hook_stub = self.generate_hook_stub();
72
73 if prologue_size < hook_stub.len() {
75 return Err(WraithError::HookDetectionFailed {
76 function: format!("{:#x}", self.target),
77 reason: format!(
78 "insufficient space: need {} bytes, have {}",
79 hook_stub.len(),
80 prologue_size
81 ),
82 });
83 }
84
85 let mut final_stub = hook_stub;
87 if final_stub.len() < prologue_size {
88 let padding = A::encode_nop_sled(prologue_size - final_stub.len());
89 final_stub.extend_from_slice(&padding);
90 }
91
92 {
94 let _guard = ProtectionGuard::new(
95 self.target,
96 prologue_size,
97 PAGE_EXECUTE_READWRITE,
98 )?;
99
100 unsafe {
102 core::ptr::copy_nonoverlapping(
103 final_stub.as_ptr(),
104 self.target as *mut u8,
105 prologue_size,
106 );
107 }
108 }
109
110 flush_icache(self.target, prologue_size)?;
112
113 Ok(HookGuard::new(
114 self.target,
115 self.detour,
116 prologue_bytes,
117 trampoline_memory,
118 ))
119 }
120
121 pub fn install_stateful(self) -> Result<StatefulHookGuard<A>> {
123 let hook_size = self.hook_size();
124
125 let mut builder = TrampolineBuilder::<A>::new(self.target);
127 builder.analyze(hook_size)?;
128 builder.allocate()?;
129 builder.build()?;
130
131 let prologue_bytes = builder.prologue_bytes().to_vec();
132 let prologue_size = builder.prologue_size();
133 let trampoline_memory = builder.take_memory();
134
135 let hook_stub = self.generate_hook_stub();
137
138 if prologue_size < hook_stub.len() {
139 return Err(WraithError::HookDetectionFailed {
140 function: format!("{:#x}", self.target),
141 reason: format!(
142 "insufficient space: need {} bytes, have {}",
143 hook_stub.len(),
144 prologue_size
145 ),
146 });
147 }
148
149 let mut final_stub = hook_stub;
150 if final_stub.len() < prologue_size {
151 let padding = A::encode_nop_sled(prologue_size - final_stub.len());
152 final_stub.extend_from_slice(&padding);
153 }
154
155 {
157 let _guard = ProtectionGuard::new(
158 self.target,
159 prologue_size,
160 PAGE_EXECUTE_READWRITE,
161 )?;
162
163 unsafe {
164 core::ptr::copy_nonoverlapping(
165 final_stub.as_ptr(),
166 self.target as *mut u8,
167 prologue_size,
168 );
169 }
170 }
171
172 flush_icache(self.target, prologue_size)?;
173
174 let guard = HookGuard::new(
175 self.target,
176 self.detour,
177 prologue_bytes,
178 trampoline_memory,
179 );
180
181 Ok(StatefulHookGuard::new(guard, final_stub))
182 }
183}
184
185impl<A: Architecture> Hook for InlineHook<A> {
186 type Guard = HookGuard<A>;
187
188 fn install(self) -> Result<Self::Guard> {
189 InlineHook::install(self)
190 }
191
192 fn target(&self) -> usize {
193 self.target
194 }
195
196 fn detour(&self) -> usize {
197 self.detour
198 }
199}
200
201pub fn hook<A: Architecture>(target: usize, detour: usize) -> Result<HookGuard<A>> {
203 InlineHook::<A>::new(target, detour).install()
204}
205
206#[cfg(target_arch = "x86_64")]
208pub fn hook_native(target: usize, detour: usize) -> Result<HookGuard<crate::manipulation::inline_hook::arch::X64>> {
209 hook::<crate::manipulation::inline_hook::arch::X64>(target, detour)
210}
211
212#[cfg(target_arch = "x86")]
213pub fn hook_native(target: usize, detour: usize) -> Result<HookGuard<crate::manipulation::inline_hook::arch::X86>> {
214 hook::<crate::manipulation::inline_hook::arch::X86>(target, detour)
215}
216
217fn flush_icache(address: usize, size: usize) -> Result<()> {
219 let result = unsafe {
220 FlushInstructionCache(
221 GetCurrentProcess(),
222 address as *const _,
223 size,
224 )
225 };
226
227 if result == 0 {
228 Err(WraithError::from_last_error("FlushInstructionCache"))
229 } else {
230 Ok(())
231 }
232}
233
234#[link(name = "kernel32")]
235extern "system" {
236 fn FlushInstructionCache(
237 hProcess: *mut core::ffi::c_void,
238 lpBaseAddress: *const core::ffi::c_void,
239 dwSize: usize,
240 ) -> i32;
241
242 fn GetCurrentProcess() -> *mut core::ffi::c_void;
243}