Skip to main content

dobby_rs_framework/framework/
mod.rs

1mod module;
2pub mod params;
3
4use crate::hook_utils::HookHandle;
5use crate::symbols;
6use crate::{Error, Result, hooks};
7use core::ffi::c_void;
8use std::ffi::CString;
9
10pub use module::ModuleHandle;
11
12pub struct HookDef {
13    pub symbol: String,
14    pub alias: Option<String>,
15    pub detour: *mut c_void,
16}
17
18unsafe fn fn_to_ptr<F: Copy>(f: F) -> *mut c_void {
19    debug_assert_eq!(
20        core::mem::size_of::<F>(),
21        core::mem::size_of::<*mut c_void>()
22    );
23    let raw: *const () = core::mem::transmute_copy(&f);
24    raw as *mut c_void
25}
26
27pub fn make_hook<F: Copy>(symbol: &str, alias: &str, detour: F) -> HookDef {
28    let detour = unsafe { fn_to_ptr(detour) };
29    HookDef {
30        symbol: symbol.to_owned(),
31        alias: Some(alias.to_owned()),
32        detour,
33    }
34}
35
36pub fn make_hook_simple<F: Copy>(symbol: &str, detour: F) -> HookDef {
37    let detour = unsafe { fn_to_ptr(detour) };
38    HookDef {
39        symbol: symbol.to_owned(),
40        alias: None,
41        detour,
42    }
43}
44
45pub type ExtraAction<'a> = Box<dyn Fn(&ModuleHandle) -> Result<()> + Send + Sync + 'a>;
46
47pub struct InlineHooksConfig<'a> {
48    pub lib_name: &'a str,
49    pub hooks: Vec<HookDef>,
50    pub extra_action: Option<ExtraAction<'a>>,
51}
52
53pub struct InstalledHook {
54    pub symbol: String,
55    pub alias: Option<String>,
56    pub address: *mut c_void,
57    handle: Option<HookHandle>,
58}
59
60pub struct HookSession {
61    pub module: ModuleHandle,
62    pub installed: Vec<InstalledHook>,
63}
64
65impl HookSession {
66    pub unsafe fn unhook_all(&mut self) -> Result<()> {
67        for item in &mut self.installed {
68            if let Some(handle) = item.handle.take() {
69                handle.unhook()?;
70            }
71        }
72        Ok(())
73    }
74}
75
76pub struct InlineHooksBuilder<'a> {
77    lib_name: &'a str,
78    hooks: Vec<HookDef>,
79    extra_action: Option<ExtraAction<'a>>,
80}
81
82impl<'a> InlineHooksBuilder<'a> {
83    pub fn new(lib_name: &'a str) -> Self {
84        Self {
85            lib_name,
86            hooks: Vec::new(),
87            extra_action: None,
88        }
89    }
90    pub fn hook_install<F: Copy>(mut self, symbol: &str, detour: F) -> Self {
91        self.hooks.push(make_hook_simple(symbol, detour));
92        self
93    }
94    pub fn hook_install_alias<F: Copy>(mut self, symbol: &str, alias: &str, detour: F) -> Self {
95        self.hooks.push(make_hook(symbol, alias, detour));
96        self
97    }
98    pub fn hook<F: Copy>(self, symbol: &str, detour: F) -> Self {
99        self.hook_install(symbol, detour)
100    }
101    pub fn hook_alias<F: Copy>(self, symbol: &str, alias: &str, detour: F) -> Self {
102        self.hook_install_alias(symbol, alias, detour)
103    }
104    pub fn extra_action<F>(mut self, action: F) -> Self
105    where
106        F: Fn(&ModuleHandle) -> Result<()> + Send + Sync + 'a,
107    {
108        self.extra_action = Some(Box::new(action));
109        self
110    }
111    pub fn extra_action_fn(mut self, action: fn(&ModuleHandle) -> Result<()>) -> Self {
112        self.extra_action = Some(Box::new(action));
113        self
114    }
115    pub unsafe fn install(self) -> Result<HookSession> {
116        install_inline_hooks(InlineHooksConfig {
117            lib_name: self.lib_name,
118            hooks: self.hooks,
119            extra_action: self.extra_action,
120        })
121    }
122}
123
124pub fn inline_hooks(lib_name: &str) -> InlineHooksBuilder<'_> {
125    InlineHooksBuilder::new(lib_name)
126}
127
128pub unsafe fn install_inline_hooks(config: InlineHooksConfig<'_>) -> Result<HookSession> {
129    let module = ModuleHandle::open(config.lib_name)?;
130    let mut installed = Vec::with_capacity(config.hooks.len());
131    for hook in config.hooks {
132        if hook.detour.is_null() {
133            return Err(Error::NullPointer);
134        }
135        let symbol_c = CString::new(hook.symbol.as_str()).map_err(|_| Error::InvalidInput)?;
136        let target = module.resolve(&symbol_c).ok_or(Error::SymbolNotFound)?;
137        if let Some(alias) = hook.alias.clone() {
138            symbols::register_alias_with_symbol(
139                alias,
140                Some(module.lib_name_cstr()),
141                &symbol_c,
142                target,
143            )?;
144        }
145        let handle = hooks::install_addr(target, hook.detour)?;
146        installed.push(InstalledHook {
147            symbol: hook.symbol,
148            alias: hook.alias,
149            address: target,
150            handle: Some(handle),
151        });
152    }
153    if let Some(extra) = config.extra_action {
154        extra(&module)?;
155    }
156    Ok(HookSession { module, installed })
157}