Skip to main content

ib_hook/
lib.rs

1/*!
2Windows binary and system hooking library.
3
4Features:
5- [Inline hooking](#inline-hooking):
6  Hook functions on x86/x64/ARM64, `no_std` and `Ntdll.dll` only.
7- [DLL injection](#dll-injection):
8  Inject DLL into processes with optional RPC and auto self unload.
9- [Windows shell hook (`WH_SHELL`)](#windows-shell-hook-wh_shell):
10  Monitor window operations: creating, activating, title redrawing, monitor changing...
11- [GUI process watcher](#gui-process-watcher):
12  Monitor GUI processes.
13
14## Inline hooking
15- Supported CPU architectures: x86, x64, ARM64.
16- Support system ABI (`system`, `stdcall`/`win64`) only.
17- `no_std` and depend on `Ntdll.dll` only.
18
19See [`inline`] module for more details. Here is a quick example:
20```
21// cargo add ib-hook --features inline
22use ib_hook::inline::InlineHook;
23
24extern "system" fn original(x: u32) -> u32 { x + 1 }
25
26// Hook the function with a detour
27extern "system" fn hooked(x: u32) -> u32 { x + 0o721 }
28let mut hook = InlineHook::<extern "system" fn(u32) -> u32>::new(original, hooked).unwrap();
29
30// Now calls to original are redirected to hooked
31assert_eq!(original(0x100), 721); // redirected to hooked: 0x100 + 0o721 = 721
32
33// Access original via trampoline
34assert_eq!(hook.trampoline()(0x100), 0x101); // 0x100 + 1
35
36// Disable the hook manually (or automatically on drop)
37hook.disable().unwrap();
38assert_eq!(original(0x100), 0x101); // back to original
39```
40
41## DLL injection
42Inject DLL into processes with optional RPC and auto self unload.
43
44- Optional RPC with `serde` input and output.
45- RAII (drop guard) design with optional `leak()`.
46- Single DLL injection / Multiple DLL injection manager.
47- Optioanlly, in the DLL, unload self automatically if the injector process aborted.
48
49See [`inject::dll`] module for more details. Here is a quick example:
50```no_run
51use ib_hook::inject::dll::app::{DllApp, DllInjectionVec};
52
53struct MyDll;
54impl DllApp for MyDll {
55    const APPLY: &str = "apply_hook";
56    type Input = String;
57    type Output = ();
58}
59
60// Inject into all processes named Notepad.exe
61let mut injections = DllInjectionVec::<MyDll>::new();
62injections.inject_with_process_name("Notepad.exe")
63    .dll_path(std::path::Path::new("hook.dll"))
64    .apply(&"input".into())
65    .on_error(|pid, err| ())
66    .call()
67    .unwrap();
68
69// Eject all manually or let drop handle it
70injections.eject().on_error(|pid, err| ()).call();
71```
72
73## Windows shell hook (`WH_SHELL`)
74Monitor window operations: creating, activating, title redrawing, monitor changing...
75
76See [`windows::shell`] module for more details. Here is a quick example:
77```no_run
78use ib_hook::windows::shell::{ShellHook, ShellHookMessage};
79{
80    let hook = ShellHook::new(Box::new(|msg: ShellHookMessage| {
81        println!("{msg:?}");
82        false
83    }))
84    .unwrap();
85
86    // Perform window operations to see received events...
87    std::thread::sleep(std::time::Duration::from_secs(30));
88}
89// Shell hook unregistered
90```
91See also [ib-shell: Some desktop environment libraries, mainly for Windows Shell.](https://github.com/Chaoses-Ib/ib-shell)
92
93## GUI process watcher
94Monitor GUI processes.
95
96See [`process`] module for more details. Here is a quick example:
97```no_run
98use ib_hook::process::{GuiProcessEvent, GuiProcessWatcher};
99
100let watcher = GuiProcessWatcher::new(Box::new(|event| {
101    println!("Process event: {event:?}");
102})).unwrap();
103
104println!("Monitoring GUI processes...");
105std::thread::sleep(std::time::Duration::from_secs(60));
106```
107
108Apply a function on every existing and new GUI process exactly once:
109```no_run
110// cargo add ib-hook --features sysinfo
111use ib_hook::process::GuiProcessWatcher;
112
113let _watcher = GuiProcessWatcher::for_each(|pid| println!("pid: {pid}"))
114    .filter_image_path(|path| {
115        path.and_then(|p| p.file_name())
116            .is_some_and(|n| n.to_ascii_lowercase() == "notepad.exe")
117    })
118    .build();
119std::thread::sleep(std::time::Duration::from_secs(60));
120```
121
122## Crate features
123*/
124#![cfg_attr(docsrs, feature(doc_cfg))]
125#![cfg_attr(feature = "doc", doc = document_features::document_features!())]
126pub mod inject;
127#[cfg(feature = "inline")]
128pub mod inline;
129mod log;
130pub mod process;
131pub mod windows;
132
133/// Marker trait for [function pointers (`fn`)](https://doc.rust-lang.org/nightly/core/primitive.fn.html).
134pub trait FnPtr:
135    PartialEq
136    + Eq
137    + PartialOrd
138    + Ord
139    + core::hash::Hash
140    + core::fmt::Pointer
141    + core::fmt::Debug
142    + Clone
143    + Copy
144    + Send
145    + Sync
146    + Unpin
147    + core::panic::UnwindSafe
148    + core::panic::RefUnwindSafe
149    + Sized
150    + 'static
151{
152}
153
154impl<F> FnPtr for F where
155    F: PartialEq
156        + Eq
157        + PartialOrd
158        + Ord
159        + core::hash::Hash
160        + core::fmt::Pointer
161        + core::fmt::Debug
162        + Clone
163        + Copy
164        + Send
165        + Sync
166        + Unpin
167        + core::panic::UnwindSafe
168        + core::panic::RefUnwindSafe
169        + Sized
170        + 'static
171{
172}