MMPFN

The MMPFN (Memory Manager Page Frame Number) structure is the key to pretty much everything that the Memory Manager knows about a page of physical memory that is in general use. There can be, and typically is, other physical memory known to Windows but it is treated as device-specific memory in a so-called I/O space and is never used to support allocations of virtual memory: pages of memory in the I/O space are not represented by MMPFN structures.

The MMPFN is arguably the most fundamental of all Memory Manager structures. An array of MMPFN structures, one for each page from physical address zero to the highest possible, is one of the kernel’s largest single uses of memory, and certainly of memory that isn’t ever paged out. A little more than 1% of physical memory—a little less on 32-bit Windows—is lost from general use as overhead just from this MMPFN array.

To inspect either a single MMPFN or the whole array of them when debugging, use the !pfn command or get the array’s address from the internal variable named MmPfnDatabase. In 64-bit Windows, starting with Windows Vista, this variable is pre-set to FFFFFA80`00000000, which is hard-coded throughout the kernel as the one address of the MMPFN array. This stops for the 1607 release of Windows 10. Apparently as a continuing programme of kernel-mode Address Space Layout Randomization (ASLR), this address is among the several whose (many) hard-coded references throughout the kernel get changed at load time through the Dynamic Value Relocation Table in the kernel’s load configuration.

Variability

With one MMPFN for every physical page, nobody wants this structure to grow as Windows evolves. In all known Windows versions, the MMPFN is:

That the MMPFN doesn’t change in size certainly doesn’t mean that it doesn’t change. The layout has varied greatly as ever more gets packed in ever more intricately. Inevitably, variation occurs even between builds of the one version. Along with the conventional dated names of the frequent Windows 10 releases, this note uses the following shorthands for variations within old versions:

Beware that although Windows XP SP2 is more usual as these notes’ cut-off between early and late for version 5.1, the MMPFN changed at SP1.

Layout

Microsoft’s name for the MMPFN structure itself and the names and types of its members are known from type information in public symbol files, starting with Windows 2000 SP3. What’s shown for earlier versions is inferred from what use these are (yet) known to make of the MMPFN. Where known use corresponds closely with that of a version for which Microsoft’s symbols are available, it seems reasonable to suppose continuity—but this is more than usually uncertain for this structure, given its elaborate substructure. Some use anyway 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.

Offset (x86) Offset (x64) Definition Versions
0x00 0x00
union {
    /*  changing members, follow link  */
} u1;
3.10 to 6.3
union {
    LIST_ENTRY ListEntry;
    RTL_BALANCED_NODE TreeNode;
    struct {
        /*  see below: ListEntry Overlay  */
    };
};
10.0 and higher
0x04 (3.10 to 5.2) 0x08 (late 5.2)
MMPTE *PteAddress;
3.10 to 5.2
0x08 (3.10 to 5.2);
0x04 (6.0 to 6.3);
0x10
0x10 (late 5.2);
0x08 (6.0 to 6.3);
0x18
union {
    /*  changing members, follow link  */
} u2;
3.10 to 6.3
MIPFNBLINK u2;
10.0 and higher
0x08 (6.0 to 6.3) 0x10 (6.0 to 6.3)
union {
    /*  see below: Union with PteAddress  */
};
6.0 to 6.3
0x0C (3.10 to 3.51)  
USHORT ReferenceCount;
3.10 to 3.50
ULONG ReferenceCount;
3.51 only
0x0E (3.10 to 3.50)   unknown 16-bit count 3.10 to 3.50
0x0C (4.0 to 6.3);
0x14
0x18 (late 5.2 to 6.3);
0x20
union {
    /*  changing members, follow link  */
} u3;
5.0 and higher
  0x1C (late 5.2 to 6.3);
0x24
ULONG UsedPageTableEntries;
late 5.2 only
USHORT UsedPageTableEntries;
6.0 to 6.1
USHORT NodeBlinkLow;
6.2 and higher
  0x1E (6.0 to 6.3);
0x26
UCHAR VaType;
6.0 to 6.1
struct {
    /*  see below: VaType Bits  */
};
6.2 and higher
  0x1F (6.0 to 6.3);
0x27
UCHAR ViewCount;
6.0 to 6.1
union {
    UCHAR ViewCount;
    UCHAR NodeFlinkLow;
};
6.2 and higher
0x10 (3.10 to 6.3) 0x20 (late 5.2 to 6.3)
MMPTE OriginalPte;
3.10 to 5.1
union {
    /*  see below: Union with OriginalPte  */
};
5.2 to 6.1
MMPTE OriginalPte;
6.2 to 6.3
0x14 (non-PAE);
0x18 (PAE)
0x28 unknown MMPFNENTRY 3.10 to 3.51
ULONG PteFrame;
4.0 to 5.0
union {
    /*  changing members, follow link  */
} u4;
5.1 and higher

ListEntry Overlay

Windows 10 introduces to the MMPFN the relatively large LIST_ENTRY and RTL_BALANCED_NODE structures. Three old members are made into an unnamed structure that shares the space. Take away the construction and the Windows 10 MMPFN has the following 0x10 or 0x18 bytes at its start:

Offset (x86) Offset (x64) Definition Versions
0x00 0x00
union {
    /*  changing members, follow link   */
} u1;
10.0 and higher
0x04 0x08
union {
    /*  see below: Union with PteAddress  */
};
10.0 and higher
0x08 0x10
MMPTE OriginalPte;
10.0 and higher

Union with PteAddress

The PteAddress is a direct MMPFN member before version 6.0, which wraps it in an unnamed union and shifts it deeper into the structure. Version 10.0 then wraps the whole of this union into an unnamed structure within a different unnamed union and brings it back towards the front.

Offset (x86) Offset (x64) Definition Versions
0x08 (6.0 to 6.3);
0x04
0x10 (6.0 to 6.3);
0x08
MMPTE *PteAddress;
6.0 and higher
PVOID volatile VolatilePteAddress;
6.0 and higher
0x08 (6.1 to 6.3) 0x10 (6.1 to 6.3)
LONG volatile Lock;
6.1 to 6.3
0x08 (6.1 to 6.3);
0x04
0x10 (6.1 to 6.3);
0x08
ULONG_PTR PteLong;
6.1 and higher

VaType Bits

Mask (x64) Definition Versions
0x0F
UCHAR Unused : 4;
6.2 and higher
0xF0
UCHAR VaType : 4;
6.2 to 1607
UCHAR Unused2 : 4;
1703 and higher

Union with OriginalPte

Offset (x86) Offset (x64) Definition Versions
0x10 (5.2 to 6.1) 0x20 (5.2 to 6.1)
MMPTE OriginalPte;
5.2 to 6.1
0x10 (5.2 to 6.1) 0x20 (5.2 to 6.1)
LONG AweReferenceCount;
5.2 only
LONG volatile AweReferenceCount;
6.0 to 6.1