MinHook-rs
A Rust implementation of the MinHook library for Windows x64 function hooking.
Installation
[]
= "2.2"
= { = "0.61", = [
"Win32_UI_WindowsAndMessaging",
"Win32_Foundation"
] }
Quick Start
Every hook follows the same six-step lifecycle:
initialize → create_hook_api → enable_hook → [use] → disable_hook → remove_hook → uninitialize
Here is a minimal MessageBoxA hook that intercepts the call and replaces the message text:
use *;
use c_void;
use ptr;
use HWND;
use UI;
use PCSTR;
type MessageBoxAFn = unsafe extern "system" fn ;
static mut ORIGINAL: = None;
unsafe extern "system"
API Reference
Library Management
initialize
Hook Creation
// Hook by address — returns trampoline
create_hook // Hook by API name — returns (trampoline, target)
create_hook_api
Hook Control
enable_hook // pass ALL_HOOKS to affect all
disable_hook // Deferred batch operations
queue_enable_hook // commits all queued changes atomically
Usage Patterns
Hook by Address
Use create_hook when you already have a function pointer.
initialize?;
let trampoline = create_hook?;
enable_hook?;
Bulk Enable / Disable
ALL_HOOKS (a null pointer sentinel) targets every registered hook at once.
enable_hook?;
disable_hook?;
Queued (Atomic) Operations
Queue multiple changes and apply them in a single atomic step:
queue_enable_hook?;
queue_disable_hook?;
apply_queued?;
Error Handling
match create_hook_api
Error Types
| Error | Description |
|---|---|
NotInitialized |
Call initialize() first |
AlreadyCreated |
Hook already exists for this target |
NotCreated |
No hook registered for this target |
Enabled / Disabled |
Hook already in the requested state |
ModuleNotFound |
Specified module is not loaded |
FunctionNotFound |
Function not exported by the module |
UnsupportedFunction |
Prologue too short or contains unhookable instructions |
MemoryAlloc / MemoryProtect |
Windows memory operation failed |
x86_64 Instruction Structure
x86_64 uses variable-length encoding (1–15 bytes per instruction). Understanding the encoding is essential for trampoline generation, because the library must copy whole instructions out of the target prologue without splitting one in the middle.
[Legacy Prefixes] [REX] [Opcode] [ModR/M] [SIB] [Displacement] [Immediate]
0–4 0–1 1–3 0–1 0–1 0–4/8 0–4/8
Legacy Prefixes
| Byte | Meaning |
|---|---|
F0 |
LOCK |
F2 / F3 |
REPNE / REP |
66 |
Operand-size override (switches to 16-bit) |
67 |
Address-size override |
2E 36 3E 26 64 65 |
Segment overrides |
REX Prefix (x64 only)
Occupies one byte in the range 40–4F. Its four low bits extend register fields:
Bit 3 (W) — 64-bit operand size
Bit 2 (R) — extends ModR/M reg
Bit 1 (X) — extends SIB index
Bit 0 (B) — extends ModR/M r/m or SIB base
REX.W = 1 is required for most 64-bit operations (mov rax, ..., add rbx, ..., etc.).
ModR/M and SIB
ModR/M (1 byte) encodes the addressing mode and operands:
bits 7–6 mod — 00: [reg], 01: [reg+disp8], 10: [reg+disp32], 11: reg direct
bits 5–3 reg — source/destination register or opcode extension
bits 2–0 r/m — second operand or memory base register
When mod != 11 and r/m == 100 (RSP), a SIB byte follows:
bits 7–6 scale — multiplier: 1/2/4/8
bits 5–3 index — index register (RSP here means no index)
bits 2–0 base — base register (RBP here with mod=00 means disp32 only)
RIP-relative Addressing
The most important case for hooking. When mod == 00 and r/m == 101, the effective address is RIP + disp32:
mov rax, [rip + 0x1234] ; 48 8B 05 34 12 00 00
lea rcx, [rip + label] ; 48 8D 0D ...
call [rip + iat_slot] ; FF 15 ...
When copying these instructions into a trampoline buffer at a different address, the disp32 must be recalculated so the operand still points at the original target. This is the main transformation trampoline.rs performs.
Relative Jumps and Calls
Branches use a signed offset from the end of the instruction, not an absolute address:
jmp rel8 ; EB xx — ±127 bytes
jmp rel32 ; E9 xx xx xx xx — ±2 GB
call rel32 ; E8 xx xx xx xx — ±2 GB
jz rel32 ; 0F 84 xx xx xx xx
Trampolines replace these with absolute indirect jumps (FF 25 00000000 <addr>) so they work regardless of where the trampoline buffer was allocated.
Requirements
- Architecture: x86_64 only
- OS: Windows
- Rust: 1.85.0+
- Privileges: Administrator required for system-level hooks
License
MIT — see LICENSE.