Repository navigation
hibernation: Windows 11 24H2+ support (build-dependent counter offset) and a decoder size fix - #1
Merged
forensicxlab merged 2 commits intoOct 16, 2026
Conversation
…uffman set size Two independent problems of the hibernation layer on Windows 11 Fast Startup files, validated on 21H2, 22H2, 23H2, 24H2, 25H2 and 26H1 lab VMs (see contrib/hibernation_checks/campaign_2026-10). 1. KernelPagesProcessed is not at 0x230 on build 26100 and later, and its offset depends on the build: 0x238 in 26100.1742, 26100.6584 and 28000.1340, 0x250 in a 26100.9445 file. With --version 0 the kernel section is skipped or mis-walked. New --version 4 (Windows 11 24H2 and later): the counter is read at the known offsets and accepted only if the chain of compression sets of the kernel section adds up to exactly that number of pages; a file where no candidate matches is refused with an explicit message. On those builds the set header carries the size in bits 8-28 (bit 29 is not part of it) and bit 30 selects LZ77+Huffman. 2. _uncompress asked the LZ77+Huffman decoder for 65536 bytes for every set. A set of fewer than 16 pages (present at the end of the kernel section of every Windows 11 21H2-24H2 file of the campaign) fails with "EOF Error" and aborts the whole conversion, including with the existing --version 0. The layer now passes the size of the set (pages * 4096). Versions 0 to 3 are otherwise unchanged.
…campaign logs) - hib_check.py: walks the chain of compression sets of a hiberfil.sys with the 23h2, 24h2-early (0x238) and 24h2 (0x250) layouts, finds the header qwords that bound the kernel section, reports set kinds and header bits, and with --decompress decodes every compressed set with the output size checked. Counters only, nothing identifying by default. - compare_outputs.py, count_nonzero_pages.py: page-by-page comparison of two Dump outputs, non-zero page count of a raw image. - test_hib_layout.py (16 tests of the tools) and test_hib_layer.py (3 tests of the real layer) on synthetic files generated in memory, no sample data. - campaign_2026-10/: logs of the campaign on Windows 11 21H2/22H2/23H2/24H2/ 25H2/26H1 lab VMs: per file hib_check output, Dump with this branch (twice), with the original code and with the wrong --version; unit tests x5, with the decoder fix reverted, and ruff.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Target:
forensicxlab/volatility3, branchfeature/hibernation-layer(upstream PR volatilityfoundation#1036).Follow-up of my comment on volatilityfoundation#1036: volatilityfoundation#1036 (comment)
Summary
Two independent problems of the hibernation layer, found while converting Windows 11 Fast Startup
hiberfil.sysfiles, with a fix for each and the test campaign that validates them on Windows 11 21H2, 22H2, 23H2, 24H2, 25H2 and 26H1 lab VMs (official ISOs, kernel builds 22000.318, 22621.382, 22621.2428, 26100.1742, 26100.6584, 28000.1340).KernelPagesProcessedis not at0x230on build 26100 and later, and its offset depends on the build. With--version 0the layer reads0x230, which on those files holds another counter (or 0), so the kernel section is either skipped (image almost empty, no error) or mis-walked (conversion refused). This PR adds--version 4(Windows 11 24H2 and later): the counter is looked for at the offsets seen so far (0x238,0x250) and the candidate is accepted only if the chain of compression sets of the kernel section adds up to exactly that number of pages. A file where no candidate matches is refused with an explicit message instead of producing a truncated image. The compression-set header of those builds is parsed with the size in bits 8-28 and bit 30 selecting LZ77+Huffman (bit 29, set on most sets, is not part of the size).A LZ77+Huffman set of fewer than 16 pages aborts the whole conversion ("EOF Error").
_uncompressalways asked the decoder for 65536 bytes. Every Windows 11 21H2/22H2/23H2/24H2 Fast Startup file of the campaign has one such set at the end of the kernel section, so the existing--version 0fails on 21H2-23H2 files (ValueError: EOF Error, exit 1). The layer now passes the size of the set (pages x 4096). No behaviour change for sets of 16 pages.Versions 0 to 3 are otherwise untouched.
Offsets seen (counter that bounds the kernel section)
KernelPagesProcessed0x2300x2300x238_PO_HIBER_PERFlayout for 24H20x250So a fixed offset per
--versioncannot cover build 26100: the two 26100 builds differ. The chain check picks the right one without a build number, and refuses the file otherwise.What is in the PR
volatility3/framework/layers/hib.py:WINDOWS_11_24H2 = 4;_find_kernel_pages_processed/_kernel_chain_has_pages(chain-validated counter); 24H2 set-header rules;_uncompresstakes the output size of the set (fix 2, applies to every version).volatility3/framework/plugins/windows/hibernation.py:--versionhelp text.contrib/hibernation_checks/: standalone checking tools (no Volatility needed, counters only, nothing identifying printed by default), 16 synthetic tests of the tools, 3 tests of the real layer on synthetic files (test_hib_layer.py: every page reads back for the three layouts, a 4-page Huffman set converts, a 24H2 file with no matching counter is refused), and the logs of the campaign below.Test campaign (logs in
contrib/hibernation_checks/campaign_2026-10/)Method, one VM at a time: restore the
after-install-coldsnapshot of the VM, boot, injectcmd.exe /c powercfg /h on & shutdown /s /hybrid /t 5into the logged-in session (DRAKVUF,-m createproc), wait for the domain to power off by itself, mount the disk image read-only and copyhiberfil.sys. Each file is identified by its ownSystemTime(windows.hibernation.Info; the guest clocks are 2 h behind UTC) and its sha256, which differ between rounds. Three generations per build plus the six files of the day before (round 0); three generations where the VM resumed from its own Fast Startup file and the injection failed are logged and excluded (signatureWAKE, no hybrid shutdown), and were redone.For every file:
hib_check.py --decompress(chain walk with the 3 layouts, header bits, decompression of every compressed set with the output size checked), thenwindows.hibernation.Dumpwith this branch and the right--version(exit code, size and sha256 of the raw image, number of non-zero pages), a second identical run (sha256 identical: deterministic), the original branch head (603b53f) with--version 0and--version 4, and this branch with the wrong--version(must refuse, not produce a truncated image). Unit tests run 5 times, once more with fix 2 reverted (the 3 layer tests then fail withEOF Error), andruff check/ruff format --checkas in the repository workflow (the two reports on the whole tree are pre-existing on the base branch:codecs/__init__.py,linux/sockscan.py).Rounds: r0 = files of 2026-10-13 (same method; the 26H1 r0 file turned out to be the one written at install time, it is kept as a genuine 26H1 file but not counted as a generation), r1-r3 = 2026-10-14, r4 = replacement of the three failed generations. The hiberfil.sys of rounds 2-3 were deleted after their checks (disk space), so their
hib_check.pyoutput comes from the earlier version of the tool, which did not report the kernel-section bit statistics for the0x230/0x238layouts (n/a); the decompression counters are the same in both versions.Files and counter offset (
hib_check.py --decompress)windows.hibernation.DumpUnit tests: 19/19 on each of the 5 runs (
final-unittest-*.log); with fix 2 reverted the 3 layer tests fail with 6EOF Error(final-unittest-fix-reverted.log). Ruff: clean on the files of this PR (final-ruff.log).Not covered
0x250offset comes from one file only (the 26100.9445 one), which cannot be shared.