ApiScope 是一个 Windows x64 研究工具,用于跟踪新进程或运行进程中选定的 API 调用。它通过 Windows 调试器 API 跟踪 DLL 加载事件,并在被调试者从每个事件继续运行之前安装钩子。
包含的钩子:
ntdll.dll!NtCreateFile(记录目标路径)ntdll.dll!NtOpenFile(记录目标路径)ntdll.dll!NtReadFilentdll.dll!NtWriteFilentdll.dll!NtClosentdll.dll!NtOpenKey(记录键路径)ntdll.dll!NtSetValueKeyntdll.dll!NtQueryValueKeybcrypt.dll!BCryptOpenAlgorithmProvider要求:
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-apiscope-hooks.ps1 .\build\bin\Release\apiscope-hooks.dll
powershell -ExecutionPolicy Bypass -File .\scripts\smoke.ps1 -SkipBuild
Zydis v4.1.1 在固定提交处被获取,并且仅链接到 apiscope.exe 中。注入的 apiscope-hooks.dll 保持无导入。
apiscope.exe [--help | --version | --list-hooks]
apiscope.exe run -k <module!export|all> [-k <module!export>] [-f text|jsonl] [-o <path>] [-q] -- <program> [args...]
apiscope.exe attach -p <pid> -k <module!export|all> [-k <module!export>] [-f text|jsonl] [-o <path>] [-q]
钩子名称始终以模块限定。模块匹配不区分大小写;导出匹配区分大小写。
.\apiscope.exe run `
--hook ntdll.dll!NtCreateFile `
--hook bcrypt.dll!BCryptOpenAlgorithmProvider `
-- C:\path\app.exe
.\apiscope.exe attach --pid 4242 --hook all
.\apiscope.exe run --hook all --format jsonl --output trace.jsonl --quiet -- app.exe
终端事件保持可读,而 --output 可选地将文本或 JSONL 输出到文件。--quiet 抑制终端事件镜像。--color auto|always|never 控制终端颜色(默认 auto:在 TTY 上启用,管道或 NO_COLOR 下禁用);颜色永远不会出现在 --output 文件中。在 TTY 上,ApiScope 还会显示一个实时状态页脚(事件、丢弃、速率以及每个钩子的计数),该页脚在原地重绘;使用 --status auto|always|never 控制。
[*] ntdll.dll!NtWriteFile ----------
timestamp : 2026-06-07T17:44:23.3834340Z
thread_id : 3508
sequence : 3
file_handle : 0x000000000000008C
length : 16
buffer_ascii : Hello, ApiScope!
result : STATUS_SUCCESS (0x00000000)
JSONL 事件包含通用元数据和钩子本地字段:
{"schema_version":1,"sequence":1,"module":"bcrypt.dll","api":"BCryptOpenAlgorithmProvider","hook":"bcrypt.dll!BCryptOpenAlgorithmProvider","fields":{"flags":0,"result":"STATUS_SUCCESS (0x00000000)"}}
有关事件信封和每个类型字段的编码(指针和状态以 0x 十六进制字符串呈现),请参见 SCHEMA.md。
按 Ctrl+C 或 Ctrl+Break 以恢复活动钩子、释放远程工具并分离。目标继续运行。在自然退出时,ApiScope 以十进制和十六进制打印目标状态,后跟 stderr 上的会话摘要(事件、丢弃和每个钩子的计数)。
NtCreateFile、NtOpenFile 和 NtOpenKey 从 OBJECT_ATTRIBUTES 解析目标 path,并报告生成的句柄。ApiScope 记录每个成功的打开操作,并在同一句柄上的后续操作(读取、写入、注册表值访问以及匹配的 NtClose)上注释解析的 path,因此无需手动跟踪句柄即可阅读活动。NtClose 还会逐出句柄,而相对打开通过先前看到的 root_directory 句柄解析。
[*] ntdll.dll!NtCreateFile ----------
sequence : 2
path : test_file.txt
file_handle : 0x000000000000008C
result : STATUS_SUCCESS (0x00000000)
[*] ntdll.dll!NtReadFile ----------
sequence : 3
file_handle : 0x000000000000008C
buffer_ascii : Hello, ApiScope!
result : STATUS_SUCCESS (0x00000000)
path : test_file.txt
读取和写入事件上的 path 是从句柄关联的,而不是在调用本身中观察到的。
每个钩子是 src/apiscope-hooks/hooks/ 下的一个文件,并一起声明其源模块、导出、处理程序、蹦床槽、调用约定和签名:
DEFINE_API_HOOK(
BCryptOpenAlgorithmProvider,
"bcrypt.dll",
"BCryptOpenAlgorithmProvider",
NTSTATUS,
WINAPI,
PVOID* Algorithm,
const wchar_t* AlgorithmId,
const wchar_t* Implementation,
ULONG Flags) {
TraceEvent event;
InitializeTraceEvent(&event, "bcrypt.dll", "BCryptOpenAlgorithmProvider");
AddTraceUInt32(&event, "flags", Flags);
NTSTATUS result = CALL_ORIGINAL(
BCryptOpenAlgorithmProvider,
Algorithm,
AlgorithmId,
Implementation,
Flags);
AddTraceStatus(&event, "result", result);
EmitTraceEvent(&event);
return result;
}
apiscope.exe --list-hooks 发现钩子 DLL 导出的固定布局描述符。无需中央 API 列表或启动器端事件模式。
sequenceDiagram
actor User
participant ApiScope as apiscope.exe
participant Debugger as Windows 调试器 API
participant Target as 目标进程
participant Modules as 加载的 DLL
participant Hooks as apiscope-hooks.dll
participant Ring as 共享内存环
User->>ApiScope: 运行程序或附加 PID
alt 运行
ApiScope->>Debugger: CreateProcess(DEBUG_ONLY_THIS_PROCESS)
else 附加
ApiScope->>Debugger: DebugActiveProcess(PID)
end
ApiScope->>Debugger: DebugSetProcessKillOnExit(FALSE)
Debugger-->>ApiScope: CREATE_PROCESS_DEBUG_EVENT
ApiScope->>Target: 映射无导入的挂钩映像
ApiScope->>Ring: 创建并初始化有界环
ApiScope->>Target: 使用 NtMapViewOfSection 映射共享节
loop CREATE_PROCESS / LOAD_DLL 事件
Debugger-->>ApiScope: 模块基址和文件句柄
ApiScope->>Modules: 注册模块名称和基址
alt ntdll.dll 已加载
ApiScope->>Target: 构建未修补的 NtReadVirtualMemory 绕过
end
ApiScope->>Modules: 解析 module.dll!Export 和转发器
alt 导出和依赖项已加载
ApiScope->>Hooks: 解析处理程序和蹦床槽
ApiScope->>Target: 分配蹦床并修补导出
else 依赖项尚未加载
ApiScope->>ApiScope: 保持钩子挂起
end
ApiScope->>Debugger: ContinueDebugEvent
end
Target->>Hooks: 调用修补后的 API
Hooks->>Target: 通过蹦床调用 CALL_ORIGINAL
Hooks->>Ring: 发布有界 TLV 事件
opt 挂起批次中的第一个事件
Hooks->>Target: 通过未修补的 NtSetEvent 信号读取器
end
Ring-->>ApiScope: 排空事件批次
ApiScope-->>User: 可读文本和可选的 JSONL
opt UNLOAD_DLL_DEBUG_EVENT
Debugger-->>ApiScope: 模块已卸载
ApiScope->>Target: 释放关联的钩子状态
ApiScope->>Debugger: ContinueDebugEvent
end
alt 目标退出
Debugger-->>ApiScope: EXIT_PROCESS_DEBUG_EVENT 和退出状态
ApiScope->>Ring: 排空排队事件
ApiScope-->>User: 打印十进制和十六进制退出状态
else Ctrl+C 或 Ctrl+Break
User->>ApiScope: 停止跟踪
ApiScope->>Target: DebugBreakProcess
ApiScope->>Target: 恢复钩子并释放工具
ApiScope->>Debugger: DebugActiveProcessStop
ApiScope-->>User: 目标继续运行
end
控制器创建一个分页文件支持的节,并将其映射到两个进程中。钩子线程使用每个槽的序列号将事件发布到固定容量、多生产者环中。一个未修补的 NtSetEvent 为每个挂起批次发送一个合并的唤醒信号,控制器以批次方式排空事件。生产者从不等待读取器;当环满时丢弃并计数事件。缓冲区预览仍然使用未修补的 NtReadVirtualMemory 绕过,以便无效的目标指针不会导致钩子崩溃。
cmake/ 依赖配置
docs/ 事件模式和格式参考
scripts/ 验证和运行时冒烟测试
src/apiscope/ CLI、调试器、映射器、修补器和渲染器
src/apiscope-hooks/ 无导入的钩子 DLL 和独立钩子
src/include/ 共享契约
tests/ 单元和运行时测试目标
ntdll.dll!NtClose 钩子激活时逐出;没有该钩子时,重复使用的已关闭句柄会保留其先前的路径。DebugSetProcessKillOnExit(FALSE) 会保持目标存活,但钩子和映射的工具会驻留直到目标退出。仅在您有权检查的系统与进程上使用 ApiScope。请参阅 SECURITY.md、CONTRIBUTING.md 和 ROADMAP.md。
MIT。第三方组件保留其许可证;请参阅 THIRD_PARTY_NOTICES.md。