Geoff Chappell - Software Analyst
The MI_USER_VA_INFO structure (formally _MI_USER_VA_INFO) is a vital part of the implementation of the Working Set List (WSL) in Windows 8 and higher. Indeed, it’s the largest and most complex of all the implementation’s structures. Some measure of its importance as an implementation detail—or its deserved obscurity, depending on perspective—is that the MI_USER_VA_INFO disappears entirely from Microsoft’s public symbol files for the 1607 release of Windows 10, and is not known in any since.
From all the way back at Windows NT 3.1, the Working Set List is modelled by the MMWSL structure. Each process has one, pointed to by the VmWorkingSetList member of the MMSUPPORT that’s nested in the EPROCESS as the latter’s Vm member.
The MI_USER_VA_INFO looks to have been introduced to tidy the implementation of the Working Set List in Windows 8. An assortment of MMWSL members, some long-standing and some very large, were collected into the new structure as a new member named UserVaInfo at the end of the MMWSL. The splitting of the MMWSL for the 1607 release of Windows 10, into an MMWSL_INSTANCE and MMWSL_SHARED, also split off the MI_USER_VA_INFO: it is not nested in either. Better, perhaps, would be to say the MMWSL was split in three.
In the 1607 release itself, the MI_USER_VA_INFO precedes the MMWSL_FULL (which in turn has the Instance and Shared parts). All these parts of the Working Set List are in hyperspace, as for earlier versions. For 32-bit Windows, hyperspace continues to have the fixed address 0xC0800000 and the MI_USER_VA_INFO has the fixed address 0xC0802000. For 64-bit Windows, however, the 1607 release allows that hyperspace can vary. What can be said in common about the positioning of the MI_USER_VA_INFO is that it has a fixed offset into hyperspace: 0x2000 and 0x10804000 bytes in 32-bit and 64-bit Windows, respectively.
The 1703 release reverses the order: the MI_USER_VA_INFO begins immediately where the MMWSL_INSTANCE ends. Neither is in hyperspace. Both are instead in one allocation of non-paged pool. The MMWSL_INSTANCE at its start is pointed to from the VmWorkingSetList member of the MMSUPPORT_FULL structure that is embedded in the EPROCESS as its Vm member.
As an internal structure with little, if any, visibility outside the kernel, the MI_USER_VA_INFO varies greatly between versions and even between builds. The following changes of size give a rough measure of this variability so far:
Version | Size (x86) | Size (x64) |
---|---|---|
6.2 | 0x0CFC | 0x0478 |
6.3 | 0x0D1C | 0x0128 |
10.0 | 0x0D20 | 0x0108 |
1511 | 0x0D48 | 0x0158 |
1607 | 0x0D50 | 0x0168 |
1703 to 1709 | 0x0D58 | 0x0178 |
1803 | 0x0F68 | 0x01E8 |
1809 | 0x0F78 | 0x0208 |
1903 to 2004 | 0x0F80 | 0x0248 |
The size for the 1607 release of 64-bit Windows is uncertain. The kernel’s own use that is yet known reaches up to but not including offset 0x0164. With 8-byte alignment the structure then is 0x0168 bytes. But the MMWSL_FULL that follows the structure is at offset 0x0170. In the difference may be use that is not yet accounted for. What is here thought more likely is that the MMWSL_FULL has 16-byte alignment, apparently picked up from its nesting of MMWSL_SHARED.
These sizes, and the offsets, names and types in the tables that follow, are from Microsoft’s symbol files for the kernel from version 6.2 up to and includng the 1511 release of Windows 10. What’s known for later versions is something of a guess, or at least a work in progress, pending discovery of type information in public symbol files (or some similarly authoritative but public disclosure).
Offset (x86) | Offset (x64) | Definition | Versions | Remarks |
---|---|---|---|---|
0x00 (6.2 to 1607) | 0x00 (6.2 to 1607) |
ULONG NumberOfCommittedPageTables; |
6.2 to 1607 | previously at 0x20 and 0x28 in MMWSL |
0x04 (6.2 to 6.3) | 0x04 (6.2 to 6.3) |
ULONG PhysicalMappingCount; |
6.2 to 6.3 | next at 0x24 and 0x28 |
0x08 (6.2 to 6.3); 0x04 (10.0) |
0x08 (6.2 to 6.3); 0x04 (10.0) |
ULONG VadBitMapHint; |
6.2 to 10.0 | previously at 0x24 and 0x2C in MMWSL |
0x0C (6.2 to 6.3); 0x08 (10.0) |
0x0C (6.2 to 6.3); 0x08 (10.0) |
ULONG LastAllocationSizeHint; |
6.2 to 10.0 | previously at 0x34 and 0x3C in MMWSL |
0x10 (6.2 to 6.3); 0x0C (10.0) |
0x10 (6.2 to 6.3); 0x0C (10.0) |
ULONG LastAllocationSize; |
6.2 to 10.0 | previously at 0x38 and 0x40 in MMWSL |
0x14 (6.2 to 6.3); 0x10 (10.0) |
0x14 (6.2 to 6.3); 0x10 (10.0) |
ULONG LowestBottomUpVadBit; |
6.2 to 10.0 | |
0x18 (6.2 to 6.3); 0x14 (10.0) |
0x18 (6.2 to 6.3); 0x14 (10.0) |
ULONG VadBitMapSize; |
6.2 to 10.0 | |
0x04 (1511 to 1607); 0x00 (1703 to 1709) |
0x08 (1511 to 1607); 0x00 (1703 to 1709) |
PVOID HighestTopDownAllocationAddress; |
1511 to 1709 | previously at 0x34 and 0x48 |
0x00 | unaccounted eight bytes | 1803 and higher | ||
0x08 (1511 to 1607); 0x04 (1703 to 1709); 0x00 |
0x10 (1511 to 1607); 0x08 |
MI_VAD_ALLOCATION_CELL VadCell [MiVadAllocationCellMaximum]; |
1511 and higher | |
0x48 | 0xB0 (1803 to 1809); 0xE0 |
unknown ULONG | 1803 to 1809 | |
unknown ULONG_PTR | 1903 and higher | |||
0x1C (6.3); 0x18 (10.0); 0x40 (1511 to 1607); 0x3C (1703 to 1709); 0x4C |
0x1C (6.3); 0x18 (10.0); 0x60 (1511 to 1607); 0x58 (1703 to 1709); 0xB4 (1803 to 1809); 0xE8 |
ULONG VadBitMapCommitment; |
6.3 to 1809 | |
ULONG_PTR VadBitMapCommitment; |
1903 and higher | |||
0x1C (6.2); 0x20 (6.3); 0x1C (10.0); 0x44 (1511 to 1607); 0x40 (1703 to 1709) |
0x1C (6.2); 0x20 (6.3); 0x1C (10.0); 0x64 (1511 to 1607); 0x5C (1703 to 1709) |
ULONG MaximumLastVadBit; |
6.2 to 1709 | previously at 0x30 and 0x38 in MMWSL |
0x20 (6.2); 0x24 (6.3); 0x20 (10.0); 0x48 (1511 to 1607); 0x44 (1703 to 1709); 0x50 |
0x20 (6.2); 0x24 (6.3); 0x20 (10.0); 0x68 (1511 to 1607); 0x60 (1703 to 1709); 0xB8 (1803 to 1809); 0xF0 |
LONG VadsBeingDeleted; |
6.2 and higher | |
0x48 (1703 to 1709); 0x54 |
0x64 (1703 to 1709); 0xBC (1803 to 1809); 0xF4 |
unaccounted four bytes | 1703 and higher | |
0xF8 | unaccounted eight bytes | 1903 and higher | ||
0x4C (1511 to 1709); 0x58 |
0x6C (1511 to 1607); 0x68 (1703 to 1709); 0xC0 (1803 to 1809); 0x0100 |
LONG NumberOfDebugEnclaves; |
1511 and higher | |
0x24 (10.0); 0x50 (1511 to 1709); 0x5C |
0x28 (10.0); 0x70 (1511 to 1709); 0xC8 (1803 to 1809); 0x0108 |
ULONG_PTR PhysicalMappingCount; |
10.0 and higher | previously at 0x04 |
0x24 (6.2); 0x28 (6.3 to 10.0); 0x54 (1511 to 1709); 0x60 |
0x28 (6.2 to 6.3); 0x30 (10.0); 0x78 (1511 to 1709); 0xD0 (1803 to 1809); 0x0110 |
KEVENT *LastVadDeletionEvent; |
6.2 and higher | |
0x28 (6.2); 0x2C (6.3 to 10.0) |
0x30 (6.2 to 6.3); 0x38 (10.0) |
ULONG *VadBitBuffer; |
6.2 to 10.0 | |
0x2C (6.2); 0x30 (6.3 to 10.0) |
0x38 (6.2 to 6.3); 0x40 (10.0) |
PVOID LowestBottomUpAllocationAddress; |
6.2 to 10.0 | |
0x30 (6.2); 0x34 (6.3 to 10.0) |
0x40 (6.2 to 6.3); 0x48 (10.0) |
PVOID HighestTopDownAllocationAddress; |
6.2 to 10.0 | next at 0x04 and 0x08 |
0x34 (6.2); 0x38 (6.3 to 10.0) |
0x48 (6.2 to 6.3); 0x50 (10.0) |
PVOID FreeTebHint; |
6.2 to 10.0 | |
0x58 (1511 to 1709); 0x64 |
0x80 (1511 to 1709); 0xD8 (1803 to 1809); 0x0118 |
LIST_ENTRY SubVadRanges [1]; |
1511 to 1607 (x86) | |
LIST_ENTRY SubVadRanges [2]; |
1703 to 1803 (x86) | |||
LIST_ENTRY SubVadRanges [1]; |
1809 and higher (x86) | |||
LIST_ENTRY SubVadRanges [3]; |
1511 to 1607 (x64) | |||
LIST_ENTRY SubVadRanges [4]; |
1703 to 1803 (x64) | |||
LIST_ENTRY SubVadRanges [2]; |
1809 and higher (x64) | |||
0x3C (6.3 to 10.0); 0x60 (1511 to 1607); 0x68 (1703 to 1709); 0x74 (1803); 0x6C |
0x50 (6.3); 0x58 (10.0); 0xB0 (1511 to 1607); 0xC0 (1703 to 1709); 0x0118 (1803); 0x0108 (1809); 0x0138 |
BOOLEAN NumaAware; |
6.3 and higher | |
0x40 (10.0); 0x68 (1511 to 1607); 0x70 (1703 to 1709); 0x78 (1803); 0x70 |
0x60 (10.0); 0xB8 (1511 to 1607); 0xC8 (1703 to 1709); 0x0120 (1803); 0x0110 (1809); 0x0140 |
ULONGLONG CloneNestingLevel; |
10.0 and higher | |
0x80 (1803); 0x78 |
0x0128 (1803); 0x0118 (1809); 0x0148 |
unknown SLIST_HEADER | 1803 and higher | |
0x38 (6.2); 0x40 (6.3); 0x48 (10.0); 0x70 (1511 to 1607); 0x78 (1703 to 1709); 0x88 (1803); 0x80 |
0x50 (6.2); 0x58 (6.3); 0x68 (10.0); 0xC0 (1511 to 1607); 0xD0 (1703 to 1709); 0x0138 (1803); 0x0128 (1809); 0x0158 |
ULONG_PTR PrivateFixupVadCount; |
6.2 and higher | |
0x58 (6.2) |
PVOID FreeUmsTebHint; |
6.2 only | next at 0x0120 | |
0x84 | 0x0130 (1809); 0x0160 |
unknown pointer-sized integer | 1809 and higher nd higher | |
0x88 | 0x0138 (1809); 0x0168 |
unknown pointer | 1809 and higher | |
0x8C | 0x0170 | unknown EX_PUSH_LOCK_AUTO_EXPAND | 1809 and higher | |
0x74 (1607); 0x7C (1703 to 1709); 0x8C (1803); 0x98 |
0xC8 (1607); 0xD8 (1703 to 1709); 0x0140 (1803); 0x0150 (1809); 0x0180 |
unknown ULONG_PTR count | 1607 and higher | |
0x78 (1607); 0x80 (1703 to 1709); 0x90 (1803); 0x9C |
0xD0 (1607); 0xE0 (1703 to 1709); 0x0148 (1803); 0x0158 (1809); 0x0188 |
unknown ULONG_PTR count | 1607 and higher | |
0x44 (6.3); 0x4C (10.0); 0x74 (1511); 0x7C (1607); 0x84 (1703 to 1709); 0x94 (1803); 0xA0 |
0x60 (6.3); 0x70 (10.0); 0xC8 (1511); 0xD8 (1607); 0xE8 0x0150 (1803); 0x0160 (1809); 0x0190 |
MI_CFG_BITMAP_INFO CfgBitMap [CfgBitMapMax - 1]; |
6.3 and higher | |
0x54 (6.3); 0x58 (10.0); 0x80 (1511); 0x88 (1607); 0x90 (1703 to 1709); 0xA0 (1803); 0xB0 |
0xC0 (6.3); 0xA0 (10.0); 0xF8 (1511); 0x0108 (1607); 0x0118 (1703 to 1709); 0x0180 (1803); 0x01A0 (1809); 0x01D0 |
ULONG CommittedPageTableBufferForTopLevel [0x30]; |
6.3 and higher (x86) | |
ULONG CommittedPageTableBufferForTopLevel [8]; |
6.3 and higher (x64) | |||
0x0114 (6.3); 0x0118 (10.0); 0x0140 (1511); 0x0148 (1607); 0x0150 (1703 to 1709); 0x0160 (1803); 0x0170 |
0xE0 (6.3); 0xC0 (10.0); 0x0118 (1511); 0x0128 (1607); 0x0138 (1703 to 1709); 0x01A0 (1803); 0x01C0 (1809); 0x01F0 |
RTL_BITMAP CommittedPageTableBitmaps [1]; |
6.3 and higher (x86) | |
RTL_BITMAP CommittedPageTableBitmaps [3]; |
6.3 to 1809 (x64) | |||
RTL_BITMAP_EX CommittedPageTableBitmaps [3]; |
1903 and higher | |||
0x3C (6.2); 0x011C (6.3); 0x0120 (10.0); 0x0148 (1511); 0x0150 (1607); 0x0158 1703 to 1709); 0x0168 (1803); 0x0178 |
USHORT UsedPageTableEntries [0x0600]; [0x0600]; |
6.2 to 1703 | previously at 0x48 in MMWSL | |
0x0C3C (6.2) | 0x60 (6.2) |
ULONG CommittedPageTables [0x30]; |
6.2 only (x86) | previously at 0x0C48 in MMWSL |
ULONG *CommittedPageTables; |
6.2 only (x64) | previously at 0x68 in MMWSL | ||
0x68 (6.2); 0x0110 (6.3); 0xF0 (10.0); 0x0148 (1511); 0x0158 (1607); 0x0168 (1703 to 1709); 0x01D0 (1803); 0x01F0 (1809); 0x0224 |
ULONG PageTableBitmapPages; |
6.2 only | ||
ULONG PageTableBitmapPages [3]; |
10.0 and higher | |||
0x70 |
ULONGLONG CommitedPageDirectories [0x80]; |
6.2 only | previously at 0x78 in MMWSL | |
0x0470 |
ULONGLONG CommitedPageDirectoryParents [1]; |
6.2 only | previously at 0x0480 in MMWSL | |
0x0120 (6.3); 0x0100 |
PVOID FreeUmsTebHint; |
6.3 to 10.0 | previously at 0x58 | |
0x01E0 (1803); 0x0200 (1809); 0x0230 |
unaccounted eight bytes | 1803 and higher | ||
0x0238 | unknown pointer | 1903 and higher | ||
0x0F78 | 0x0240 | unknown 64-bit count | 1903 and higher |
The CfgBitMap member is indexed by the MI_CFG_BITMAP_TYPE enumeration. Its first element is for native addressing. This is the only element in 32-bit Windows but 64-bit Windows has a second for the 32-bit address space of WOW64 processes.