Clairvoyance (/klɛərˈvɔɪəns/; from French clair meaning clear and voyance meaning vision) from Wikipedia.
clairvoyance creates a colorful visualization of the page protection of an entire 64-bit process address space (user and kernel) running on a Windows 64-bit kernel.
To transform the 1 dimension space, that is the address space, into a 2 dimensions visualization, the hilbert space-filling curve is used. Each colored pixel on the above picture represents the page protection (UserRead, UserReadWrite, etc.) of a 4KB page in virtual memory.
The address space is directly calculated by manually parsing the four-level page tables hierarchy associated with a process from a kernel crash-dump that has been generated using WindDbg.
Finally, the program program outputs a file with the metadata required to have it displayed on a two dimensional canvas as well as being able to calculate the virtual address corresponding to a specific highlighted pixel.
Compiled binaries are available in the releases section. An online viewer is also hosted at 0vercl0k.github.io/clairvoyance.
Shouts out to:
- Alexandru Radocea and Georg Wicherski for the inspiration (see their BlackHat USA 2013 research: Visualizing Page Tables for Exploitation),
- The Hacker's delight second edition's chapter 16 Hilbert's curve for providing the algorithms used.
To generate the kernel crash dump it is recommended to use WinDbg, KDNet with the .dump /f command.
Once the dump has been acquired you can pass its path to clairvoyance as well as the physical address of the page directory you are interested in:
./clairvoyance <dump path> [<page dir pa>]
This generates a file with the clairvoyance extension that you then can visualize in your browser at 0vercl0k.github.io/clairvoyance or by checking out the gh-pages branch which is where the viewer is hosted at.
The CI builds clairvoyance on Linux using clang++-11 and on Windows using Microsoft's Visual studio 2019.
To build it yourself you can use the scripts in 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
The below are things I've noticed on a kernel crash-dump generated from an Hyper-V VM of Windows:
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
Windows doesn't seem to be using huge pages (1GB) or at least I have not seen one being used in any of the dumps I collected.
Large pages are used in abundance to map some kernel executables like the Windows kernel nt for example:
kd> ? nt
Evaluate expression: -8773703827456 = fffff805`36800000
VA:0xfffff80536800000, PA:0x2400000 (KernelReadWriteExec, Large, PML4E:0xd5745f80, PDPTE:0x42080a0, PDE:0x4209da0, PTE:0x0)
There are also a bunch of kernel read, write, executable pages that are not large pages, which was somewhat a surprise. I was aware that the kernel / hal could be mapped using large pages and that those were krwx. The reason for that is that 2MB is so large that it spans both executable and data sections; meaning the page has to be writeable and executable.
The only public mention of this I could find is in this blogpost (thx `Ivan):
I contacted Microsoft which claimed that this is intended since “in some cases the kernel is mapped with large pages” and that this can be prevented by enabling virtualization based protection (VBS).
A bunch of large kernel memory sections are mapped against the same physical page (filled with zero):
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, ...)
Here is smaller one (the region is not completely contiguous, there are a few holes):
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, ...)
This is just a section showing off some of the cool patterns you can see in some regions of an address space.
Page heap allocations and their guard pages are pretty cool looking and easy to spot:
Kernel stacks also have a nice recognizable shape because of their size and guard pages:
The system cache region in the kernel seems to be looking like a nebula in the dumps I have seen:
Axel '0vercl0k' Souchet