NtMapUserPhysicalPages
Mapea páginas físicas asignadas vía AWE en una ventana de direcciones virtuales previamente reservada.
Prototipo
NTSTATUS NtMapUserPhysicalPages( PVOID VirtualAddress, ULONG_PTR NumberOfPages, PULONG_PTR UserPfnArray );
Argumentos
| Name | Type | Dir | Description |
|---|---|---|---|
| VirtualAddress | PVOID | in | Base de una ventana de direcciones virtuales reservada con MEM_RESERVE | MEM_PHYSICAL. |
| NumberOfPages | ULONG_PTR | in | Número de páginas a mapear. No debe exceder el tamaño de la ventana reservada. |
| UserPfnArray | PULONG_PTR | in | Arreglo de identificadores de página opacos de NtAllocateUserPhysicalPages, o NULL para desmapear. |
IDs de syscalls por versión de Windows
| Versión de Windows | ID de syscall | Build |
|---|---|---|
| Win10 1507 | 0x101 | win10-1507 |
| Win10 1607 | 0x106 | win10-1607 |
| Win10 1703 | 0x10A | win10-1703 |
| Win10 1709 | 0x10B | win10-1709 |
| Win10 1803 | 0x10C | win10-1803 |
| Win10 1809 | 0x10D | win10-1809 |
| Win10 1903 | 0x10E | win10-1903 |
| Win10 1909 | 0x10E | win10-1909 |
| Win10 2004 | 0x113 | win10-2004 |
| Win10 20H2 | 0x113 | win10-20h2 |
| Win10 21H1 | 0x113 | win10-21h1 |
| Win10 21H2 | 0x114 | win10-21h2 |
| Win10 22H2 | 0x114 | win10-22h2 |
| Win11 21H2 | 0x11A | win11-21h2 |
| Win11 22H2 | 0x11B | win11-22h2 |
| Win11 23H2 | 0x11B | win11-23h2 |
| Win11 24H2 | 0x11D | win11-24h2 |
| Server 2016 | 0x106 | winserver-2016 |
| Server 2019 | 0x10D | winserver-2019 |
| Server 2022 | 0x119 | winserver-2022 |
| Server 2025 | 0x11D | winserver-2025 |
Módulo del kernel
APIs relacionadas
Stub del 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
Notas no documentadas
NtMapUserPhysicalPages enlaza páginas físicas obtenidas vía NtAllocateUserPhysicalPages en la ventana virtual reservada con `VirtualAlloc(MEM_RESERVE | MEM_PHYSICAL)`. Pase NULL como UserPfnArray para desmapear toda la ventana en una sola llamada — propiedad que los motores de base de datos explotan para desalojo rápido de páginas. El re-mapeo es esencialmente una operación solo de TLB en régimen estable: los PFN físicos están fijados, así que no hay IO, no hay ajuste de working-set, no hay soft fault. Ese perfil de latencia (sub-microsegundo en TLB caliente) es exactamente lo que hacía atractivo a AWE para SQL Server de 32 bits cuando PAE era el rodeo del límite de 2 GB.
Uso común por malware
Dos patrones. Primero, **truco de aliasing de página**: un atacante mapea la misma página física en dos direcciones virtuales con protecciones diferentes (p. ej. una ventana RW, otra RX), obteniendo una separación estable escritura-XOR-ejecución sin llamar nunca a NtProtectVirtualMemory — eludiendo a los EDR que hookean cambios de protección. Segundo, **staging de payload resistente al swap**: las páginas se asignan vía AWE una vez y se re-mapean vía este syscall en cada callback de beacon, manteniendo los bytes del payload fuera del archivo de paginación permanentemente. Combinado con un driver vulnerable que expone I/O de memoria física, el mismo syscall puede injertar (tras construir el handle PFN correcto) contenido de página de kernel en una ventana user-mode para primitivas de lectura que sobreviven entre llamadas. El tradecraft requiere SeLockMemoryPrivilege como puerta aguas arriba.
Oportunidades de detección
Las ventanas virtuales MEM_PHYSICAL son visibles en `!vad` como un subtipo distinto y vía `VirtualQueryEx` (Type contiene `MEM_PHYSICAL`). Los EDR que no inspeccionan detalles VAD perderán el mapeo, pero la llamada *aguas arriba* NtAllocateUserPhysicalPages (y el ajuste `SeLockMemoryPrivilege` correspondiente) es un disparador mucho más fiable. Cace procesos que mantengan a la vez una reserva VirtualAlloc MEM_PHYSICAL y llamen a NtMapUserPhysicalPages en bucle — ese es el patrón de cambio de página de una base de datos o un implante con aliasing de payload; si el binario no es sqlservr.exe / exsetup.exe / oracle.exe, merece una mirada más profunda. El ETW Microsoft-Windows-Kernel-Memory no expone directamente los remaps AWE; confíe en los recorridos VAD en el momento del escaneo de memoria.
Ejemplos de syscalls directos
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 ENDPMapeos MITRE ATT&CK
Last verified: 2026-05-20