Windows Process Hook Scanner — Detects IAT, EAT, and inline hooks in running Windows processes.
简体中文文档见 README.zh-CN.md。
HookScanTool is a Windows security/malware analysis tool that scans running processes for API hooks. It detects three types of hooks:
- IAT Hooks — Import Address Table tampering (function pointers redirected to different modules)
- EAT Hooks — Export Address Table tampering (exported function RVAs modified in memory)
- Inline Hooks — Function prologue patching (JMP/CALL/PUSH+RET/MOV+JMP to another module)
The tool compares each function's in-memory code against the on-disk DLL, using Zydis for instruction-level disassembly. Hook chains are traced through multiple JMP/CALL indirections to reveal the final target. The module that owns a hook's target address can be checked against a user-supplied SHA-256 signature database to mark known (e.g. security-software) hooks.
- GUI Mode — Dear ImGui + DirectX 11 interface with process tree, module triage panel, hook list, chain viewer, and one-click (background) restore
- CLI Mode — Command-line scanning with JSON/CSV/SARIF output and automated restore
- Continuous Monitoring —
--watchmode polls a process and alerts on newly appearing hooks - Fleet Scan —
--deepmode scans every accessible process in parallel - Selective Scanning — Recon module list with suspicion scores, scan only suspicious modules
- Module Triage — Suspicion scoring system (path, name, WoW64 heuristics)
- Hook Chain Tracing — Follows JMP/CALL/PUSH+RET/MOV+JMP chains to the final hook target
- Hook Restoration — Thread suspension, TOCTOU verification before writing, read-back verification after, and rollback on partial writes
- Signature Detection — SHA-256 known-signature database marks hook targets that belong to known modules
- WoW64 Support — Scan 32-bit processes from a 64-bit scanner via
NtWow64ReadVirtualMemory64 - API Set Awareness —
api-ms-*imports are resolved to their owner module and verified against its export table - JSON / CSV / SARIF Export — Versioned (
schema_version), UTF-8 safe, valid for strict parsers and code-scanning pipelines
- Compiler: MSVC 2022+ (or MinGW-w64 GCC)
- CMake: 3.20+
- Dependencies: Zydis 4.0.0 + Zycore, Dear ImGui 1.91.8 (included in
deps/, licenses in each directory)
cmake -B build -G "Visual Studio 17 2022" -A x64
cmake --build build --config Releasecmake -B build -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release
cmake --build build -j4Or use the helper script: ./build.sh [msvc|mingw] (set VS_GEN="Visual Studio 18 2026" for VS Insiders).
| Target | Path | Description |
|---|---|---|
hookscan_cli.exe |
build/Release/ (MSVC) or build/ (MinGW) |
Command-line tool |
hookscantool.exe |
same | GUI application |
cmake -B build -DHOOKSCAN_BUILD_TESTS=ON # add your generator/arch flags as above
cmake --build build --config Release
./build/Release/hookscan_test.exe # MinGW: ./build/hookscan_test.exe207 tests cover PE parsing, module scoring, inline instruction matching, CLI argument parsing, the JSON/CSV/SARIF writers, EAT alias matching, and the signature subsystem.
Run hookscantool.exe as Administrator for full process access.
- Select a process from the process tree (left panel)
- Click Scan Selected to run a full scan, or use the module triage panel to select specific modules
- View detected hooks in the hook list (middle-right); use Export JSON/CSV to save a report
- Click a hook to see its chain trace (bottom-right)
- Use Restore Hook to restore original bytes (runs in the background, UI stays responsive)
# List running processes
hookscan_cli.exe --list
# Scan a process for hooks
hookscan_cli.exe 1234
# Scan and write reports (JSON / CSV / SARIF are independent)
hookscan_cli.exe 1234 --json report.json --csv report.csv --sarif report.sarif
# List modules with suspicion scores
hookscan_cli.exe 1234 --modules
# Filter modules: s=suspicious only, a=all, m=non-microsoft
hookscan_cli.exe 1234 --modules --filter s
# Scan a specific module by index
hookscan_cli.exe 1234 --scan-module 5
# Restore a detected hook (asks for confirmation)
hookscan_cli.exe 1234 --restore 0
# Watch a process: rescan every 30s (default), alert on NEW hooks
hookscan_cli.exe 1234 --watch
hookscan_cli.exe 1234 --watch --interval 10 --json watch.json
# Scan every accessible process (parallel, 4 workers by default)
hookscan_cli.exe --deep
hookscan_cli.exe --deep --jobs 8 --filter s --json deep.json # list only processes with hooks
# Load a known-signature database (default: signatures.txt in the current directory)
hookscan_cli.exe 1234 --sigs signatures.txtWatch exit codes: 0 clean stop, 1 new hooks were seen (script/CI friendly), 2 target lost.
Reports are UTF-8 JSON with a schema version:
{
"schema_version": 1,
"pid": 1234,
"process_name": "target.exe",
"hook_count": 1,
"modules_scanned": 14,
"scan_time_ms": 210,
"truncated": false,
"hooks": [
{
"module": "user32.dll",
"function": "MessageBoxW",
"type": "INLINE",
"original_addr": "0x00007FFA12345678",
"current_addr": "0x00007FFA98765432",
"restorable": true,
"signature": "",
"chain_depth": 2,
"original_bytes": "48894C2408",
"hooked_bytes": "E9CD123400",
"chain": [
{"address": "0x00007FFA98765432", "disasm": "jmp 0x..."}
]
}
]
}--deep writes {"schema_version": 1, "process_count": N, "processes": [ ... ]} with the same report objects.
A signature database is a plain-text file (signatures.txt by default, see signatures.example.txt):
# <sha256 of the DLL file> <label>
9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 our-edit-hook-dll
When a detected hook's target address lands inside a module whose SHA-256 matches a database entry, the hook is labelled with that entry (visible in the CLI table, the JSON signature field). Hash a module with certutil -hashfile some.dll SHA256.
| Criterion | Points |
|---|---|
| Module not in Windows/Program Files | +30 |
| Suspicious name (hook, inject, patch, etc.) | +40 |
| WoW64 DLL in 64-bit process | +20 |
Score 0-30: Low risk | 31-60: Suspicious | 61-80: High | 81-100: Critical
- Inline detection depth — only the first 5 instructions of each function are compared against disk. Hooks placed after the prologue window are not detected.
- EAT hooks vs inline — EAT scan only compares export RVAs; a hook that patches a function body without changing the RVA is caught by the inline scan instead.
- Address-based API-set verification — an api-ms import hijacked to another legitimate export of the same owner module cannot be distinguished from a normal forward by address alone.
- Truncation — if the report hook buffer fills, the scan sets
"truncated": trueand results may be incomplete.
- Without elevation, most processes outside your own session return
ENGINE_ACCESS_DENIED. Run the CLI/GUI as Administrator for full coverage. - A tool that reads other processes' memory and writes to them will attract antivirus heuristics. If your AV quarantines HookScanTool, verify the published SHA-256 checksums against the release and add an exclusion.
| Code | Meaning |
|---|---|
ENGINE_OK (0) |
Success |
ENGINE_ACCESS_DENIED (-1) |
Cannot open process (run as Admin) |
ENGINE_PROCESS_NOT_FOUND (-2) |
Process or modules not found |
ENGINE_NO_MEMORY (-6) |
Out of memory |
ENGINE_SCAN_FAILED (-7) |
General scan failure |
ENGINE_READ_ERROR/ENGINE_WRITE_ERROR/ENGINE_PACKED_DLL are reserved (see src/engine/types.h): scanner-level read failures surface as "no hook", and packed images are signalled per-hook via restorable: false.
src/
├── engine/ # Core scanner engine (pure C)
│ ├── engine.h/.c # Public API: scan, recon, restore, JSON/CSV/SARIF export
│ ├── types.h # Shared type definitions + error codes
│ ├── pe_parser.h/.c # PE header parsing (in-memory + on-disk)
│ ├── process_enum.h/.c # Process/module enumeration
│ ├── iat_scanner.h/.c # IAT hook detection
│ ├── eat_scanner.h/.c # EAT hook detection
│ ├── inline_scanner.h/.c# Inline hook detection
│ ├── chain_tracer.h/.c # Hook chain tracing
│ ├── restore.h/.c # Hook restoration
│ ├── wow64.h/.c # WoW64 support
│ ├── sigcheck.h/.c # Known-signature database (SHA-256, BCrypt)
│ └── module_scorer.h/.c # Suspicion scoring
├── cli/
│ ├── main.c # CLI entry point
│ └── args.c/.h # Argument parsing (unit tested)
└── gui/
├── main.cpp # GUI entry point + main loop
├── ui_process_tree.cpp # Process tree panel
├── ui_module_list.cpp # Module triage panel
├── ui_hook_list.cpp # Hook list panel + report export
├── ui_chain_view.cpp # Chain viewer panel
└── ui_restore.cpp # Restore panel (background thread)
- Recon — Enumerate process modules, score each for suspicion
- IAT Scan — For each import, verify the function pointer points within the expected DLL's address range (follows export forwarders; api-ms imports are verified against the resolved owner module's export table)
- EAT Scan — Compare in-memory export RVAs against on-disk export RVAs (export-name aliases handled)
- Inline Scan — Disassemble first instructions of each exported function, compare against on-disk using Zydis
- Chain Tracing — Follow JMP/CALL/PUSH+RET/MOV+JMP indirections to the final hook target
- Signature Annotation — Hash the module owning each hook target and label known modules from the database
MIT — see LICENSE. Third-party dependencies (Zydis, Zycore, Dear ImGui) are vendored under their own MIT licenses in deps/.