
Um framework de hooking de API para interceptar e monitorar aplicações Windows.
ApiScope é uma ferramenta de pesquisa para Windows x64 que rastreia chamadas de API selecionadas em um processo novo ou em execução. Ela acompanha eventos de carregamento de DLL por meio da API de depuração do Windows e instala hooks antes de o processo depurado continuar a partir de cada evento.
Hooks incluídos:
ntdll.dll!NtCreateFile (registra o caminho de destino)ntdll.dll!NtOpenFile (registra o caminho de destino)ntdll.dll!NtReadFilentdll.dll!NtWriteFilentdll.dll!NtClosentdll.dll!NtOpenKey (registra o caminho da chave)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
O Zydis v4.1.1 é obtido em um commit fixado e vinculado somente ao apiscope.exe. O apiscope-hooks.dll injetado permanece livre 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]
Os nomes de hooks são sempre qualificados por módulo. A correspondência de módulos não diferencia maiúsculas de minúsculas; a correspondência de exports diferencia.
.\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
Os eventos de terminal permanecem legíveis enquanto --output opcionalmente grava texto ou JSONL em um arquivo. --quiet suprime o espelhamento de eventos no terminal. --color auto|always|never controla a cor do terminal (padrão auto: ativo em um TTY, desativado quando redirecionado ou sob NO_COLOR); a cor nunca aparece em arquivos --output. Em um TTY, o ApiScope também mostra um rodapé de status ao vivo (eventos, descartes, taxa e contagens por hook) que é redesenhado no lugar; controle-o com --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)
Os eventos JSONL contêm metadados genéricos e campos específicos do hook:
{"schema_version":1,"sequence":1,"module":"bcrypt.dll","api":"BCryptOpenAlgorithmProvider","hook":"bcrypt.dll!BCryptOpenAlgorithmProvider","fields":{"flags":0,"result":"STATUS_SUCCESS (0x00000000)"}}
Consulte SCHEMA.md para o envelope de eventos e as codificações de campos por tipo (ponteiros e status são exibidos como strings hexadecimais 0x).
Pressione Ctrl+C ou Ctrl+Break para restaurar os hooks ativos, liberar a instrumentação remota e desanexar. O alvo continua em execução. Na saída natural, o ApiScope imprime o status do alvo em decimal e hexadecimal, seguido por um resumo da sessão (eventos, descartes e contagens por hook) no stderr.
NtCreateFile, NtOpenFile e NtOpenKey resolvem o path de destino a partir de OBJECT_ATTRIBUTES e relatam o handle resultante. O ApiScope registra cada abertura bem-sucedida e anota operações posteriores no mesmo handle — leituras, gravações, acesso a valores de registro e o NtClose correspondente — com o path resolvido, para que a atividade seja legível sem rastrear handles manualmente. NtClose também remove o handle, e aberturas relativas são resolvidas por meio de um handle root_directory visto anteriormente.
[*] 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
O path em eventos de leitura e gravação é correlacionado a partir do handle, não observado na própria chamada.
Cada hook é um arquivo sob src/apiscope-hooks/hooks/ e declara seu módulo de origem, export, handler, slot de trampoline, convenção de chamada e assinatura juntos:
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 descobre descritores de layout fixo exportados pela DLL de hooks. Não é necessária uma lista central de APIs ou um esquema de eventos no lado do lançador.
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
endO controlador cria uma seção com suporte de arquivo de paginação e a mapeia em ambos os processos. Os threads de hook publicam em um anel multi-produtor de capacidade fixa usando números de sequência por slot. Um bypass não corrigido de NtSetEvent envia um único wakeup coalescido para cada lote pendente, e o controlador drena eventos em lotes. Os produtores nunca esperam pelo leitor; um anel cheio descarta e conta o evento. As pré-visualizações de buffer ainda usam um bypass não corrigido de NtReadVirtualMemory para que ponteiros de destino inválidos falhem sem derrubar o hook.
cmake/ Configuração de dependências
docs/ Referência de esquema e formato de eventos
scripts/ Testes de validação e fumaça em tempo de execução
src/apiscope/ CLI, depurador, mapper, aplicador de patches e renderizador
src/apiscope-hooks/ DLL de hooks livre de imports e hooks autônomos
src/include/ Contratos compartilhados
tests/ Alvos de teste de unidade e tempo de execução
ntdll.dll!NtClose quando esse hook está ativo; sem ele, um handle fechado que é reutilizado mantém seu caminho anterior.DebugSetProcessKillOnExit(FALSE) mantém o alvo vivo, mas os hooks e a instrumentação mapeada permanecem residentes até o alvo sair.Use o ApiScope somente em sistemas e processos que você está autorizado a inspecionar. Consulte SECURITY.md, CONTRIBUTING.md e ROADMAP.md.
MIT. Componentes de terceiros mantêm suas licenças; consulte THIRD_PARTY_NOTICES.md.