dobby_rs_framework/framework/
mod.rs1mod 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}