Skip to main content

Module inline

Module inline 

Source
Available on crate feature inline only.
Expand description

Inline hooking.

  • Supported CPU architectures: x86, x64, ARM64.

  • Support all common ABIs.

    • On x86/x64, system ABI (system, stdcall/win64) and System V ABI (sysv64) are tested.
  • no_std and depend on Ntdll.dll only (if tracing is not enabled).

  • RAII (drop guard) design.

    To leak the hook, wrap InlineHook as std::mem::ManuallyDrop<InlineHook> (or call std::mem::forget()).

  • Thread unsafe at the moment.

    If you may enable/disable hooks from multiple threads at the same time, use a std::sync::Mutex lock.

  • To init a (mut) static, InlineHook::new() can be used.

§Examples

// cargo add ib-hook --features inline
use ib_hook::inline::InlineHook;

extern "system" fn original(x: u32) -> u32 { x + 1 }

// Hook the function with a detour
extern "system" fn hooked(x: u32) -> u32 { x + 0o721 }
let mut hook = InlineHook::<extern "system" fn(u32) -> u32>::new_enabled(original, hooked).unwrap();
assert!(hook.is_enabled());

// Now calls to original are redirected to hooked
assert_eq!(original(0x100), 721); // redirected to hooked: 0x100 + 0o721 = 721

// Access original via trampoline
assert_eq!(hook.trampoline()(0x100), 0x101); // 0x100 + 1

// Disable the hook manually (or automatically on drop)
hook.disable().unwrap();
assert!(!hook.is_enabled());
assert_eq!(original(0x100), 0x101); // back to original

§Multiple hooks

There are mainly four ways to storing multiple hooks:

  • Custom struct: Store static hooks.
    • no_std
  • Vec<InlineHook<F>>: Store dynamic hooks of the same function type.
  • HashMap<F, InlineHook<F>>: Store dynamic hooks of the same function type, indexed by target function.
  • InlineHookMap: Store dynamic hooks of different function types, indexed by target function.

However, as ID args aren’t supported at the moment, dynamic hooks aren’t quite useful unless you don’t need trampoline.

§InlineHookMap example

// cargo add ib-hook --features inline
use ib_hook::inline::{InlineHook, InlineHookMap};

type MyFn = extern "system" fn(u32) -> u32;

extern "system" fn original1(x: u32) -> u32 { x + 1 }
extern "system" fn original2(x: u32) -> u32 { x + 2 }

extern "system" fn hooked1(x: u32) -> u32 { x + 0o721 }
extern "system" fn hooked2(x: u32) -> u32 { x + 0o722 }

// Create a collection of hooks
let mut hooks = InlineHookMap::new();
hooks.insert::<MyFn>(original1, hooked1);
// Insert and enable a hook
hooks.insert::<MyFn>(original2, hooked2).enable().unwrap();

// Enable all hooks at once
hooks.enable().on_error(|target, e| eprintln!("Target {target:?} failed: {e:?}"));

// Verify hooks are enabled
assert_eq!(original1(0x100), 721); // redirected to hooked1
assert_eq!(original2(0x100), 722); // redirected to hooked2

// Disable all hooks at once
hooks.disable().on_error(|target, e| eprintln!("Target {target:?} failed: {e:?}"));

// Verify hooks are disabled
assert_eq!(original1(0x100), 0x101); // back to original
assert_eq!(original2(0x100), 0x102); // back to original

// Access individual hooks by target function
if let Some(hook) = hooks.get::<MyFn>(original1) {
    println!("Hook is enabled: {}", hook.is_enabled());
}

§Disclaimer

This is currently implemented as a wrapper of KNSoft.SlimDetours, for type safety and RAII (drop guard).

Ref: https://github.com/Chaoses-Ib/ib-shell/pull/1

Structs§

InlineHook
Type-safe and RAII (drop guard) wrapper of an inline hook.
InlineHookMapstd
A type-erased map of inline hooks indexed by target function pointer.