📘 Volatility3: Modern Windows Hibernation file analysis
In the Digital Forensics ecosystem, the field of memory forensics can help uncover artifacts that can’t be found anywhere else. That can include deleted files, network connections, running processes, rootkits, code injection, fileless malware and many more.
Microsoft introduced the hibernation feature in Windows 2000, allowing systems to be powered down while preserving their volatile state. This is achieved by saving RAM contents and processor context to a file called hiberfil.sys before shutting down inside the root folder of the filesystem drive. When the computer is turned on again, the system restores the volatile state from the saved file. Hibernation files are valuable for digital forensic professionals as they store temporary data from RAM to non-volatile storage, eliminating the requirement for specialized tools on the target device.
The Hibernation file structure has evolved in time. In this blog post, we will dive into the structure of the modern Windows hibernation file and propose a new translation layer for the volatility3 framework to create a raw memory image from a hibernation file.
The structure of a modern hibernation file
The structure of the hibernation file can be divided into two variations:
- From Windows XP to Windows 7 (old)
- From Windows 8 to Windows 11 (modern)
The following image represent the modern structure.
In this blog post we will focus on the modern hibernation structure1, though you’ll notice that the underlying principle remains the same which is to compress and store the physical memory pages of the hibernated system in specific data structures called “restoration sets”.
The file header
The hibernation files start with a header called PO_MEMORY_IMAGE. The structure of this header may differ slightly depending on the operating system version, but its definition is publicly available in the kernel's debugging symbols, which is very nice. The vergiliusproject2 gives us a nice description of this structure for each Windows version. Below is an example of the structure of the header.
The signature of the file for an exploitable hibernation file is ‘HIBR’. The ‘RSTR’ signature indicates that the system is resuming from the file, making it apparently useless when this is the case. Valuable forensics artifacts are available like the system time when it was put into the hibernation state.
Notice the “FirstBootRestorePage” and “FirstKernelRestorePage” attributes. Those values are storing the page number of the first restoration sets. Mutiplying those values by the size of a Windows x64 page (4096) we get the offset of the sections.
Windows currently divide the storage of the memory pages in 2 locations :
- The boot section.
- The kernel section.
You might notice the “SecureRestorePage” section, however this value seems to be always 0 and there is no publicly available explanation on what those pages are used for. It might be used in the future though to encrypt memory pages so only the host machine can decrypt them via its TPM?
To know how many pages are stored for each sections the information can be retrieved from specific attributes:
- "NumPagesForLoader": The number of pages in the "Boot" section
- "PerfInfo->KernelPagesProcessed": The number of pages in the "Kernel" section
Unfortunately for us, the attribute storing the location and number of pages for the Kernel section did change with the Windows OS updates. Below is a table tracking those changes.
Windows Versions | FirstKernelRestorePage offset | KernelPagesProcessed offset |
---|---|---|
Windows 8/8.1 | 0x68 | 0x1C8 |
Windows 10 2016 1507-1511 | 0x70 | 0x218 |
Windows 10 2016 1607 | 0x70 | 0x220 |
Windows 10 2016 1703 - Windows 11 23H2 | 0x70 | 0x230 |
The compression sets.
A restoration set contains multiple compression sets. Each compression set contains a header structure giving information about how many page descriptors it contains, the compression algorithm used and the size of the compressed concatenated pages of the set.
The compression algorithm used can be one the following:
- None: the pages are stored uncompressed
- Xpress: The pages are stored using the Microsoft Xpress LZ77 Algorithm 3
- HuffmanXpress: The pages are stored using the Microsoft Xpress LZ77+Huffman Algorithm 3 4
All the page descriptors are located after the header and are describing where the pages reside in the decompressed data.
Creating a volatility3 translation layer
To be able to read multiple memory image formats, the volatility3 framework is using what’s called “Translation Layers”. Each layer in volatility3 focuses on a particular aspect of memory forensics, such as parsing specific data structures, analyzing various kinds of artifacts, or understanding specific evidence formats. The layers can be seen as individual building blocks that can be stacked on top of each other in a specific order to build the right context before extracting data for analysis. As an example, a memory image with the "vmem" format will induce the stacking of the "VmwareLayer" to be able to read such file format and extract memory pages.
In our case, when the framework is requesting several bytes at a specific physical address, it needs to understand the structure of the hibernation file and how to extract the right pages. For this, we have to indicate to the framework for a given memory “segment”, the corresponding set of pages.
The Hibernation layer role is to provide the volatility framework with the following information :
- Check the file header to see if the layer needs to be stacked (check the signature to verify it is a hibernation file)
- Build a list of segments: A segment is here to translates a given physical address to an offset inside of the uncompressed data of the relevant compression set.
- Provide the decompression method for a given segment: Our layer needs to be able to decompress a given compression set and returns the number of bytes requested by the upper layer.
Implementation of the different compression algorithms
Windows is using two proprietary compression algorithms to compress the pages:
- Xpress: The Microsoft Xpress LZ77 Decompression Algorithm 3
- HuffmanXpress: Microsoft Xpress LZ77+Huffman Decompression Algorithm 3 4
Those two algorithms were implemented and added to the volatility3 framework codecs without the needs of using the Windows API via cstruct (making this layer OS independant). Those algorithms are well documented but python3 is not really the best programming language to perform bitwise operations. Thus the developpement of those two algorithm took time to make sure that the decompressed page using our implementation is giving the same result as the native API 5
Proof Of Concept
After implementing this new translation layer, it was time to test it against multiple hibernation files. Knowing that we need to do a lot of decompressions operations, the hibernation layer is meant to be combine with the “layerwriter” plugin to create a raw memory image and then analyze it with the windows plugins. Indeed, the decompression of a set takes time and most of the plugin need to scan the entire memory dump pages making a lot of decompression operations thus slowing the framework. Creating a raw memory dump first is the best approach here for performances. To this end, 2 new plugins were added to the volatility3 framework:
- windows.hibernation.Info : Display the basic information about the hibernation file and if it seems exploitable.
- windows.hibernation.Dump : Convert the hibernation file into a raw memory dump (based on the layerwriter plugin)
The generation of the raw memory file was performed on 8 hibernations files from different versions of Windows:
Version / Target | hiberfile size | Conversion time |
---|---|---|
Windows 8 x64 / Laptop | 8GB | ~30 minutes |
Windows 10 1507 x64 / Laptop | 8GB | ~20 minutes |
Windows 10 1607 x64 / Laptop | 8GB | ~20 minutes |
Windows 10 1809 x64 / KVM | 8GB | ~10 minutes |
Windows 10 1809 x64 / KVM | 4GB | ~5 minutes |
Windows 10 22H02 x64 / Laptop | 8GB | ~60 minutes |
Windows 10 22H02 x64 / Laptop | 16GB | ~100 minutes |
Windows 11 23H02 x64 / Laptop | 8GB | ~100 minutes |
Note : On Windows 1809 hiberfile from a KVM, it was found that pages were located only in the boot section and none in the kernel section. The compressed data were either using the Xpress LZ77 compression or were not compressed but the Huffman variant was not identified. On Windows 22H2, most of the pages are located inside the kernel section using both plain LZ77 and LZ77+Huffman compression. It is not clear how Microsoft decides which compression algorithm is used and if both sections are used to store the pages.
Usage : Conversion
Firstly, we check if the hibernation file seems exploitable :
~/work/DFIR/volatility3» ./vol.py -f hiberfil_Win10_1507.sys windows.hibernation.Info
Volatility 3 Framework 2.5.1
Progress: 100.00 PDB scanning finished
Variable Value
Signature b'HIBR'
PageSize 4096
Comment The hibernation file header signature is correct.
System Time 2023-11-03 20:32:25
FirstBootRestorePage 0x23
NumPagesForLoader 32199
Next, we can try to convert the hibernation file to a raw file:
~/work/DFIR/volatility3» ./vol.py -f hiberfil_Win10_1507.sys windows.hibernation.Dump -h
usage: volatility windows.hibernation.Dump [-h] --version VERSION
optional arguments:
-h, --help show this help message and exit
--version VERSION The Windows version of the hibernation file :
0=>[Windows 10 1703 to Windows 11 23H2]
1=>[Windows 8/8.1]
2=>[Windows 10 1507 to 1511]
3=>[Windows 10 1607]
~/work/DFIR/volatility3» ./vol.py -f hiberfil_Win10_1507.sys windows.hibernation.Dump --version 2
Status
Progress: 99.99 Writing layer memory_layer
The hibernation file was converted to memory_layer.raw
Example: pslist
~/work/DFIR/volatility3» ./vol.py -f memory_layer.raw windows.pslist
Volatility 3 Framework 2.5.1
Progress: 100.00 PDB scanning finished
PID PPID ImageFileName Offset(V) Threads Handles SessionId Wow64 CreateTime ExitTime File output
4 0 System 0xe5072e280300 182 - N/A False 2023-09-30 14:52:10.000000 N/A Disabled
136 4 Registry 0xe5072e2ee040 4 - N/A False 2023-09-30 14:52:09.000000 N/A Disabled
400 4 smss.exe 0xe507322aa0c0 3 - N/A False 2023-09-30 14:52:10.000000 N/A Disabled
532 484 csrss.exe 0xe50732242140 13 - 0 False 2023-09-30 14:52:11.000000 N/A Disabled
628 484 wininit.exe 0xe50734c6a080 5 - 0 False 2023-09-30 14:52:12.000000 N/A Disabled
636 620 csrss.exe 0xe50734c6f140 13 - 1 False 2023-09-30 14:52:12.000000 N/A Disabled
700 628 services.exe 0xe50734cf0100 17 - 0 False 2023-09-30 14:52:12.000000 N/A Disabled
708 628 lsass.exe 0xe50734d25080 9 - 0 False 2023-09-30 14:52:12.000000 N/A Disabled
840 700 svchost.exe 0xe50734d8e280 29 - 0 False 2023-09-30 14:52:12.000000 N/A Disabled
868 628 fontdrvhost.ex 0xe50734db9180 5 - 0 False 2023-09-30 14:52:12.000000 N/A Disabled
932 700 svchost.exe 0xe50734b25540 13 - 0 False 2023-09-30 14:52:12.000000 N/A Disabled
[TRUNCATED]
Example : netscan
~/work/DFIR/volatility3» ./vol.py -f memory_layer.raw windows.netscan
Volatility 3 Framework 2.5.1
Progress: 100.00 PDB scanning finished
Offset Proto LocalAddr LocalPort ForeignAddr ForeignPort State PID Owner Created
0xe507320e1b00 TCPv4 127.0.0.1 49743 127.0.0.1 49742 ESTABLISHED 6660 RiotClientServ 2023-09-30 14:52:33.000000
0xe50733c5ba60 TCPv4 192.168.122.74 49883 8.247.201.124 443 ESTABLISHED 6660 RiotClientServ 2023-09-30 14:52:38.000000
0xe50734ab2d00 TCPv4 0.0.0.0 135 0.0.0.0 0 LISTENING 932 svchost.exe 2023-09-30 14:52:12.000000
0xe50734ab2d00 TCPv6 :: 135 :: 0 LISTENING 932 svchost.exe 2023-09-30 14:52:12.000000
0xe50734ab2e50 TCPv4 0.0.0.0 49664 0.0.0.0 0 LISTENING 628 wininit.exe 2023-09-30 14:52:12.000000
0xe50734ab2e50 TCPv6 :: 49664 :: 0 LISTENING 628 wininit.exe 2023-09-30 14:52:12.000000
0xe50734ab34e0 TCPv4 0.0.0.0 49664 0.0.0.0 0 LISTENING 628 wininit.exe 2023-09-30 14:52:12.000000
0xe50734ab38d0 TCPv4 0.0.0.0 135 0.0.0.0 0 LISTENING 932 svchost.exe 2023-09-30 14:52:12.000000
0xe50734d624e0 TCPv4 192.168.122.74 49687 20.223.46.67 443 ESTABLISHED 2552 MsMpEng.exe 2023-09-30 14:52:17.000000
0xe5073540a990 TCPv4 192.168.122.74 139 0.0.0.0 0 LISTENING 4 System 2023-09-30 14:52:12.000000
0xe5073540b410 TCPv4 0.0.0.0 49665 0.0.0.0 0 LISTENING 952 svchost.exe 2023-09-30 14:52:12.000000
0xe5073540b800 TCPv4 0.0.0.0 49666 0.0.0.0 0 LISTENING 1416 svchost.exe 2023-09-30 14:52:12.000000
0xe5073540c130 TCPv4 0.0.0.0 49666 0.0.0.0 0 LISTENING 1416 svchost.exe 2023-09-30 14:52:12.000000
0xe5073540c130 TCPv6 :: 49666 :: 0 LISTENING 1416 svchost.exe 2023-09-30 14:52:12.000000
0xe5073540c3d0 TCPv4 0.0.0.0 49665 0.0.0.0 0 LISTENING 952 svchost.exe 2023-09-30 14:52:12.000000
0xe5073540c3d0 TCPv6 :: 49665 :: 0 LISTENING 952 svchost.exe 2023-09-30 14:52:12.000000
[TRUNCATED]
Conclusion
For all tested files, the framework was able to extract and decompress all of the pages in each restoration sets. The hibernation layer currently only support Windows 8 x64 to Windows 11 23H2 x64 hibernation files. Windows server hibernation analysis was not tested yet but it will come in time. Hopping the forensics community will also test this new addition and give some feedback to improve this layer. You can find the feature here: https://github.com/forensicxlab/volatility3/tree/feature/hibernation-layer.
Hoping this article will bring back the interests of the Digital Forensics community to hibernation files which are very valuable when available.
Special thanks to Chad Tilbury @chadtilbury who gave me the motivation to dive into this project and the team of researchers behind the Hibernation file structure analysis (Joe T. Sylve, Vico Marziale, Golden G. Richard III).
Do not hesitate to reach me at felix.guyard@forensicxlab.com to enhance this article or to comment on the integration to the volatility framework.
Footnotes
-
https://www.vergiliusproject.com/kernels/x64/Windows%2011/Insider%20Preview%20(Jun%202021)/PO_MEMORY_IMAGE ↩
-
https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-xca/a8b7cb0a-92a6-4187-a23b-5e14273b96f8 ↩ ↩2 ↩3 ↩4
-
https://raw.githubusercontent.com/Velocidex/go-prefetch/master/lzxpress.go ↩ ↩2
-
https://learn.microsoft.com/en-us/windows/win32/cmpapi/using-the-compression-api ↩