
Un framework de hooking d'API pour intercepter et surveiller les applications Windows
ApiScope est un outil de recherche Windows x64 pour tracer des appels API sélectionnés dans un nouveau processus ou un processus en cours. Il suit les événements de chargement de DLL via l'API du débogueur Windows et installe des hooks avant que le débogué ne continue après chaque événement.
Hooks inclus :
ntdll.dll!NtCreateFile (enregistre le chemin cible)ntdll.dll!NtOpenFile (enregistre le chemin cible)ntdll.dll!NtReadFilentdll.dll!NtWriteFilentdll.dll!NtClosentdll.dll!NtOpenKey (enregistre le chemin de la clé)ntdll.dll!NtSetValueKeyntdll.dll!NtQueryValueKeybcrypt.dll!BCryptOpenAlgorithmProviderPrérequis :
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 est récupéré à un commit épinglé et lié uniquement dans
apiscope.exe. Le fichier apiscope-hooks.dll injecté reste sans importation.
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]
Les noms de hooks sont toujours qualifiés par le module. La correspondance de module est insensible à la casse ; la correspondance d'export est sensible à la casse.
.\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
Les événements du terminal restent lisibles tandis que --output peut copier le texte ou le JSONL vers un fichier. --quiet supprime le miroir des événements dans le terminal. --color auto|always|never contrôle la couleur du terminal (par défaut auto : activé pour un TTY, désactivé en cas de redirection ou sous NO_COLOR) ; la couleur n'apparaît jamais dans les fichiers --output. Sur un TTY, ApiScope affiche également un pied de page en direct (événements, pertes, taux et compteurs par hook) qui se redessine en place ; contrôlez-le avec --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)
Les événements JSONL contiennent des métadonnées génériques et des champs locaux au hook :
{"schema_version":1,"sequence":1,"module":"bcrypt.dll","api":"BCryptOpenAlgorithmProvider","hook":"bcrypt.dll!BCryptOpenAlgorithmProvider","fields":{"flags":0,"result":"STATUS_SUCCESS (0x00000000)"}}
Voir SCHEMA.md pour l'enveloppe des événements et les encodages de champs par type (les pointeurs et les états s'affichent sous forme de chaînes hexadécimales 0x).
Appuyez sur Ctrl+C ou Ctrl+Break pour restaurer les hooks actifs, libérer l'instrumentation distante et se détacher. La cible continue de s'exécuter. En cas de sortie naturelle, ApiScope affiche l'état de la cible en décimal et en hexadécimal, suivi d'un résumé de session (événements, pertes et compteurs par hook) sur stderr.
NtCreateFile, NtOpenFile et NtOpenKey résolvent le path cible à partir de OBJECT_ATTRIBUTES et signalent le handle résultant. ApiScope enregistre chaque ouverture réussie et annote les opérations ultérieures sur le même handle — lectures, écritures, accès aux valeurs de registre et le NtClose correspondant — avec le path résolu, afin que l'activité soit lisible sans suivre manuellement les handles. NtClose supprime également le handle, et les ouvertures relatives sont résolues via un handle root_directory précédemment vu.
[*] 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
Le path sur les événements de lecture et d'écriture est corrélé à partir du handle, et non observé sur l'appel lui-même.
Chaque hook est un fichier sous src/apiscope-hooks/hooks/ et déclare ensemble son module source, son export, son gestionnaire, son emplacement de trampoline, sa convention d'appel et sa signature :
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 découvre les descripteurs à mise en page fixe exportés par la DLL de hook. Aucune liste d'API centrale ni schéma d'événement côté lanceur n'est requis.
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