The first megabyte of a VHDX file is the header section: five 64 KiB blocks, then
reserved space. This page follows Microsoft’s specification, [MS-VHDX]: Virtual Hard
Disk v2 (VHDX) File Format, revision 8.0 (April 2024), section 2.2. All numbers
are little-endian, and checksums are CRC-32C (Castagnoli polynomial
0x1EDC6F41). The specification stores GUIDs “in their binary form”;
QEMU’s VHDX driver reads them as Microsoft’s GUID structure (a 32-bit and two
16-bit little-endian numbers, then 8 bytes as written), which is how the example below
is laid out.
Layout
0x00000File type identifiervhdxfileand the creator64 KiB0x10000Header 164 KiB0x20000Header 264 KiB0x30000Region table 164 KiB0x40000Region table 264 KiB0x50000Reserved768 KiB
File type identifier
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
0 | 8 | text | Signature | vhdxfile (76 68 64 78 66 69 6C 65). |
8 | 512 | UTF-16 | Creator | Optional; identifies the program that made the file, and may be zero-terminated. For diagnosis only: programs must not change their behaviour because of it. |
520 | rest | – | Reserved | To the end of the 64 KiB block. Nothing in the first 64 KiB is rewritten after the file is created. |
Headers
There are two 4 KiB headers, at 64 KiB and 128 KiB, so that one is always intact while the other is being updated. A header is valid when its signature and checksum are right; the current header is the only valid one, or the valid one with the higher sequence number. With no current header the file is corrupt. A writer updates the out-of-date header (sequence number plus one, new checksum), flushes, and should then update the other too.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
0 | 4 | text | Signature | head. |
4 | 4 | uint32 | Checksum | CRC-32C (Castagnoli polynomial) of the first 4 KiB of the block, computed with this field as 0. |
8 | 8 | uint64 | Sequence number | Raised with each update; the higher valid header is current. |
16 | 16 | GUID | File write GUID | Changed to a new value on each open, before the first change of any kind to the file (metadata or log replay); may be left alone on read-only media. |
32 | 16 | GUID | Data write GUID | Changed before the first change a reader of the virtual disk could see: metadata, block data, disk size or block states. Moving blocks within the file does not change it. A differencing child records its parent’s data write GUID (see parent locator). |
48 | 16 | GUID | Log GUID | All zero: the log is empty and must not be replayed. Otherwise, only log entries carrying this GUID are valid. |
64 | 2 | uint16 | Log version | 0. Otherwise the file must not be processed, unless the log GUID is zero. |
66 | 2 | uint16 | Version | 1. Otherwise this specification does not apply. |
68 | 4 | uint32 | Log length | Bytes of log, a multiple of 1 MiB. |
72 | 8 | uint64 | Log offset | Where the log is, a multiple of 1 MiB; it must not overlap anything else. |
80 | 4016 | – | Reserved | Zeros, to the end of the 4 KiB checksum area. |
Region tables
Two identical copies. A region table lists the big structures in the file and where they are.
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
0 | 4 | text | Signature | regi. |
4 | 4 | uint32 | Checksum | CRC-32C of the whole 64 KiB table, computed with this field as 0. |
8 | 4 | uint32 | Entry count | At most 2,047. |
12 | 4 | – | Reserved | 0. |
16 | 32 each | entries | Entries | See below. |
| Offset | Size | Type | Name | Description |
|---|---|---|---|---|
0 | 16 | GUID | Region GUID | What the region is; see below. |
16 | 8 | uint64 | File offset | Where it starts, a multiple of 1 MiB, at least 1 MiB. |
24 | 4 | uint32 | Length | Its size, a multiple of 1 MiB. |
28 | 4 | bits | Required | 1: a reader that does not recognise this region must refuse to open the file. |
| GUID | Region |
|---|---|
2DC27766-F623-4200-9D64-115E9BFD4A08 | The block allocation table; required. |
8B7CA206-4790-4B9A-B8FE-575F050F886E | The metadata region; required. |
The log, BAT, metadata and data blocks can be anywhere after the first megabyte, each on a 1 MiB boundary.
Example
A region table entry for the BAT. The GUID’s first three groups are stored byte reversed.
00030010 66 77 C2 2D 23 F6 00 42 9D 64 11 5E 9B FD 4A 08 fw.-#..B.d.^..J.
00030020 00 00 30 00 00 00 00 00 00 00 10 00 01 00 00 00 ..0.............
Reading it: the BAT GUID 2DC27766-F623-4200-9D64-115E9BFD4A08; file
offset 0x300000 (3 MiB); length 0x100000 (1 MiB); flags 1,
required.