MMWSL

The MMWSL is the Memory Manager’s modelling of a Working Set List. Every working set, as modelled by an MMSUPPORT, has one: it’s pointed to from the latter’s VmWorkingSetList member.

Some measure of the MMWSL structure’s importance in memory management is that it’s one of the few things that Windows builds at a preset address. Through much of the history of Windows, every process’s working set has its MMWSL at the same address:

Version x86 PAE x64
3.10 to 3.50 0xC0482000    
3.51 to 5.0 0xC0502000 0xC0882000  
5.1 to early 5.2 0xC0503000
0xC0503800
0xC0883000
0xC0883800
 
late 5.2 0xC0502000
0xC0502800
0xC0881000
0xC0881800
0xFFFFF700`00081000
6.0 0xC0401000 0xC0801000
6.1 to 6.2 0xC0402000 0xC0802000 0xFFFFF700`01080000
6.3 to 1511   0xC0802000 0xFFFFF580`10804000

The early 32-bit builds, both with and without support for Physical Address Extension (PAE), access the current process’s MMWSL through an internal variable named MmWorkingSetList. Versions 5.1 and 5.2 can vary this variable’s initialisation by 0x00000800 when a virtual bias applies, as for the /3GB boot option. Starting with version 6.1, the variable is discarded in favour of hard-coding the address. All known 64-bit kernels hard-code the address.

Whether the predictability of this address for the MMWSL has ever been exploitable is not known, but the 1703 release of Windows 10 does away with it. Before then, however, the 1607 release discontinues the MMWSL structure with exactly this name. The structure survives but is named MMWSL_FULL and combines new MMWSL_INSTANCE and MMWSL_SHARED structures which each have a different selection of the old MMWSL members.

Variability

As an internal structure with little, if any, visibility outside the kernel, the MMWSL varies greatly between versions and even between builds.

Version Size (x86) Size (PAE) Size (x64)
3.10 0x0868    
3.50 0x0468    
3.51 to 4.0 0x0470    
early 5.0 (before Windows 2000 SP3) 0x06A0 0x0D00  
late 5.0 (starting with Windows 2000 SP3) 0x06A8 0x0D08  
5.1 0x069C 0x0CFC  
5.2 0x0698 0x0CF8 0x80
early 6.0 (before Windows Vista SP1) 0x06A8 0x0D08 0x0488
late 6.0 (starting with Windows Vista SP1) 0x06B8 0x0D18 0x0498
6.1 0x06A8 0x0D08 0x0488
6.2   0x0D9C 0x0530
6.3   0x0DBC 0x0260
10.0   0x0E20 0x0300
1511   0x0E48 0x0350

Layout

These sizes, and the names, offsets and types in the tables that follow, are from Microsoft’s symbol files for the kernel, starting with Windows 2000 SP3. What little information is given below for earlier versions—even for early builds of version 5.0—is at best tentative. What’s known of Microsoft’s names and types for these versions is something of a guess. Where use of a member corresponds closely with that of a version for which Microsoft’s symbols are available, it seems reasonable to suppose continuity. Some use, however, has no correspondence, the code having changed too much. Even where the use hasn’t changed, tracking it down exhaustively would be difficult, if not impossible, even with source code—and I haven’t yet tried to track it down exhaustively.

Two members are arrays whose size varies with the changing allowance for user-mode address space:

To index all the page tables that can be involved in mapping an arbitrary user-mode address to physical memory, occasional use is made below of MAX_USER_PDES:

Offset (x86) Offset (PAE) Offset (x64) Definition Versions Remarks
0x00 (3.10 to 5.1)  
ULONG Quota;
3.10 to 5.1  
0x04 (3.10 to 5.1);
0x00
0x00
ULONG FirstFree;
3.10 to 6.2  
ULONG_PTR FirstFree;
6.3 to 1511 next at 0x00 in MMWSL_SHARED
0x08 (3.10 to 5.1);
0x04
0x04 (5.2 to 6.2);
0x08
ULONG FirstDynamic;
3.10 to 6.2  
ULONG_PTR FirstDynamic;
6.3 to 1511 next at 0x04 and 0x08 in MMWSL_SHARED
0x0C (3.10 to 5.1);
0x08
0x08 (5.2 to 6.2);
0x10
ULONG LastEntry;
3.10 to 6.2  
ULONG_PTR LastEntry;
6.3 to 1511 next at 0x08 and 0x10 in MMWSL_SHARED
0x10 (3.10 to 5.1);
0x0C
0x0C (5.2 to 6.2);
0x18
ULONG NextSlot;
3.10 to 6.2  
ULONG_PTR NextSlot;
6.3 to 1511 next at 0x00 in MMWSL_INSTANCE
0x14 (3.10 to 5.1);
0x10 (5.2 to 6.1)
0x10 (5.2 to 6.1)
MMWSLE *Wsle;
3.10 to 6.1 next at 0x9C and 0xB0
0x14 (6.0 to 6.1) 0x18 (6.0 to 6.1)
PVOID LowestPagableAddress;
6.0 to 6.1 next at 0x2C and 0x30
0x18 (5.0)  
ULONG NumberOfCommittedPageTables;
5.0 only next at 0x28

0x20 (3.10 to 3.50);
0x1C (3.51 to 5.0);
0x18 (5.1);
0x14 (5.2);
0x18 (6.0 to 6.1);
0x10
0x18 (5.2);
0x10 (6.2);
0x20
ULONG LastInitializedWsle;
3.10 to 6.2  
ULONG_PTR LastInitializedWsle;
6.3 to 1511 next at 0x0C and 0x18 in MMWSL_SHARED
0x1C (6.0) 0x24 (6.0)
ULONG NextEstimationSlot;
6.0 only  
0x20 (6.0);
0x1C (6.1);
0x14
0x28 (6.0);
0x24 (6.1);
0x14 (6.2);
0x28
ULONG NextAgingSlot;
6.0 to 6.2  
ULONG_PTR NextAgingSlot;
6.3 to 1511 next at 0x04 and 0x08 in MMWSL_INSTANCE
0x18 0x18 (6.2);
0x30
ULONG NextAccessClearingSlot;
6.2 only  
ULONG_PTR NextAccessClearingSlot;
6.3 to 1511 next at 0x08 and 0x10 in MMWSL_INSTANCE
0x1C 0x1C (6.2);
0x38
ULONG LastAccessClearingRemainder;
6.2 to 1511 next at 0x0C and 0x18 in MMWSL_INSTANCE
0x20 0x20 (6.2);
0x3C
ULONG LastAgingRemainder;
6.2 to 1511 next at 0x10 and 0x1C in MMWSL_INSTANCE
0x24 0x24 (6.2);
0x40
ULONG WsleSize;
6.2 to 1511 next at 0x10 and 0x20 in MMWSL_SHARED
0x24 (6.0) 0x2C (6.0)
ULONG EstimatedAvailable;
6.0 only  
0x28 (6.0) 0x30 (6.0)
ULONG GrowthSinceLastEstimate;
6.0 only  
0x2C (6.0);
0x20 (6.1)
0x34 (6.0);
0x28 (6.1)
ULONG NumberOfCommittedPageTables;
6.0 to 6.1 previously at 0x24 and 0x2C;
next at 0x00 in MI_USER_VA_INFO
0x30 (6.0);
0x24 (6.1)
0x38 (6.0);
0x2C (6.1)
ULONG VadBitMapHint;
6.0 to 6.1 previously at 0x34 and 0x44;
next at 0x08 in MM_USER_VA_INFO
0x20 (5.0);
0x1C (5.1);
0x18 (5.2);
0x34 (6.0);
0x28
0x1C (5.2);
0x3C (6.0);
0x30 (6.1);
0x28 (6.2);
0x48
ULONG NonDirectCount;
5.0 to 6.2  
ULONG_PTR NonDirectCount;
6.3 to 1511 next at 0x14 and 0x28 in MMWSL_SHARED
0x38 (late 6.0);
0x2C (6.1)
0x40 (late 6.0);
0x34 (6.1)
ULONG LastVadBit;
late 6.0 to 6.1  
0x3C (late 6.0);
0x30 (6.1)
0x44 (late 6.0);
0x38 (6.1)
ULONG MaximumLastVadBit;
late 6.0 to 6.1 next at 0x1C in MI_USER_VA_INFO
0x40 (late 6.0);
0x34 (6.1)
0x48 (late 6.0);
0x3C (6.1)
ULONG LastAllocationSizeHint;
late 6.0 to 6.1 next at 0x0C in MM_USER_VA_INFO
0x44 (late 6.0);
0x38 (6.1)
0x4C (late 6.0);
0x40 (6.1)
ULONG LastAllocationSize;
late 6.0 to 6.1 next at 0x10 in MM_USER_VA_INFO
0x24 (5.0);
0x20 (5.1);
0x1C (5.2)
0x20 (5.2)
MMWSLE_HASH *HashTable;
5.0 to 5.2  
0x28 (5.0);
0x24 (5.1);
0x20 (5.2)
0x28 (5.2)
ULONG HashTableSize;
5.0 to 5.2  
0x24 (3.10 to 3.50);
0x2C (3.51 to 5.0)
 
KEVENT *ImageMappingPteEvent;
3.10 to early 5.0  
KEVENT ImageMappingPteEvent;
late 5.0 only  
0x28 (5.1);
0x24 (5.2)
0x2C (5.2)
ULONG NumberOfCommittedPageTables;
5.1 to 5.2 previously at 0x18;
next at 0x2C and 0x34
0x2C 0x30 (6.2);
0x50
PVOID LowestPagableAddress;
6.2 to 1511 previously at 0x14 and 0x18;
next at 0x18 and 0x30 in MMWSL_SHARED
0x38 (early 6.0);
0x48 (late 6.0);
0x3C (6.1);
0x30
0x40 (early 6.0);
0x50 (late 6.0);
0x48 (6.1);
0x38 (6.2);
0x58
MMWSLE_NONDIRECT_HASH *NonDirectHash;
6.0 to 1511 next at 0x1C and 0x38 in MMWSL_SHARED
0x30 (early 5.0);
0x3C (late 5.0);
0x2C (5.1);
0x28 (5.2);
0x3C (early 6.0);
0x4C (late 6.0);
0x40 (6.1);
0x34
0x30 (5.2);
0x48 (early 6.0);
0x58 (late 6.0);
0x50 (6.1);
0x40 (6.2);
0x60
PVOID HashTableStart;
5.0 to 5.2  
MMWSLE_HASH *HashTableStart;
6.0 to 1511 next at 0x20 and 0x40 in MMWSL_SHARED
0x34 (early 5.0);
0x40 (late 5.0);
0x30 (5.1);
0x2C (5.2);
0x40 (early 6.0);
0x50 (late 6.0);
0x44 (6.1);
0x38
0x38 (5.2);
0x50 (early 6.0);
0x60 (late 6.0);
0x58 (6.1);
0x48 (6.2);
0x68
PVOID HighestPermittedHashAddress;
5.0 to 5.2  
MMWSLE_HASH *HighestPermittedHashAddress;
6.0 to 1511 next at 0x24 and 0x48 in MMWSL_SHARED
0x44 (late 5.0);
0x34 (5.1);
0x30 (5.2)
0x40 (5.2)
ULONG NumberOfImageWaiters;
late 5.0 to 5.2  
0x38 (5.1);
0x34 (5.2)
0x44 (5.2)
ULONG VadBitMapHint;
5.1 to 5.2 next at 0x30 and 0x38
0x44 (early 6.0);
0x54 (late 6.0)
0x48 (5.2);
0x58 (early 6.0);
0x68 (late 6.0)
PVOID HighestUserAddress;
6.0 only (x86);
5.2 to 6.0 (x64)
 
  0x50 (5.2);
0x60 (early 6.0);
0x70 (late 6.0);
0x60 (6.1)
ULONG MaximumUserPageTablePages;
5.2 to 6.1  
  0x54 (5.2);
0x64 (early 6.0);
0x74 (late 6.0);
0x64 (6.1)
ULONG MaximumUserPageDirectoryPages;
5.2 to 6.1  
0x28 (3.10 to 3.50);
0x30 (3.51 to 4.0);
0x40 (early 5.0);
0x48 (late 5.0);
0x3C (5.1);
0x38 (5.2);
0x48 (early 6.0);
0x58 (late 6.0);
0x48 (6.1)
 
USHORT UsedPageTableEntries [0x0400];
3.10 only  
USHORT UsedPageTableEntries [MAX_USER_PDES];
3.50 to 6.1 next at 0x3C in MI_USER_VA_INFO
0x0828 (3.10);
0x0428 (3.50);
0x0430 (3.51 to 4.0);
0x0640 (early 5.0);
0x0648 (late 5.0);
0x063C (5.1);
0x0638 (5.2);
0x0648 (early 6.0);
0x0658 (late 6.0);
0x0648 (6.1)
0x0C40 (early 5.0);
0x0C48 (late 5.0);
0x0C3C (5.1);
0x0C38 (5.2);
0x0C48 (early 6.0);
0x0C58 (late 6.0);
0x0C48 (6.1)
0x58 (5.2);
0x68 (early 6.0);
0x78 (late 6.0);
0x68 (6.1)
ULONG CommittedPageTables [MAX_USER_PDES / 0x20];
3.10 to 6.1 next at 0x0C3C in MI_USER_VA_INFO;
ULONG *CommittedPageTables;
5.2 to 6.1 (x64) next at 0x60 in MI_USER_VA_INFO
  0x60 (5.2);
0x70 (early 6.0);
0x80 (late 6.0);
0x70 (6.1)
ULONG NumberOfCommittedPageDirectories;
5.2 to 6.1  
  0x68 (5.2);
0x78 (early 6.0);
0x88 (late 6.0);
0x78 (6.1)
ULONG *CommittedPageDirectories;
5.2 only  
ULONGLONG CommittedPageDirectories [0x80];
6.0 to 6.1 next at 0x70 in MI_USER_VA_INFO
  0x70 (5.2);
0x0478 (early 6.0);
0x0488 (late 6.0);
0x0478 (6.1)
ULONG NumberOfCommittedPageDirectoryParents;
5.2 to 6.1  
  0x78 (5.2);
0x0480 (early 6.0);
0x0490 (late 6.0);
0x0480 (6.1)
ULONGLONG CommittedPageDirectoryParents [1];
5.2 to 6.1 next at 0x0470 in MI_USER_VA_INFO
0x3C 0x50 (6.2);
0x70
ULONG ActiveWsleCounts [8];
6.2 only  
ULONG_PTR ActiveWsleCounts [8];
6.3 only  
ULONG_PTR ActiveWsleCounts [0x10];
10.0 to 1511 next at 0x14 and 0x20 in MMWSL_INSTANCE
0x5C (6.2 to 6.3);
0x7C
0x70 (6.2);
0xB0 (6.3);
0xF0
MI_ACTIVE_WSLE ActiveWsles [8];
6.2 only  
MI_ACTIVE_WSLE_LISTHEAD ActiveWsles [8];
6.3 only  
MI_ACTIVE_WSLE_LISTHEAD ActiveWsles [0x10];
10.0 to 1511 next at 0x54 and 0xA0 in MMWSL_INSTANCE
0x9C (6.2 to 6.3);
0xFC
0xB0 (6.2);
0x0130 (6.3);
0x01F0
MMWSLE *Wsle;
6.2 to 1511 previously at 0x10;
next at 0x30 and 0x50 in MMWSL_SHARED
0xA0 (6.2 to 6.3);
0x0100
0xB8 (6.2);
0x0138 (6.3);
0x01F8
MI_USER_VA_INFO UserVaInfo;
6.2 to 1511