A proof-of-concept to reconstruct the GUI of a Xen VM running Windows 7.
This repository is work in progress. It currently only works with 32-bit Windows 7 SP1 systems.
vmi-reconstruct-gui
is a reimplementation of Brendan Dolan-Gavitt’s Volatility screenshot-plugin using libvmi to reconstruct the GUI of a Windows 7-box running as a Xen-guest. It was developed as a draft and blueprint to incorporate GUI forensics into the black-box binary analysis system DRAKVUF in the future.
This tools reconstructs each desktop, that could be found in the memory of a running guest, and shows it one after another as a wireframe. To advance the presented desktop, press any key. Note that, Windows houses a lot of empty desktops, so many of those wireframes will be blank, if there are no visible desktops.
vmi-reconstruct-gui
depends on the following libraries:
- libvmi
- libx11-dev
- glib
Please make sure to install those libaries and then run make
from within this directory.
To use this tool determine the Xen domain identifier of the Win 7 VM in question and run the compiled binary as illustrated below:
# Set name of the VM specified in its config file
VM_NAME="your-vm-name"
# Retrieve dom id
DOM_ID=$(sudo xl list | grep $VM_NAME | awk '{print $2}')
# Set path to separately created intermediate symbol file for ntoskrn
KRN_IST_FILE="/usr/local/share/vmi/windows7-sp1.json"
# Set path to separately created intermediate symbol file for ntoskrn
W32K_IST_FILE="/usr/local/share/vmi/windows7-sp1-win32k.json"
# Reconstruct the gui, using short opts
sudo ./vmi-reconstruct-gui -d $DOM_ID -k $KRN_IST_FILE -w $W32K_IST_FILE
Per default only the so-called desktop named Default of WinSta0 of session #1 is shown.
To show all desktops – regardless, if they are empty or not – use the CLI-flag --all
.
A full example with the more explicit CLI-options (long opts) looks like this:
sudo ./vmi-reconstruct-gui --all --domid $DOM_ID \
--kernel $KRN_IST_FILE --win32k $W32K_IST_FILE
For information on the creation of the referenced intermediate symbol table-files, see section “Excursus: Derivation of IST-files” below.
The Windows GUI subsystem is structured by the concepts of sessions, window stations, desktops and windows. A session is the logon-environment of a user. Each session has multiple window stations – interactive ones for handling user input and non-interactive ones for services. Each window station has a so-called atom table, which is basically a hashtable of strings and a notable attribute in the context of GUI forensics. The atom entries are shared by processes and – among other things – used to track classes of GUI objects. Most important is, that each window station contains a list of associated desktops. Those house all GUI objects, such as windows, buttons, menus and the like, as the name implies [1].
To perform a reconstruction of the GUI presented to the user, the interactive window stations and all of their non-empty desktops have to be found.
One of approach of finding all windows stations is to look at each thread of each process and determine, if it is a GUI-thread. Windows holds its processes in a doubly-link list of _EPROCESS
-structs, which contain an ActiveProcessList-field. The kernel symbol PsActiveProcessHead
points to the head of this list of active processes. Each process has one or more threads. If the thread environment block contains a pointer to a Win32Threadinfo-struct (and an tagDESKTOPINFO
-struct) it is GUI-thread. If this is the case, the housing window station can be retrieved by looking at the Win32Threadinfo-struct more closely, which contains a field named pwinsta
– a pointer to the window station. The address to the windowstation can then be collected in a set. Afterwards all windows stations can be traversed and all all desktops and then all windows can be retrieved [2]. For a detailed description of the procedure look at the following section.
The following procedure is applied to reconstruct the GUI windows:
To find all Window Stations (WinStas), traverse the linked list of _EPROCESS
-structs and perform the following steps for each process:
- Find ThreadListHead
- Traverse linked list of
_ETHREAD
-structs and check, if current_ETHREAD
is a GUI thread. This is done by using theTEB
-struct in_KTHREAD
, which houses a Win32ThreadInfo-struct, if it is a GUI thread- If current
_ETHREAD
is a GUI-thread, retrieve pointerpwinsta
totagWINDOWSTATION
-struct - If current
_ETHREAD
is not a GUI-thread, continue
- If current
To parse each tagWINDOWSTATION
-struct, do
- Retrieve session ID
- Retrieve offset to
_RTL_ATOM_TABLE
and parse it (not detailed here) - Retrieve flags (specifying, if interactive or not)
- Traverse all
tagDESKTOP
-structs associated with the winsta- Find
rpdesklist
-pointer to the head of the linked list of associated desktops - Traverse linked list of desktops by using their
rpnext
-pointers
- Find
To get a list of all windows associated with a desktop in their Z-order (bottom to top), traverse each interactive window station and perform for each associated desktop the following steps:
- Find the root window
- Find
_DESKTOPINFO
- Find
spwnd
(struct pointer to the firsttagWND
-struct)
- Find
- Allocate an empty list of windows
- Form a list of top windows by following
pNextWindow
of thetagWND
-struct until NULL or seen- Check the visibility of each window (
WS_VISIBLE
-flag has to be set)- If not visible, continue
- If visible, add to list of visible windows
- Check the visibility of each window (
- Traverse list of visbile windows in reverse order
- For each window in list, get child window and recurse to 3.3
After performing this last step, the depth-ordered list of the addresses to all visible tagWND
-structs of a desktop is available, this list can be traverse and each tagWND-struct can be parsed.
Note, that Brendan Dolan-Gavitt followed a slightly different approach and retrieved the top window directly by utilizing the following fields: Win32Threadinfo->pDeskInfo->spwnd
Libvmi can read its intermediate symbol files in two variants – Volatility’s or Rekall’s format. To generate an IST-file in any of the two formats, you need to know the GUID and the age of the kernel-module in question – here win32k.sys
. Those uniquely identify a particular version of a PDB-file.
You can retrieve those two values, by utilizing a utility drakpdb.py from CERT.pl’s Drakvuf Sandbox.
# Get drakpdb.py
wget https://raw.githubusercontent.com/CERT-Polska/drakvuf-sandbox/master/drakrun/drakrun/drakpdb.py
# Install dependencies
pip3 install pdbparse tqdm
# Get the GUID and age from the file in question
python3 drakpdb.py pdb_guid --file ~/share/win32k.sys
Since the GUID is now known, an IST-file can be generated by retrieving the PDB-file matching the given GUID from Microsoft’s symbol servers and converting it to the JSON-format, which is used by Volatility:
python3 ./volatility3/framework/symbols/windows/pdbconv.py \
--guid 6a1a499eed2d42d29e40866f0c374d492 \
-p win32k.pdb -o windows7-sp1-win32k.json
To generate a IST-file in Rekalls’ format, you can use the following commands:
# Use the GUID age to retrieve the PDB-file from Microsoft's symbol servers
python3 drakpdb.py fetch_pdb --pdb_name win32k.pdb --guid_age 6a1a499eed2d42d29e40866f0c374d492
# Generate an intermediate symbol file from the retrieved PDB-file
python3 drakpdb.py parse_pdb --pdb_name win32k.pdb > win32k.json
[1] Cf. Ligh, M. H., Case, A., Levy, J., & Walters, A. (2014). The art of memory forensics. John Wiley & Sons. p. 408 f.
[2] This information is based Brendan Dolan-Gavitt’s work on GDI utilities. https://www.cc.gatech.edu/~brendan/volatility/
- https://www.cc.gatech.edu/~brendan/volatility/
- https://www.tophertimzen.com/resources/cs407/slides/week07_01-GUI.html#slide1
- https://www.tophertimzen.com/resources/cs407/slides/week07_02-GUI2.html#slide1
- https://libvmi.com/api/
- https://www.cc.gatech.edu/~brendan/volatility/
- https://resources.infosecinstitute.com/topic/windows-gui-forensics-session-objects-window-stations-and-desktop/
- https://reactos.org/wiki/Techwiki:Win32k/DESKTOP
- https://www.microsoftpressstore.com/articles/article.aspx?p=2233328&seqNum=4
- https://titanwolf.org/Network/Articles/Article?AID=0de2af5a-cff3-49f8-b7d6-c2f9369ff313#gsc.tab=0
- https://code.google.com/archive/p/volatility/issues/131