> Windows Syscalls
ntoskrnl.exeT1106T1055T1027

NtMapUserPhysicalPages

Mappe des pages physiques allouées via AWE dans une fenêtre d'adresse virtuelle préalablement réservée.

Prototype

NTSTATUS NtMapUserPhysicalPages(
  PVOID       VirtualAddress,
  ULONG_PTR   NumberOfPages,
  PULONG_PTR  UserPfnArray
);

Arguments

NameTypeDirDescription
VirtualAddressPVOIDinBase d'une fenêtre d'adresse virtuelle réservée avec MEM_RESERVE | MEM_PHYSICAL.
NumberOfPagesULONG_PTRinNombre de pages à mapper. Ne doit pas dépasser la taille de la fenêtre réservée.
UserPfnArrayPULONG_PTRinTableau d'identifiants de page opaques issus de NtAllocateUserPhysicalPages, ou NULL pour démapper.

IDs de syscalls par version de Windows

Version de WindowsID de syscallBuild
Win10 15070x101win10-1507
Win10 16070x106win10-1607
Win10 17030x10Awin10-1703
Win10 17090x10Bwin10-1709
Win10 18030x10Cwin10-1803
Win10 18090x10Dwin10-1809
Win10 19030x10Ewin10-1903
Win10 19090x10Ewin10-1909
Win10 20040x113win10-2004
Win10 20H20x113win10-20h2
Win10 21H10x113win10-21h1
Win10 21H20x114win10-21h2
Win10 22H20x114win10-22h2
Win11 21H20x11Awin11-21h2
Win11 22H20x11Bwin11-22h2
Win11 23H20x11Bwin11-23h2
Win11 24H20x11Dwin11-24h2
Server 20160x106winserver-2016
Server 20190x10Dwinserver-2019
Server 20220x119winserver-2022
Server 20250x11Dwinserver-2025

Module noyau

ntoskrnl.exeNtMapUserPhysicalPages

APIs liées

MapUserPhysicalPagesMapUserPhysicalPagesScatterAllocateUserPhysicalPagesNtAllocateUserPhysicalPagesNtFreeUserPhysicalPagesVirtualAlloc

Stub du syscall

4C 8B D1                  mov r10, rcx
B8 1D 01 00 00            mov eax, 0x11D
F6 04 25 08 03 FE 7F 01   test byte ptr [0x7FFE0308], 1
75 03                     jne short +3
0F 05                     syscall
C3                        ret
CD 2E                     int 2Eh
C3                        ret

Notes non documentées

NtMapUserPhysicalPages lie les pages physiques obtenues via NtAllocateUserPhysicalPages dans la fenêtre virtuelle réservée par `VirtualAlloc(MEM_RESERVE | MEM_PHYSICAL)`. Passez NULL pour UserPfnArray afin de démapper toute la fenêtre en un appel — propriété exploitée par les moteurs de base de données pour une éviction de page rapide. Le remappage est essentiellement une opération purement TLB en régime stable : les PFN physiques sont épinglés, donc pas d'IO, pas d'ajustement de working-set, pas de soft fault. Ce profil de latence (sous-microseconde sur TLB chaud) est exactement ce qui rendait AWE attractif pour SQL Server 32 bits à l'époque où PAE était un contournement de la limite 2 Go.

Usage courant par les malwares

Deux schémas. Premièrement, **astuce d'aliasing de page** : un attaquant mappe la même page physique dans deux adresses virtuelles avec protections différentes (par ex. une fenêtre RW, une autre RX), obtenant une séparation stable écriture-XOR-exécution sans jamais appeler NtProtectVirtualMemory — contournant les EDR qui hookent les changements de protection. Deuxièmement, **mise en scène de payload résistante au swap** : les pages sont allouées AWE une fois et remappées via ce syscall à chaque rappel de beacon, gardant les octets du payload hors du fichier d'échange en permanence. Combiné à un driver vulnérable exposant des I/O mémoire physique, le même syscall peut greffer (après construction du bon handle PFN) le contenu d'une page noyau dans une fenêtre user-mode pour des primitives de lecture qui survivent entre appels. Le tradecraft requiert SeLockMemoryPrivilege en amont.

Opportunités de détection

Les fenêtres virtuelles MEM_PHYSICAL sont visibles dans `!vad` comme sous-type distinct et via `VirtualQueryEx` (Type contient `MEM_PHYSICAL`). Les EDR qui n'introspectent pas les détails VAD rateront le mapping, mais l'appel *amont* NtAllocateUserPhysicalPages (et l'ajustement `SeLockMemoryPrivilege` correspondant) est un déclencheur bien plus fiable. Chassez les processus qui détiennent à la fois une réservation VirtualAlloc MEM_PHYSICAL et appellent NtMapUserPhysicalPages en boucle — c'est le pattern de retournement de page d'une base de données ou d'un implant à aliasing de payload ; si le binaire n'est pas sqlservr.exe / exsetup.exe / oracle.exe, il mérite un examen plus profond. L'ETW Microsoft-Windows-Kernel-Memory n'expose pas directement les remaps AWE ; comptez sur les parcours VAD au moment du scan mémoire.

Exemples de syscalls directs

cPage-flip remap loop

// Rotate two batches of physical pages through the same virtual window —
// classic AWE pattern used by SQL Server for buffer-pool extension.
#include <windows.h>

void awe_flip(PVOID window, ULONG_PTR pages, PULONG_PTR pfnA, PULONG_PTR pfnB) {
    for (;;) {
        MapUserPhysicalPages(window, pages, pfnA);   // bring batch A into view
        // ... work on 'window' ...
        MapUserPhysicalPages(window, pages, NULL);   // unmap (NULL pfn array)
        MapUserPhysicalPages(window, pages, pfnB);   // bring batch B into view
        // ... work on 'window' ...
        MapUserPhysicalPages(window, pages, NULL);
    }
}

cW^X aliasing without NtProtectVirtualMemory

// Map the same physical pages twice: one writable window, one executable.
// EDR hooks on NtProtectVirtualMemory never fire because protection never changes.
#include <windows.h>

void* alias_wx(PULONG_PTR pfn, ULONG_PTR pages) {
    SIZE_T size = pages * 4096;
    PVOID wr = VirtualAlloc(NULL, size, MEM_RESERVE | MEM_PHYSICAL, PAGE_READWRITE);
    PVOID ex = VirtualAlloc(NULL, size, MEM_RESERVE | MEM_PHYSICAL, PAGE_EXECUTE_READ);
    MapUserPhysicalPages(wr, pages, pfn);
    MapUserPhysicalPages(ex, pages, pfn);
    // Write shellcode via 'wr', execute via 'ex' — same physical memory.
    return ex;
}

asmx64 direct stub (Win11 24H2, SSN 0x11D)

NtMapUserPhysicalPages PROC
    mov  r10, rcx
    mov  eax, 11Dh
    syscall
    ret
NtMapUserPhysicalPages ENDP

Mappings MITRE ATT&CK

Last verified: 2026-05-20