
Un framework di API hooking per intercettare e monitorare le applicazioni Windows.
ApiScope è uno strumento di ricerca per Windows x64 che traccia chiamate API selezionate in un processo nuovo o già in esecuzione. Segue gli eventi di caricamento delle DLL tramite l'API del debugger di Windows e installa hook prima che il processo sottoposto a debug continui da ogni evento.
Hook inclusi:
ntdll.dll!NtCreateFile (registra il percorso di destinazione)ntdll.dll!NtOpenFile (registra il percorso di destinazione)ntdll.dll!NtReadFilentdll.dll!NtWriteFilentdll.dll!NtClosentdll.dll!NtOpenKey (registra il percorso della chiave)ntdll.dll!NtSetValueKeyntdll.dll!NtQueryValueKeybcrypt.dll!BCryptOpenAlgorithmProviderRequisiti:
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 viene recuperato a un commit fisso e collegato solo in apiscope.exe. La DLL iniettata apiscope-hooks.dll rimane priva di import.
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]
I nomi degli hook sono sempre qualificati dal modulo. La corrispondenza dei moduli non fa distinzione tra maiuscole e minuscole; la corrispondenza degli export fa distinzione tra maiuscole e minuscole.
.\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
Gli eventi sul terminale rimangono leggibili mentre --output facoltativamente invia una copia del testo o JSONL su un file. --quiet sopprime il mirror degli eventi sul terminale. --color auto|always|never controlla il colore del terminale (default auto: attivo su una TTY, disattivato quando si usa una pipe o sotto NO_COLOR); il colore non compare mai nei file --output. Su una TTY, ApiScope mostra anche una barra di stato live (eventi, drop, velocità e conteggi per hook) che si ridisegna in posizione; controllala con --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)
Gli eventi JSONL contengono metadati generici e campi specifici dell'hook:
{"schema_version":1,"sequence":1,"module":"bcrypt.dll","api":"BCryptOpenAlgorithmProvider","hook":"bcrypt.dll!BCryptOpenAlgorithmProvider","fields":{"flags":0,"result":"STATUS_SUCCESS (0x00000000)"}}
Vedi SCHEMA.md per l'involucro dell'evento e le codifiche dei campi per tipo (i puntatori e gli stati vengono mostrati come stringhe esadecimali 0x).
Premi Ctrl+C o Ctrl+Break per ripristinare gli hook attivi, rilasciare la strumentazione remota e scollegarti. Il processo target continua a essere eseguito. All'uscita naturale, ApiScope stampa lo stato del target in decimale ed esadecimale, seguito da un riepilogo della sessione (eventi, drop e conteggi per hook) su stderr.
NtCreateFile, NtOpenFile e NtOpenKey risolvono il path target da OBJECT_ATTRIBUTES e riportano l'handle risultante. ApiScope registra ogni apertura riuscita e annota le operazioni successive sullo stesso handle — letture, scritture, accesso ai valori del registro e la corrispondente NtClose — con il path risolto, così l'attività è leggibile senza dover tracciare manualmente gli handle. NtClose rimuove anche l'handle, e le aperture relative vengono risolte tramite un handle root_directory già visto in precedenza.
[*] 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
Il path negli eventi di lettura e scrittura viene correlato dall'handle, non osservato direttamente nella chiamata.
Ogni hook è un file in src/apiscope-hooks/hooks/ e dichiara insieme modulo sorgente, export, handler, slot del trampoline, convenzione di chiamata e firma:
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 individua i descrittori a layout fisso esportati dalla DLL degli hook. Non è richiesto un elenco API centrale né uno schema eventi lato launcher.
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
Il controller crea una sezione basata sul file di paging e la mappa in entrambi i processi. I thread degli hook pubblicano su un anello multi-produttore a capacità fissa usando numeri di sequenza per slot. Un bypass non patchato di NtSetEvent invia un risveglio aggregato per ogni batch in attesa, e il controller scarica gli eventi in batch. I produttori non aspettano mai il lettore; un anello pieno scarta e conta l'evento. Le anteprime dei buffer usano ancora un bypass non patchato di NtReadVirtualMemory, così puntatori target non validi falliscono senza mandare in crash l'hook.
cmake/ Dependency configuration
docs/ Event schema and format reference
scripts/ Validation and runtime smoke tests
src/apiscope/ CLI, debugger, mapper, patcher, and renderer
src/apiscope-hooks/ Import-free hook DLL and standalone hooks
src/include/ Shared contracts
tests/ Unit and runtime test targets
ntdll.dll!NtClose quando quell'hook è attivo; senza di esso, un handle chiuso e riutilizzato mantiene il percorso precedente.DebugSetProcessKillOnExit(FALSE) mantiene vivo il target, ma gli hook e la strumentazione mappata restano residenti finché il target non esce.Usa ApiScope solo su sistemi e processi che sei autorizzato a ispezionare. Vedi SECURITY.md, CONTRIBUTING.md e ROADMAP.md.
MIT. I componenti di terze parti mantengono le proprie licenze; vedi THIRD_PARTY_NOTICES.md.