
Ein API-Hooking-Framework zum Abfangen und Überwachen von Windows-Anwendungen
ApiScope ist ein Windows x64-Forschungswerkzeug zur Verfolgung ausgewählter API-Aufrufe in einem neuen oder laufenden Prozess. Es folgt DLL-Ladeereignissen über die Windows-Debugger-API und installiert Hooks, bevor der Debuggee von jedem Ereignis fortfährt.
Enthaltene Hooks:
ntdll.dll!NtCreateFile (zeichnet den Zielpfad auf)ntdll.dll!NtOpenFile (zeichnet den Zielpfad auf)ntdll.dll!NtReadFilentdll.dll!NtWriteFilentdll.dll!NtClosentdll.dll!NtOpenKey (zeichnet den Schlüsselpfad auf)ntdll.dll!NtSetValueKeyntdll.dll!NtQueryValueKeybcrypt.dll!BCryptOpenAlgorithmProviderSehen Sie sich das MP4-Video an.
Voraussetzungen:
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 wird bei einem festgelegten Commit abgerufen und nur in apiscope.exe eingebunden. Die injizierte apiscope-hooks.dll bleibt importfrei.
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]
Hook-Namen sind immer modulqualifiziert. Die Modulübereinstimmung erfolgt case-insensitiv; die Exportübereinstimmung ist case-sensitiv.
.\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
Terminalereignisse bleiben lesbar, während --output optional Text oder JSONL in eine Datei umleitet. --quiet unterdrückt die Terminalereignisspiegelung. --color auto|always|never steuert die Terminalfarbe (Standard auto: an für ein TTY, aus bei Weiterleitung oder unter NO_COLOR); Farbe erscheint nie in --output-Dateien. Auf einem TTY zeigt ApiScope auch eine Live-Statusfußzeile (Ereignisse, Verluste, Rate und Pro-Hook-Zählungen), die an Ort und Stelle neu gezeichnet wird; steuern Sie es mit --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-Ereignisse enthalten generische Metadaten und hook-lokale Felder:
{"schema_version":1,"sequence":1,"module":"bcrypt.dll","api":"BCryptOpenAlgorithmProvider","hook":"bcrypt.dll!BCryptOpenAlgorithmProvider","fields":{"flags":0,"result":"STATUS_SUCCESS (0x00000000)"}}
Siehe SCHEMA.md für den Ereignisumschlag und die feldspezifischen Kodierungen (Zeiger und Status werden als 0x Hex-Strings dargestellt).
Drücken Sie Strg+C oder Strg+Untbrechen, um aktive Hooks wiederherzustellen, die entfernte Instrumentierung freizugeben und abzutrennen. Das Ziel läuft weiter. Bei natürlichem Beenden gibt ApiScope den Zielstatus in Dezimal und Hexadezimal aus, gefolgt von einer Sitzungszusammenfassung (Ereignisse, Verluste und Pro-Hook-Zählungen) auf stderr.
NtCreateFile, NtOpenFile und NtOpenKey lösen den Ziel-path aus OBJECT_ATTRIBUTES auf und melden das resultierende Handle. ApiScope zeichnet jedes erfolgreiche Öffnen auf und annotiert spätere Operationen auf demselben Handle – Lesevorgänge, Schreibvorgänge, Registrierungswertzugriffe und das passende NtClose – mit dem aufgelösten path, sodass die Aktivität ohne manuelle Handle-Verfolgung lesbar ist. NtClose entfernt auch das Handle, und relative Öffnungen werden über ein zuvor gesehenes root_directory-Handle aufgelöst.
[*] 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
Der path bei Lese- und Schreibereignissen wird vom Handle abgeleitet, nicht beim Aufruf selbst beobachtet.
Jeder Hook ist eine Datei unter src/apiscope-hooks/hooks/ und deklariert sein Quellmodul, Export, Handler, Trampolin-Slot, Aufrufkonvention und Signatur gemeinsam:
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 erkennt Deskriptoren mit festem Layout, die von der Hook-DLL exportiert werden. Es ist keine zentrale API-Liste oder ein launcherseitiges Ereignisschema erforderlich.
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
Der Controller erstellt einen auf Seitenstützdatei basierenden Abschnitt und bildet ihn in beide Prozesse ab. Hook-Threads veröffentlichen Ereignisse in einem Ring mit fester Kapazität und mehreren Produzenten, wobei sie slotspezifische Sequenznummern verwenden. Ein ungepatchter NtSetEvent-Bypass sendet ein zusammengefasstes Aufwachen für jede anstehende Charge, und der Controller entleert Ereignisse in Chargen. Produzenten warten nie auf den Leser; ein voller Ring verwirft und zählt das Ereignis. Puffervorschauen verwenden immer noch einen ungepatchten NtReadVirtualMemory-Bypass, sodass ungültige Zielzeiger fehlschlagen, ohne den Hook zum Absturz zu bringen.
cmake/ Abhängigkeitskonfiguration
docs/ Ereignisschema und Formatreferenz
scripts/ Validierungs- und Laufzeit-Smoke-Tests
src/apiscope/ CLI, Debugger, Mapper, Patcher und Renderer
src/apiscope-hooks/ Importfreie Hook-DLL und eigenständige Hooks
src/include/ Gemeinsame Verträge
tests/ Unit- und Laufzeittestziele
ntdll.dll!NtClose, wenn dieser Hook aktiv ist; ohne ihn behält ein geschlossenes Handle, das wiederverwendet wird, seinen vorherigen Pfad.DebugSetProcessKillOnExit(FALSE) das Ziel am Leben, aber Hooks und gemappte Instrumentierung bleiben resident, bis das Ziel beendet wird.Verwenden Sie ApiScope nur auf Systemen und Prozessen, die Sie autorisiert sind zu überprüfen. Siehe SECURITY.md, CONTRIBUTING.md und ROADMAP.md.
MIT. Drittanbieterkomponenten behalten ihre Lizenzen; siehe THIRD_PARTY_NOTICES.md.