wraith/manipulation/inline_hook/
guard.rs1use crate::error::{Result, WraithError};
7use crate::util::memory::ProtectionGuard;
8use super::arch::Architecture;
9use super::trampoline::ExecutableMemory;
10use core::marker::PhantomData;
11
12const PAGE_EXECUTE_READWRITE: u32 = 0x40;
13
14pub struct HookGuard<A: Architecture> {
19 target: usize,
21 detour: usize,
23 original_bytes: Vec<u8>,
25 trampoline: Option<ExecutableMemory>,
27 auto_restore: bool,
29 _arch: PhantomData<A>,
31}
32
33impl<A: Architecture> HookGuard<A> {
34 pub(crate) fn new(
36 target: usize,
37 detour: usize,
38 original_bytes: Vec<u8>,
39 trampoline: Option<ExecutableMemory>,
40 ) -> Self {
41 Self {
42 target,
43 detour,
44 original_bytes,
45 trampoline,
46 auto_restore: true,
47 _arch: PhantomData,
48 }
49 }
50
51 pub fn target(&self) -> usize {
53 self.target
54 }
55
56 pub fn detour(&self) -> usize {
58 self.detour
59 }
60
61 pub fn trampoline(&self) -> Option<usize> {
65 self.trampoline.as_ref().map(|t| t.base())
66 }
67
68 pub fn original_bytes(&self) -> &[u8] {
70 &self.original_bytes
71 }
72
73 pub fn will_auto_restore(&self) -> bool {
75 self.auto_restore
76 }
77
78 pub fn set_auto_restore(&mut self, restore: bool) {
80 self.auto_restore = restore;
81 }
82
83 pub fn leak(mut self) {
88 self.auto_restore = false;
89 if let Some(trampoline) = self.trampoline.take() {
90 trampoline.leak();
91 }
92 core::mem::forget(self);
93 }
94
95 pub fn restore(self) -> Result<()> {
99 self.restore_internal()?;
100 core::mem::forget(self);
102 Ok(())
103 }
104
105 pub fn disable(&mut self) -> Result<()> {
110 let _guard = ProtectionGuard::new(
111 self.target,
112 self.original_bytes.len(),
113 PAGE_EXECUTE_READWRITE,
114 )?;
115
116 unsafe {
118 core::ptr::copy_nonoverlapping(
119 self.original_bytes.as_ptr(),
120 self.target as *mut u8,
121 self.original_bytes.len(),
122 );
123 }
124
125 flush_icache(self.target, self.original_bytes.len())?;
126
127 Ok(())
128 }
129
130 pub fn enable(&mut self, hook_bytes: &[u8]) -> Result<()> {
134 if hook_bytes.len() != self.original_bytes.len() {
135 return Err(WraithError::WriteFailed {
136 address: self.target as u64,
137 size: hook_bytes.len(),
138 });
139 }
140
141 let _guard = ProtectionGuard::new(
142 self.target,
143 hook_bytes.len(),
144 PAGE_EXECUTE_READWRITE,
145 )?;
146
147 unsafe {
149 core::ptr::copy_nonoverlapping(
150 hook_bytes.as_ptr(),
151 self.target as *mut u8,
152 hook_bytes.len(),
153 );
154 }
155
156 flush_icache(self.target, hook_bytes.len())?;
157
158 Ok(())
159 }
160
161 fn restore_internal(&self) -> Result<()> {
163 let _guard = ProtectionGuard::new(
164 self.target,
165 self.original_bytes.len(),
166 PAGE_EXECUTE_READWRITE,
167 )?;
168
169 unsafe {
171 core::ptr::copy_nonoverlapping(
172 self.original_bytes.as_ptr(),
173 self.target as *mut u8,
174 self.original_bytes.len(),
175 );
176 }
177
178 flush_icache(self.target, self.original_bytes.len())?;
179
180 Ok(())
181 }
182}
183
184impl<A: Architecture> Drop for HookGuard<A> {
185 fn drop(&mut self) {
186 if self.auto_restore {
187 let _ = self.restore_internal();
189 }
190 }
191}
192
193unsafe impl<A: Architecture> Send for HookGuard<A> {}
196unsafe impl<A: Architecture> Sync for HookGuard<A> {}
197
198#[derive(Debug, Clone, Copy, PartialEq, Eq)]
200pub enum HookState {
201 Enabled,
203 Disabled,
205}
206
207pub struct StatefulHookGuard<A: Architecture> {
209 guard: HookGuard<A>,
210 hook_bytes: Vec<u8>,
211 state: HookState,
212}
213
214impl<A: Architecture> StatefulHookGuard<A> {
215 pub(crate) fn new(guard: HookGuard<A>, hook_bytes: Vec<u8>) -> Self {
217 Self {
218 guard,
219 hook_bytes,
220 state: HookState::Enabled,
221 }
222 }
223
224 pub fn state(&self) -> HookState {
226 self.state
227 }
228
229 pub fn is_enabled(&self) -> bool {
231 self.state == HookState::Enabled
232 }
233
234 pub fn disable(&mut self) -> Result<()> {
236 if self.state == HookState::Enabled {
237 self.guard.disable()?;
238 self.state = HookState::Disabled;
239 }
240 Ok(())
241 }
242
243 pub fn enable(&mut self) -> Result<()> {
245 if self.state == HookState::Disabled {
246 self.guard.enable(&self.hook_bytes)?;
247 self.state = HookState::Enabled;
248 }
249 Ok(())
250 }
251
252 pub fn toggle(&mut self) -> Result<()> {
254 match self.state {
255 HookState::Enabled => self.disable(),
256 HookState::Disabled => self.enable(),
257 }
258 }
259
260 pub fn target(&self) -> usize {
262 self.guard.target()
263 }
264
265 pub fn detour(&self) -> usize {
267 self.guard.detour()
268 }
269
270 pub fn trampoline(&self) -> Option<usize> {
272 self.guard.trampoline()
273 }
274
275 pub fn leak(self) {
277 self.guard.leak();
278 }
279
280 pub fn restore(self) -> Result<()> {
282 self.guard.restore()
283 }
284}
285
286fn flush_icache(address: usize, size: usize) -> Result<()> {
288 let result = unsafe {
289 FlushInstructionCache(
290 GetCurrentProcess(),
291 address as *const _,
292 size,
293 )
294 };
295
296 if result == 0 {
297 Err(WraithError::from_last_error("FlushInstructionCache"))
298 } else {
299 Ok(())
300 }
301}
302
303#[link(name = "kernel32")]
304extern "system" {
305 fn FlushInstructionCache(
306 hProcess: *mut core::ffi::c_void,
307 lpBaseAddress: *const core::ffi::c_void,
308 dwSize: usize,
309 ) -> i32;
310
311 fn GetCurrentProcess() -> *mut core::ffi::c_void;
312}