
Windowsアプリケーションをインターセプトおよび監視するためのAPIフッキングフレームワーク
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]
フック名は常にモジュールで修飾されます。モジュールのマッチングは大文字小文字を区別しません。export のマッチングは大文字小文字を区別します。
.\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 をファイルに出力します(tee 出力)。--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 の 16 進文字列として表示)については SCHEMA.md を参照してください。
Ctrl+C または Ctrl+Break を押すと、アクティブなフックを復元し、リモート計装を解放し、デタッチします。ターゲットはそのまま実行を続けます。自然終了時には、ApiScope はターゲットのステータスを 10 進数と 16 進数で表示し、続いてセッションサマリー(イベント数、ドロップ数、フックごとのカウント)を 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/ 以下の 1 ファイルで、ソースモジュール、エクスポート、ハンドラ、トランポリンスロット、呼び出し規約、シグネチャを一緒に宣言します:
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 debugger API
participant Target as Target process
participant Modules as Loaded DLLs
participant Hooks as apiscope-hooks.dll
participant Ring as Shared-memory ring
User->>ApiScope: run program or attach PID
alt run
ApiScope->>Debugger: CreateProcess(DEBUG_ONLY_THIS_PROCESS)
else attach
ApiScope->>Debugger: DebugActiveProcess(PID)
end
ApiScope->>Debugger: DebugSetProcessKillOnExit(FALSE)
Debugger-->>ApiScope: CREATE_PROCESS_DEBUG_EVENT
ApiScope->>Target: Map import-free hook image
ApiScope->>Ring: Create and initialize bounded ring
ApiScope->>Target: Map shared section with NtMapViewOfSection
loop CREATE_PROCESS / LOAD_DLL events
Debugger-->>ApiScope: Module base and file handle
ApiScope->>Modules: Register module name and base
alt ntdll.dll loaded
ApiScope->>Target: Build unpatched NtReadVirtualMemory bypass
end
ApiScope->>Modules: Resolve module.dll!Export and forwarders
alt export and dependencies are loaded
ApiScope->>Hooks: Resolve handler and trampoline slot
ApiScope->>Target: Allocate trampoline and patch export
else dependency is not loaded yet
ApiScope->>ApiScope: Keep hook pending
end
ApiScope->>Debugger: ContinueDebugEvent
end
Target->>Hooks: Call patched API
Hooks->>Target: CALL_ORIGINAL through trampoline
Hooks->>Ring: Publish bounded TLV event
opt first event in a pending batch
Hooks->>Target: Signal reader through unpatched NtSetEvent
end
Ring-->>ApiScope: Drain event batches
ApiScope-->>User: Readable text and optional JSONL
opt UNLOAD_DLL_DEBUG_EVENT
Debugger-->>ApiScope: Module unloaded
ApiScope->>Target: Release associated hook state
ApiScope->>Debugger: ContinueDebugEvent
end
alt target exits
Debugger-->>ApiScope: EXIT_PROCESS_DEBUG_EVENT and exit status
ApiScope->>Ring: Drain queued events
ApiScope-->>User: Print decimal and hexadecimal exit status
else Ctrl+C or Ctrl+Break
User->>ApiScope: Stop tracing
ApiScope->>Target: DebugBreakProcess
ApiScope->>Target: Restore hooks and free instrumentation
ApiScope->>Debugger: DebugActiveProcessStop
ApiScope-->>User: Target continues running
endコントローラはページングファイルバックアップされたセクションを作成し、両方のプロセスにマッピングします。フックスレッドは、スロットごとのシーケンス番号を使用して、固定容量のマルチプロデューサリングに公開します。未パッチの NtSetEvent バイパスは、保留中のバッチごとに 1 つの統合ウェイクアップを送信し、コントローラはイベントをバッチで排出します。プロデューサはリーダーを待つことがありません。リングがいっぱいになるとイベントをドロップし、カウントします。バッファプレビューには、依然として未パッチの 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 を参照してください。