sonx4444/hook-nt
GitHub: sonx4444/hook-nt
HookNt是一款用于拦截和监控Windows NT API调用的研究工具。
Stars: 9 | Forks: 2
# 钩子Nt
[](https://github.com/sonx4444/hook-nt/actions/workflows/build.yml)
[](LICENSE)
HookNt 是一个用于观察其控制下启动或通过 PID 连接的进程中的选定 NT 文件 API 的小型 Windows x64 研究工具。它映射一个无导入的钩子 DLL,在 `ntdll.dll` 中安装跳板,并通过命名管道发送结构化事件到启动器。
## 快速开始
要求:
- Windows x64
- Visual Studio 2019 或更高版本,带有 C++ 工作负载
- CMake 3.20 或更高版本
- Git,由 CMake 在子模块不存在时用于获取 DiStorm
构建和运行端到端烟雾测试:
```
powershell -ExecutionPolicy Bypass -File .\scripts\smoke.ps1
```
或手动构建:
```
build.bat
cd build\bin\Release
hooknt.exe run -k all -- test_file_ops.exe
```
`build.bat` 配置为发布构建,编译项目,在构建过程中验证注入的 DLL 合同,并运行 CTest 测试套件。
## 演示
在将结构化 JSONL 事件发送到 `trace.jsonl` 的同时跟踪记事本:
```
.\hooknt.exe run --hook all --format jsonl --output trace.jsonl -- C:\Windows\System32\notepad.exe
```

[查看 MP4 录像](./imgs/demo_run.mp4).
示例终端输出:
```
[*] NtWriteFile ----------
timestamp : 2026-06-01T12:34:56.1234567Z
thread_id : 4240
sequence : 3
dropped_before: 0
file_handle : 0x000000000000004C
length : 14
buffer : 48 65 6C 6C 6F 2C 20 48 6F 6F 6B 4E 74 21
buffer_text : Hello, HookNt!
buffer_status : 0x00000000
result : 0x00000000
```
## 支持的钩子
```
NtCreateFile
NtReadFile
NtWriteFile
```
用法:
```
hooknt.exe [--help | --version | --list-hooks]
hooknt.exe run -k [-k ] [-f text|jsonl] [-o ] [-q] -- [args...]
hooknt.exe attach -p -k [-k ] [-f text|jsonl] [-o ] [-q]
```
简短选项直接映射到其长形式:`-h` 是 `--help`,`-V` 是 `--version`,`-l` 是 `--list-hooks`,`-k` 是 `--hook`,`-f` 是 `--format`,`-o` 是 `--output`,`-q` 是 `--quiet`。
连接到现有进程并记录直到它退出:
```
hooknt.exe attach -p 4242 -k all -f jsonl -o trace.jsonl
```
连接会暂时挂起目标,同时安装 DLL、传输和钩子,然后恢复。目标必须是当前用户可以使用查询、VM、复制句柄、同步和挂起/恢复权限打开的 Windows x64 进程。提升的目标可能需要从提升的终端运行 `hooknt.exe`。
`--list-hooks` 从 `ntdlln.dll` 的导出符号对中发现支持的钩子。`--hook all` 启用所有发现的钩子。跟踪事件以可读文本的形式在终端中呈现。`--output` 将事件发送到文件,可选 JSONL 格式。`--format` 仅与 `--output` 一起使用:
```
hooknt.exe run --hook all --format jsonl --output trace.jsonl -- test_file_ops.exe
```
使用 `--quiet` 与 `--output` 来抑制终端事件镜像。不支持的钩子名称在目标进程启动或挂起之前失败。
JSONL 事件使用每个钩子发出的相同通用字段:
```
{"sequence":3,"dropped_before":0,"timestamp":"2026-06-01T12:34:56.1234567Z","timestamp_100ns":134247908961234567,"thread_id":4240,"api":"NtWriteFile","status":0,"truncated":false,"field_error":false,"fields":{"file_handle":136,"length":14,"buffer":{"type":"bytes","requested":14,"captured":14,"capture_status":0,"hex":"48656C6C6F2C20486F6F6B4E7421","text":"Hello, HookNt!"}}}
```
## 添加钩子
每个钩子都是 `src/ntdlln/hooks/` 下的独立文件。当 DLL 导出一个匹配的对时,钩子自动可用:
```
NtNewFunctionN
NtNewFunctionTrampoline
```
使用 `nt_hook.h` 中的 `DEFINE_NT_HOOK` 和 `CALL_ORIGINAL`。通过 `trace_transport.h` 发射有界协议事件:
```
#include "nt_hook.h"
#include "trace_transport.h"
DEFINE_NT_HOOK(NtNewFunction, HANDLE Handle) {
TraceEvent event;
InitializeTraceEvent(&event, "NtNewFunction");
AddTracePointer(&event, "handle", Handle);
NTSTATUS result = CALL_ORIGINAL(NtNewFunction, Handle);
event.header.status = result;
EmitTraceEvent(&event);
return result;
}
```
事件构建器支持指针、`uint32`、`uint64`、NT 状态值和有界缓冲区预览。字段名称使用小写 `snake_case`。事件是自我描述的,因此不需要编辑中央模式表、渲染分支或启动器允许列表。重新构建并验证注册:
```
hooknt.exe --list-hooks
```
## 工作原理
```
sequenceDiagram
participant User
participant HookNt as hooknt.exe
participant Target as Target process
participant NTDLL as ntdll.dll
participant Hooks as ntdlln.dll
User->>HookNt: Launch target or attach by PID
HookNt->>Target: Create or briefly suspend process
HookNt->>Target: Map ntdlln.dll
HookNt->>Target: Duplicate named-pipe client handle
HookNt->>NTDLL: Create unpatched transport bypass trampolines
loop Each requested hook
HookNt->>NTDLL: Resolve NT export
HookNt->>Hooks: Resolve hook and trampoline slot
HookNt->>Target: Allocate trampoline and patch export
end
HookNt->>Target: Resume and wait
Target->>Hooks: Capture bounded event and invoke original trampoline
Hooks->>NTDLL: Continue original function
Hooks->>HookNt: Write event through unpatched NtWriteFile bypass
```
钩子 DLL 没有导入、TLS 回调或入口点,符合最小映射器的合同。它使用未修补的 `NtWriteFile` 绕过写入自我描述的二进制 TLV 事件,因此跟踪 `NtWriteFile` 不会递归。每个事件都从共享用户数据页面接收钩子入口时间戳和来自 x64 TEB 的原始线程 ID。每个事件仅在其固定 1024 字节堆栈容量内使用其初始化的字节。缓冲区预览使用未修补的 `NtReadVirtualMemory` 绕过,并保持在 64 字节以内。管道写入是非阻塞的;当读取者跟不上的情况下,事件会被丢弃并计数。
修补器将完整的指令反汇编,直到它有足够的字节来执行 x64 绝对跳转。它拒绝 RIP 相对和相对控制流指令,因为复制的跳板指令尚未重定位。
## 项目布局
```
cmake/ DiStorm dependency configuration
scripts/ End-to-end smoke test
src/hooknt/ Launcher, mapper, and patcher
src/ntdlln/ Injected hook DLL and standalone hook modules
src/include/ Shared headers
tests/ CTest targets
```
## 构建 和 测试
```
cmake -S . -B build -A x64
cmake --build build --config Release
ctest --test-dir build -C Release --output-on-failure
powershell -ExecutionPolicy Bypass -File .\scripts\validate-ntdlln.ps1 .\build\bin\Release\ntdlln.dll
powershell -ExecutionPolicy Bypass -File .\scripts\smoke.ps1 -SkipBuild
```
当 `libs/distorm` 未初始化时,DiStorm 会自动从其固定的提交中获取。现有的子模块检查仍然有效。
烟雾测试套件包括挂起启动、简短的 CLI 别名、JSONL tee 输出、安静模式、并发文件操作以及针对运行的多线程目标的按 PID 连接。
## 局限性
- 仅限 Windows x64。
- 连接设置会暂时挂起目标进程。
- 当前连接记录会一直运行到目标退出;提前停止 `hooknt.exe` 不会从目标卸载仪器。
- 应该同时只有一个 HookNt 会话对目标进行仪器化。
- 支持三个 NT 文件 API。
- 使用有界自我描述的协议事件和 64 字节缓冲区预览。
- 当管道满时,丢弃跟踪事件而不是阻塞目标。
- 拒绝需要指令重定位的跳板。
- 使用针对此钩子 DLL 的最小手动映射器,而不是通用反射加载器。
- 钩子安全敏感的进程内部可能会触发端点安全产品。
仅在使用 HookNt 的系统和进程时进行测试。请参阅 [SECURITY.md](SECURITY.md) 和 [ROADMAP.md](ROADMAP.md).
## 许可证
[MIT](LICENSE).
标签:ATT&CK Framework, C++ Development, CMake, Debugging, DLL Injection, File Operations, Git, Hooking, JSON Logging, Named Pipe Communication, NT File APIs, Performance Monitoring, Process Monitoring, Read Operations, Research Tool, Security Analysis, Structured Logging, System Call Interception, Visual Studio, Windows API, Write Operations, 端点可见性