← all posts

ParodyError: Breaking MmCopyMemory via the Parity Error Bit

2026.07.28 #kernel#anti-dump#security#evasion

Introduction

Several months ago I documented an interesting behavior of the Windows memory manager on a public forum. In this post, I'll explain how the ParityError bit within the internal MMPFN structure can be used to prevent kernel memory from being copied through common APIs such as MmCopyMemory.

What is a Parity Error?

A parity or ECC error indicates that hardware detected corruption while accessing memory. Windows records this state in the PFN database so that the memory manager can avoid using or copying pages that are believed to be faulty.

How does Windows tell the Memory Manager (MM) that the given physical memory is faulty?

Windows keeps track of every physical page on the system through the Page Frame Number (PFN) Database or MmPfnDatabase. The entries of this database store metadata about its corresponding physical page, including the parity error status. In memory, each entry is represented by an _MMPFN structure. For reference here is the named structure for Windows 10 22H2 taken from the Vergilius Project:

//0x30 bytes (sizeof)
struct _MMPFN
{
    union
    {
        struct _LIST_ENTRY ListEntry;                                       //0x0
        struct _RTL_BALANCED_NODE TreeNode;                                 //0x0
        struct
        {
            union
            {
                struct _SINGLE_LIST_ENTRY NextSlistPfn;                     //0x0
                VOID* Next;                                                 //0x0
                ULONGLONG Flink:36;                                         //0x0
                ULONGLONG NodeFlinkHigh:28;                                 //0x0
                struct _MI_ACTIVE_PFN Active;                               //0x0
            } u1;                                                           //0x0
            union
            {
                struct _MMPTE* PteAddress;                                  //0x8
                ULONGLONG PteLong;                                          //0x8
            };
            struct _MMPTE OriginalPte;                                      //0x10
        };
    };
    struct _MIPFNBLINK u2;                                                  //0x18
    union
    {
        struct
        {
            USHORT ReferenceCount;                                          //0x20
            struct _MMPFNENTRY1 e1;                                         //0x22
        };
        struct
        {
            struct _MMPFNENTRY3 e3;                                         //0x23
        struct
        {
            USHORT ReferenceCount;                                          //0x20
        } e2;                                                               //0x20
        };
        struct
        {
            ULONG EntireField;                                              //0x20
        } e4;                                                               //0x20
    } u3;                                                                   //0x20
    USHORT NodeBlinkLow;                                                    //0x24
    UCHAR Unused:4;                                                         //0x26
    UCHAR Unused2:4;                                                        //0x26
    union
    {
        UCHAR ViewCount;                                                    //0x27
        UCHAR NodeFlinkLow;                                                 //0x27
        struct
        {
            UCHAR ModifiedListBucketIndex:4;                                //0x27
            UCHAR AnchorLargePageSize:2;                                    //0x27
        };
    };
    union
    {
        ULONGLONG PteFrame:36;                                              //0x28
        ULONGLONG ResidentPage:1;                                           //0x28
        ULONGLONG Unused1:1;                                                //0x28
        ULONGLONG Unused2:1;                                                //0x28
        ULONGLONG Partition:10;                                             //0x28
        ULONGLONG FileOnly:1;                                               //0x28
        ULONGLONG PfnExists:1;                                              //0x28
        ULONGLONG Spare:9;                                                  //0x28
        ULONGLONG PageIdentity:3;                                           //0x28
        ULONGLONG PrototypePte:1;                                           //0x28
        ULONGLONG EntireField;                                              //0x28
    } u4;                                                                   //0x28
};

The bit we are looking for is stored within e3 or the _MMPFNENTRY3 structure:

//0x1 bytes (sizeof)
struct _MMPFNENTRY3
{
    UCHAR Priority:3;                                                       //0x0
    UCHAR OnProtectedStandby:1;                                             //0x0
    UCHAR InPageError:1;                                                    //0x0
    UCHAR SystemChargedPage:1;                                              //0x0
    UCHAR RemovalRequested:1;                                               //0x0
    UCHAR ParityError:1;                                                    //0x0
};

If this bit is set, it signals that this physical page is faulty and vice versa.

Verifying the Behaviour

Up to this point I've given a slight overview of the Windows MM, shown you what a PFN entry looks like and where the parity bit is held. I've also told you that setting this bit tells your system that the memory is faulty, and in return this prevents most kernel APIs from copying your memory, but what good are words without proof?

MiCopySinglePage Disassembly

Below are snippets of Windows 10 22H2's build of ntoskrnl, inside the MiCopySinglePage function. MiCopySinglePage is used internally by MmCopyMemory to copy the memory.

If you managed to make it past the if ((0x4000000000000 & rax_1) != 0) cases then you're met instantly with a conditional that determines whether or not you continue to success or end up failing with error code 0xC0000709 aka STATUS_HARDWARE_MEMORY_ERROR.

14030c3a4                    if (*((rbx_2 << 3) + -0x57fffffffdd) s>= 0 && MiIsPageSecured(-0x58000000000 + (rbx_2 << 3)) == 0)

The important part here is the first condition: *((rbx_2 << 3) + -0x57fffffffdd) s>= 0. Although the decompiler emits this as a signed comparison, the value being tested is a signed byte. A signed 8 bit value is negative when bit 7 is set, which makes this condition equivalent to PFN->e3 & 0x80. What is 0x80 checking for? If we look back at the _MMPFNENTRY3 structure we can see that UCHAR ParityError:1 is the eighth bit (bit 7). Setting this bit causes this case to fail, skipping the page copy entirely and eventually reaching:

14046dd19                    return STATUS_HARDWARE_MEMORY_ERROR; // 0xc0000709

I won't be covering the implementation details here, but the reverse engineering above should provide enough information for interested readers to reproduce the behavior themselves.