asdf_overlay_hook/
lib.rs

1//! Hooking library for Windows using Detours.
2//!
3//! This crate is intended to be used only as `asdf-overlay`'s internal dependency.
4//! It provides a safe abstraction over the Detours library for function hooking.
5
6#[allow(non_camel_case_types, non_snake_case, unused, clippy::all)]
7mod detours {
8    // Generated using `bindgen detours_wrapper.h --allowlist-function DetourTransaction.* --allowlist-function DetourAttach --override-abi ".*=stdcall" --use-core -o src/pregenerated.rs`
9    #[cfg(target_pointer_width="32")]
10    include!("./pregenerated-x86.rs");
11    #[cfg(target_pointer_width="64")]
12    include!("./pregenerated-x64.rs");
13}
14
15use tracing::debug;
16
17use core::{
18    error::Error,
19    ffi::c_long,
20    fmt::{self, Debug, Display, Formatter},
21};
22
23/// A detour function hook.
24#[derive(Debug)]
25pub struct DetourHook<F> {
26    func: F,
27}
28
29impl<F: Copy> DetourHook<F> {
30    /// Attach a hook to the target function.
31    ///
32    /// # Safety
33    /// func and detour should be valid function pointers with same signature.
34    #[tracing::instrument]
35    pub unsafe fn attach(mut func: F, mut detour: F) -> DetourResult<Self>
36    where
37        F: Debug,
38    {
39        unsafe {
40            wrap_detour_call(|| detours::DetourTransactionBegin())?;
41            wrap_detour_call(|| {
42                use core::ffi::c_void;
43
44                detours::DetourAttach(
45                    (&raw mut func).cast(),
46                    *(&raw mut detour).cast::<*mut c_void>(),
47                )
48            })?;
49            wrap_detour_call(|| detours::DetourTransactionCommit())?;
50        }
51        debug!("hook attached");
52
53        Ok(DetourHook { func })
54    }
55
56    /// Get the original function pointer.
57    #[inline(always)]
58    pub fn original_fn(&self) -> F {
59        self.func
60    }
61}
62
63type DetourResult<T> = Result<T, DetourError>;
64
65/// Detour error code.
66#[derive(Debug, Clone, Copy)]
67pub struct DetourError(c_long);
68
69impl Display for DetourError {
70    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
71        write!(f, "Detour call error: {:?}", self.0)
72    }
73}
74
75impl Error for DetourError {}
76
77/// Wrap a detour call and convert its errors to `DetourError`.
78#[inline]
79fn wrap_detour_call(f: impl FnOnce() -> c_long) -> Result<(), DetourError> {
80    let code = f();
81    if code == 0 {
82        Ok(())
83    } else {
84        Err(DetourError(code))
85    }
86}