sonx4444/hook-nt

GitHub: sonx4444/hook-nt

HookNt是一款用于拦截和监控Windows NT API调用的研究工具。

Stars: 9 | Forks: 2

# 钩子Nt [![构建](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/1880abe640211547.svg)](https://github.com/sonx4444/hook-nt/actions/workflows/build.yml) [![许可证:MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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 ``` ![HookNt 跟踪记事本](https://raw.githubusercontent.com/sonx4444/hook-nt/main/imgs/demo_run.gif) [查看 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, 端点可见性