hook.rs
1 use std::ffi::c_void; 2 3 /// A function hook that redirects function calls to a replacement function. 4 pub struct Hook { 5 hook_name: String, 6 raw: safer_ffi::boxed::Box<crate::sys::HookRaw>, 7 8 /// Whether the hook should be used when the plugin is enabled 9 enabled: bool, 10 } 11 12 impl Hook { 13 /// Create a new function hook. 14 /// 15 /// # Examples 16 /// 17 /// ``` 18 /// extern "C" fn replacement_fn() { 19 /// println!("Hello, world!"); 20 /// } 21 /// 22 /// unsafe fn enable() { 23 /// use std::ptr::addr_of_mut; 24 /// let mut target_fn = 0xDEADBEEF as *mut c_void; 25 /// let hook = Hook::new("my_hook", addr_of_mut!(target_fn), replacement_fn as _); 26 /// } 27 /// ``` 28 pub unsafe fn new( 29 hook_name: &str, 30 target_fn: *mut *mut c_void, 31 replacement_fn: *mut c_void, 32 ) -> Self { 33 let hook_name = hook_name.to_string(); 34 let raw = crate::sys::hook_new(hook_name.clone().into(), target_fn, replacement_fn); 35 36 Self { 37 hook_name, 38 raw, 39 enabled: true, 40 } 41 } 42 43 /// Create a new function hook from a signature. 44 /// 45 /// # Examples 46 /// 47 /// ``` 48 /// extern "C" fn replacement_fn() { 49 /// println!("Hello, world!"); 50 /// } 51 /// 52 /// unsafe fn enable() { 53 /// let hook = Hook::from_signature("my_hook", "48 8B 05 ? ? ? ? 48 8B 0C C8", replacement_fn as _); 54 /// } 55 #[allow(unused)] 56 pub unsafe fn from_signature( 57 hook_name: &str, 58 signature: &str, 59 replacement_fn: *mut c_void, 60 ) -> Option<Self> { 61 let hook_name = hook_name.to_string(); 62 let result = crate::sys::hook_from_signature( 63 hook_name.clone().into(), 64 signature.into(), 65 replacement_fn, 66 ); 67 68 result.map(|raw| Self { 69 hook_name, 70 raw, 71 enabled: true, 72 }) 73 } 74 } 75 76 pub trait Hookable { 77 /// Check if the hook is currently applied. 78 unsafe fn is_applied(&self) -> bool; 79 80 /// Apply the hook. 81 /// 82 /// # Errors 83 /// 84 /// Returns an error if the hook is already applied or if the hook failed to apply. 85 /// 86 /// # Safety 87 /// 88 /// This function is unsafe because it crosses the FFI boundary and writes directly to memory. 89 unsafe fn apply(&mut self) -> anyhow::Result<()>; 90 91 /// Revert the hook. 92 /// 93 /// # Errors 94 /// 95 /// Returns an error if the hook is not applied or if the hook failed to revert. 96 /// 97 /// # Safety 98 /// 99 /// This function is unsafe because it crosses the FFI boundary and writes directly to memory. 100 unsafe fn revert(&mut self) -> anyhow::Result<()>; 101 102 /// Check if the hook should be enabled. 103 /// 104 /// If the hook is not enabled, the hook will not be applied when the plugin is enabled. 105 fn is_enabled(&self) -> bool; 106 107 /// Set whether the hook should be enabled. 108 /// 109 /// If the hook is not enabled, the hook will not be applied when the plugin is enabled. 110 fn set_enabled(&mut self, enabled: bool); 111 112 /// Get the name of the hook. 113 /// 114 /// This is the name used to identify the hook in the configuration file. 115 fn get_name(&self) -> &str; 116 117 /// Check if the hook should be enabled based on the configuration (id: `hook::hook_name`). 118 /// 119 /// If the configuration value is not found, the default value is used. 120 /// 121 /// # Safety 122 /// 123 /// This function is unsafe because it crosses the FFI boundary. 124 unsafe fn is_config_enabled(&self, plugin_id: &str) -> bool; 125 } 126 127 impl Hookable for Hook { 128 unsafe fn is_applied(&self) -> bool { 129 crate::sys::hook_is_applied(&self.raw) 130 } 131 132 unsafe fn apply(&mut self) -> anyhow::Result<()> { 133 if self.is_applied() { 134 Err(anyhow::anyhow!("Hook is already applied.")) 135 } else if crate::sys::hook_apply(&mut self.raw) { 136 Ok(()) 137 } else { 138 Err(anyhow::anyhow!("Failed to apply hook.")) 139 } 140 } 141 142 unsafe fn revert(&mut self) -> anyhow::Result<()> { 143 if !self.is_applied() { 144 Err(anyhow::anyhow!("Hook is not applied.")) 145 } else if crate::sys::hook_revert(&mut self.raw) { 146 Ok(()) 147 } else { 148 Err(anyhow::anyhow!("Failed to revert hook.")) 149 } 150 } 151 152 fn is_enabled(&self) -> bool { 153 self.enabled 154 } 155 156 fn set_enabled(&mut self, enabled: bool) { 157 self.enabled = enabled; 158 } 159 160 fn get_name(&self) -> &str { 161 &self.hook_name 162 } 163 164 unsafe fn is_config_enabled(&self, plugin_id: &str) -> bool { 165 let result = crate::sys::get_setting_bool( 166 plugin_id.into(), 167 format!("hook::{}", self.get_name()).into(), 168 ); 169 170 match result.found { 171 true => result.value, 172 false => self.is_enabled(), 173 } 174 } 175 }