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