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