
Un framework de hooking de API para interceptar y monitorear aplicaciones de Windows.
ApiScope es una herramienta de investigación para Windows x64 que permite rastrear llamadas API seleccionadas en un proceso nuevo o en ejecución. Sigue los eventos de carga de DLL a través de la API del depurador de Windows e instala hooks antes de que el proceso depurado continúe desde cada evento.
Hooks incluidos:
ntdll.dll!NtCreateFile (registra la ruta de destino)ntdll.dll!NtOpenFile (registra la ruta de destino)ntdll.dll!NtReadFilentdll.dll!NtWriteFilentdll.dll!NtClosentdll.dll!NtOpenKey (registra la ruta de la clave)ntdll.dll!NtSetValueKeyntdll.dll!NtQueryValueKeybcrypt.dll!BCryptOpenAlgorithmProviderRequisitos:
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 se descarga en un commit fijado y se enlaza únicamente en
apiscope.exe. El apiscope-hooks.dll inyectado permanece libre de imports.
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]
Los nombres de hooks siempre están calificados por módulo. La coincidencia de módulos no distingue entre mayúsculas y minúsculas; la coincidencia de exports sí distingue entre mayúsculas y minúsculas.
.\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
Los eventos de terminal permanecen legibles mientras que --output opcionalmente
duplica texto o JSONL a un archivo. --quiet suprime el espejo de eventos en la
terminal. --color auto|always|never controla el color de la terminal (por defecto
auto: activo en una TTY, desactivado cuando se redirige o bajo NO_COLOR); el
color nunca aparece en los archivos de --output. En una TTY, ApiScope también
muestra un pie de estado en vivo (eventos, descartes, tasa y contadores por hook)
que se redibuja en su lugar; contrólalo 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)
Los eventos JSONL contienen metadatos genéricos y campos locales del hook:
{"schema_version":1,"sequence":1,"module":"bcrypt.dll","api":"BCryptOpenAlgorithmProvider","hook":"bcrypt.dll!BCryptOpenAlgorithmProvider","fields":{"flags":0,"result":"STATUS_SUCCESS (0x00000000)"}}
Consulta SCHEMA.md para conocer la envoltura del evento y las
codificaciones de campos por tipo (los punteros y estados se muestran como
cadenas hexadecimales 0x).
Pulsa Ctrl+C o Ctrl+Break para restaurar los hooks activos, liberar la instrumentación remota y desadjuntarte. El objetivo continúa ejecutándose. En una salida natural, ApiScope imprime el estado del objetivo en decimal y hexadecimal, seguido de un resumen de sesión (eventos, descartes y contadores por hook) en stderr.
NtCreateFile, NtOpenFile y NtOpenKey resuelven la path objetivo a partir de
OBJECT_ATTRIBUTES e informan del handle resultante. ApiScope registra cada
apertura exitosa y anota las operaciones posteriores sobre el mismo handle —
lecturas, escrituras, acceso a valores de registro y el NtClose correspondiente —
con la path resuelta, de modo que la actividad sea legible sin tener que rastrear
handles manualmente. NtClose también expulsa el handle, y las aperturas relativas
se resuelven a través de un handle root_directory visto previamente.
[*] 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
La path en los eventos de lectura y escritura se correlaciona a partir del
handle, no se observa en la propia llamada.
Cada hook es un archivo dentro de src/apiscope-hooks/hooks/ y declara
conjuntamente su módulo de origen, export, handler, ranura de trampolín, convención
de llamada y 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 descubre los descriptores de diseño fijo exportados por
la DLL de hooks. No se requiere ninguna lista central de APIs ni un esquema de
eventos en el lado del lanzador.
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
El controlador crea una sección respaldada por el archivo de paginación del sistema
y la mapea en ambos procesos. Los hilos de hooks publican en un anillo multiproductor
de capacidad fija mediante números de secuencia por ranura. Un bypass sin parchear de
NtSetEvent envía una única activación agrupada para cada lote pendiente, y el
controlador drena los eventos por lotes. Los productores nunca esperan al lector; un
anillo lleno descarta y cuenta el evento. Las vistas previas de búfer siguen
utilizando un bypass sin parchear de NtReadVirtualMemory, de modo que los punteros
inválidos del objetivo fallan sin bloquear el 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 cuando ese hook está activo; sin él, un handle cerrado que se reutiliza conserva su ruta anterior.DebugSetProcessKillOnExit(FALSE) mantiene vivo al objetivo, pero los hooks y la instrumentación mapeada permanecen residentes hasta que el objetivo sale.Usa ApiScope solo en sistemas y procesos que estés autorizado a inspeccionar. Consulta SECURITY.md, CONTRIBUTING.md y ROADMAP.md.
MIT. Los componentes de terceros conservan sus licencias; consulta THIRD_PARTY_NOTICES.md.