use std::collections::HashMap;
use std::sync::Mutex;
static REGISTRY: Mutex<Option<HookRegistry>> = Mutex::new(None);
#[derive(Debug, Clone)]
pub struct RegisteredHook {
pub target: usize,
pub detour: usize,
pub trampoline: Option<usize>,
pub hook_type: HookType,
pub installed_at: std::time::Instant,
pub active: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HookType {
Inline,
HotPatch,
MidFunction,
Chained,
}
#[derive(Default)]
pub struct HookRegistry {
by_target: HashMap<usize, RegisteredHook>,
by_detour: HashMap<usize, usize>,
}
impl HookRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, hook: RegisteredHook) {
let target = hook.target;
let detour = hook.detour;
self.by_detour.insert(detour, target);
self.by_target.insert(target, hook);
}
pub fn unregister(&mut self, target: usize) -> Option<RegisteredHook> {
if let Some(hook) = self.by_target.remove(&target) {
self.by_detour.remove(&hook.detour);
Some(hook)
} else {
None
}
}
pub fn get(&self, target: usize) -> Option<&RegisteredHook> {
self.by_target.get(&target)
}
pub fn get_mut(&mut self, target: usize) -> Option<&mut RegisteredHook> {
self.by_target.get_mut(&target)
}
pub fn is_hooked(&self, target: usize) -> bool {
self.by_target.contains_key(&target)
}
pub fn find_by_detour(&self, detour: usize) -> Option<usize> {
self.by_detour.get(&detour).copied()
}
pub fn all(&self) -> impl Iterator<Item = &RegisteredHook> {
self.by_target.values()
}
pub fn count(&self) -> usize {
self.by_target.len()
}
pub fn clear(&mut self) {
self.by_target.clear();
self.by_detour.clear();
}
pub fn set_active(&mut self, target: usize, active: bool) -> bool {
if let Some(hook) = self.by_target.get_mut(&target) {
hook.active = active;
true
} else {
false
}
}
}
pub fn get_registry() -> std::sync::MutexGuard<'static, Option<HookRegistry>> {
let mut guard = REGISTRY.lock().unwrap();
if guard.is_none() {
*guard = Some(HookRegistry::new());
}
guard
}
pub fn with_registry<F, R>(f: F) -> R
where
F: FnOnce(&mut HookRegistry) -> R,
{
let mut guard = get_registry();
let registry = guard.as_mut().unwrap();
f(registry)
}
pub fn register_hook(
target: usize,
detour: usize,
trampoline: Option<usize>,
hook_type: HookType,
) {
with_registry(|registry| {
registry.register(RegisteredHook {
target,
detour,
trampoline,
hook_type,
installed_at: std::time::Instant::now(),
active: true,
});
});
}
pub fn unregister_hook(target: usize) -> Option<RegisteredHook> {
with_registry(|registry| registry.unregister(target))
}
pub fn is_hooked(target: usize) -> bool {
with_registry(|registry| registry.is_hooked(target))
}
pub fn get_hook_info(target: usize) -> Option<RegisteredHook> {
with_registry(|registry| registry.get(target).cloned())
}
pub fn get_hooks_in_range(start: usize, end: usize) -> Vec<RegisteredHook> {
with_registry(|registry| {
registry
.all()
.filter(|h| h.target >= start && h.target < end)
.cloned()
.collect()
})
}
pub fn active_hook_count() -> usize {
with_registry(|registry| registry.all().filter(|h| h.active).count())
}
pub fn total_hook_count() -> usize {
with_registry(|registry| registry.count())
}