pub mod error;
use std::iter;
pub use error::Error;
pub use retour_utils_impl::hook_module;
use windows::{
core::{PCSTR, PCWSTR},
Win32::{
Foundation::HMODULE,
System::LibraryLoader::{GetModuleHandleW, GetProcAddress},
},
};
type Result<T> = std::result::Result<T, error::Error>;
pub enum LookupData {
Offset {
module: &'static str,
offset: usize,
},
Symbol {
module: &'static str,
symbol: &'static str,
},
}
impl LookupData {
pub const fn from_offset(module: &'static str, offset: usize) -> Self {
Self::Offset { module, offset }
}
pub const fn from_symbol(module: &'static str, symbol: &'static str) -> Self {
Self::Symbol { module, symbol }
}
fn get_module(&self) -> &str {
match self {
Self::Offset { module, .. } => module,
Self::Symbol { module, .. } => module,
}
}
#[cfg(windows)]
fn address_from_handle(&self, handle: HMODULE) -> Option<*const ()> {
use std::ffi::CString;
match self {
LookupData::Offset { offset, .. } => {
Some((handle.0 as usize + offset) as *const ())
}
LookupData::Symbol { symbol, .. } => {
let c_symbol = CString::new(*symbol).ok()?;
let wrapped_ptr = PCSTR::from_raw(c_symbol.as_ptr() as *const u8);
unsafe { GetProcAddress(handle, wrapped_ptr) }.map(|func_ptr| func_ptr as *const ())
}
}
}
}
pub unsafe fn init_detour(
lookup_data: LookupData,
init_detour_fn: fn(*const ()) -> retour::Result<()>,
) -> Result<()> {
let module = lookup_data.get_module().to_string();
let module_w_ptr = module
.encode_utf16()
.chain(iter::once(0))
.collect::<Vec<u16>>()
.as_ptr();
let wrapped_ptr = PCWSTR::from_raw(module_w_ptr);
if let Ok(handle) = unsafe { GetModuleHandleW(wrapped_ptr) } {
let Some(addr) = lookup_data.address_from_handle(handle) else {
return Err(Error::ModuleNotLoaded);
};
init_detour_fn(addr)?;
}
Ok(())
}