
Visualize o espaço de endereço virtual de um processo do Windows em uma curva de Hilbert.
Clairvoyance (/klɛərˈvɔɪəns/; do francês clair que significa claro e voyance que significa visão) da Wikipedia.
O clairvoyance cria uma visualização colorida da proteção de páginas de todo o espaço de endereçamento de um processo de 64 bits (usuário e kernel) em execução em um kernel Windows de 64 bits.
Para transformar o espaço de 1 dimensão, que é o espaço de endereçamento, em uma visualização de 2 dimensões, é usada a curva de preenchimento de espaço de Hilbert. Cada pixel colorido na imagem acima representa a proteção de página (UserRead, UserReadWrite, etc.) de uma página de 4KB na memória virtual.
O espaço de endereçamento é calculado diretamente pela análise manual da hierarquia de tabelas de páginas de quatro níveis associada a um processo a partir de um crash-dump de kernel gerado usando o WindDbg.
Por fim, o programa gera um arquivo com os metadados necessários para exibi-lo em uma tela bidimensional, além de permitir calcular o endereço virtual correspondente a um pixel destacado específico.
Binários compilados estão disponíveis na seção releases. Um visualizador online também está hospedado em 0vercl0k.github.io/clairvoyance.
Agradecimentos a:
Para gerar o crash dump do kernel, é recomendado usar o WinDbg, o KDNet com o comando .dump /f.
Depois que o dump for adquirido, você pode passar o caminho dele para o clairvoyance, bem como o endereço físico do diretório de páginas em que está interessado:
./clairvoyance <dump path> [<page dir pa>]
Isso gera um arquivo com a extensão clairvoyance que você pode então visualizar em seu navegador em 0vercl0k.github.io/clairvoyance ou fazendo checkout da branch gh-pages, que é onde o visualizador está hospedado.
O CI compila o clairvoyance no Linux usando o clang++-11 e no Windows usando o Visual studio 2019 da Microsoft.
Para compilar você mesmo, pode usar os scripts em build/:
(base) clairvoyance\build>build-msvc.bat
(base) clairvoyance\build>cmake ..
-- Selecting Windows SDK version 10.0.19041.0 to target Windows 10.0.19042.
-- Configuring done
-- Generating done
-- Build files have been written to: clairvoyance/build
(base) clairvoyance\build>cmake --build . --config RelWithDebInfo
Microsoft (R) Build Engine version 16.8.2+25e4d540b for .NET Framework
Copyright (C) Microsoft Corporation. All rights reserved.
clairvoyance.vcxproj -> clairvoyance\build\RelWithDebInfo\clairvoyance.exe
Building Custom Rule clairvoyance/CMakeLists.txt
Abaixo estão coisas que observei em um crash dump de kernel gerado a partir de uma VM Windows no Hyper-V:
kd> vertarget
Windows 10 Kernel Version 18362 UP Free x64
Product: WinNt, suite: TerminalServer SingleUserTS
Edition build lab: 18362.1.amd64fre.19h1_release.190318-1202
Machine Name:
Kernel base = 0xfffff805`36800000 PsLoadedModuleList = 0xfffff805`36c432f0
Debug session time: Sat Jul 25 10:00:19.637 2020 (UTC - 8:00)
System Uptime: 0 days 0:18:53.609
O Windows não parece usar páginas grandes (1GB) ou, pelo menos, não vi nenhuma sendo usada em nenhum dos dumps que coletei.
Páginas grandes são usadas em abundância para mapear alguns executáveis do kernel, como o kernel do Windows nt, por exemplo:
kd> ? nt
Evaluate expression: -8773703827456 = fffff805`36800000
VA:0xfffff80536800000, PA:0x2400000 (KernelReadWriteExec, Large, PML4E:0xd5745f80, PDPTE:0x42080a0, PDE:0x4209da0, PTE:0x0)
Também há um monte de páginas de kernel de leitura, gravação e execução que não são páginas grandes, o que foi uma certa surpresa. Eu sabia que o kernel / hal poderia ser mapeado usando páginas grandes e que essas páginas seriam krwx. A razão para isso é que 2MB é tão grande que abrange tanto seções executáveis quanto de dados; ou seja, a página precisa ser gravável e executável.
A única menção pública que consegui encontrar sobre isso está neste blogpost (obrigado `Ivan):
Entrei em contato com a Microsoft, que afirmou que isso é intencional, já que "em alguns casos o kernel é mapeado com páginas grandes" e que isso pode ser evitado habilitando a proteção baseada em virtualização (VBS).
Um monte de grandes seções de memória do kernel são mapeadas contra a mesma página física (preenchida com zeros):
VA:0xffffc27ef4401000, PA:0x4200000 (KernelRead, Normal, ...)
VA:0xffffc27ef4402000, PA:0x4200000 (KernelRead, Normal, ...)
VA:0xffffc27ef4403000, PA:0x4200000 (KernelRead, Normal, ...)
...
VA:0xffffc27ef63fb000, PA:0x4200000 (KernelRead, Normal, ...)
VA:0xffffc27ef63fc000, PA:0x4200000 (KernelRead, Normal, ...)
VA:0xffffc27ef63fd000, PA:0x4200000 (KernelRead, Normal, ...)
VA:0xffffc27ef63fe000, PA:0x4200000 (KernelRead, Normal, ...)
VA:0xffffc27ef63ff000, PA:0x4200000 (KernelRead, Normal, ...)
Aqui está uma menor (a região não é completamente contígua, há alguns buracos):
VA:0xffffc27ed2201000, PA:0x4300000 (KernelRead, Normal, ...)
VA:0xffffc27ed2202000, PA:0x4300000 (KernelRead, Normal, ...)
VA:0xffffc27ed2203000, PA:0x4300000 (KernelRead, Normal, ...)
...
VA:0xffffc27ed25fc000, PA:0x4300000 (KernelRead, Normal, ...)
VA:0xffffc27ed25fd000, PA:0x4300000 (KernelRead, Normal, ...)
VA:0xffffc27ed25fe000, PA:0x4300000 (KernelRead, Normal, ...)
VA:0xffffc27ed25ff000, PA:0x4300000 (KernelRead, Normal, ...)
Esta é apenas uma seção mostrando alguns dos padrões interessantes que você pode ver em algumas regiões de um espaço de endereçamento.
As alocações de page heap e suas páginas de guarda são bem legais de ver e fáceis de identificar:
As pilhas do kernel também têm uma forma reconhecível e agradável devido ao seu tamanho e às páginas de guarda:
A região do cache do sistema no kernel parece uma nebulosa nos dumps que vi:
Axel '0vercl0k' Souchet