NtFreeUserPhysicalPages
Libera páginas físicas previamente asignadas vía NtAllocateUserPhysicalPages, devolviéndolas al pool del sistema.
Prototipo
NTSTATUS NtFreeUserPhysicalPages( HANDLE ProcessHandle, PULONG_PTR NumberOfPages, PULONG_PTR UserPfnArray );
Argumentos
| Name | Type | Dir | Description |
|---|---|---|---|
| ProcessHandle | HANDLE | in | Handle al proceso que posee el arreglo PFN AWE. Habitualmente NtCurrentProcess(). |
| NumberOfPages | PULONG_PTR | in/out | En entrada: número de páginas a liberar. En salida: número realmente liberado. |
| UserPfnArray | PULONG_PTR | in | Arreglo de identificadores PFN opacos obtenidos de NtAllocateUserPhysicalPages. |
IDs de syscalls por versión de Windows
| Versión de Windows | ID de syscall | Build |
|---|---|---|
| Win10 1507 | 0xDE | win10-1507 |
| Win10 1607 | 0xE1 | win10-1607 |
| Win10 1703 | 0xE4 | win10-1703 |
| Win10 1709 | 0xE5 | win10-1709 |
| Win10 1803 | 0xE6 | win10-1803 |
| Win10 1809 | 0xE7 | win10-1809 |
| Win10 1903 | 0xE8 | win10-1903 |
| Win10 1909 | 0xE8 | win10-1909 |
| Win10 2004 | 0xED | win10-2004 |
| Win10 20H2 | 0xED | win10-20h2 |
| Win10 21H1 | 0xED | win10-21h1 |
| Win10 21H2 | 0xEE | win10-21h2 |
| Win10 22H2 | 0xEE | win10-22h2 |
| Win11 21H2 | 0xF3 | win11-21h2 |
| Win11 22H2 | 0xF4 | win11-22h2 |
| Win11 23H2 | 0xF4 | win11-23h2 |
| Win11 24H2 | 0xF6 | win11-24h2 |
| Server 2016 | 0xE1 | winserver-2016 |
| Server 2019 | 0xE7 | winserver-2019 |
| Server 2022 | 0xF2 | winserver-2022 |
| Server 2025 | 0xF6 | winserver-2025 |
Módulo del kernel
APIs relacionadas
Stub del syscall
4C 8B D1 mov r10, rcx B8 F6 00 00 00 mov eax, 0xF6 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
La contraparte de limpieza de `NtAllocateUserPhysicalPages`. Recorre el arreglo PFN, decrementa el contador de referencia de cada página en la base PFN del sistema y devuelve las páginas a las listas standby / free. Las páginas todavía mapeadas vía `NtMapUserPhysicalPages` se desmapean primero de la ventana de espacio de direcciones del llamante. Como su gemelo allocator, la llamada requiere que el proceso o bien posea actualmente `SeLockMemoryPrivilege` o lo poseyera al momento de la asignación — el kernel registra el token de origen en el momento de la asignación y lo usa para verificaciones de propiedad. Llamar `NtFreeUserPhysicalPages` sobre valores PFN que no provienen del mismo allocator devuelve `STATUS_INVALID_PARAMETER_3` sin tocar el estado del sistema.
Uso común por malware
Valor ofensivo directo mínimo — es puramente el destructor de un camino de asignación ya raro. Donde aparece es en la **limpieza anti-forense**: implantes que stagearon shellcode en páginas AWE no-paginables (para sobrevivir capturas de swap por presión de memoria) llaman `NtFreeUserPhysicalPages` justo antes de la salida del implante, para que un análisis post-incidente de `vmcore` o de archivo de hibernación no vea ninguna región no-paginable residual atada al proceso muerto. También aparece en cadenas de **desmontaje de primitiva R/W de kernel** donde el atacante asigna páginas AWE para aliasar memoria del kernel vía un driver vulnerable y luego las libera para borrar el único rastro forense persistente. El malware commodity prácticamente nunca lo toca.
Oportunidades de detección
Espejo de la señal del allocator — llamadas pareadas de `NtAllocateUserPhysicalPages` y `NtFreeUserPhysicalPages` desde un mismo proceso no servidor-de-base-de-datos son el indicador alta-fidelidad, especialmente cuando el tiempo de vida es corto (asignar → mapear → ejecutar → liberar en el mismo segundo). Sysmon Event ID 1 para el proceso padre combinado con el evento ETW de kernel `Microsoft-Windows-Kernel-Memory` 14 (familia `PfnPageRelease`) permite reconstruir el ciclo de vida. La auditoría de `SeLockMemoryPrivilege` (Event 4673) suele dispararse solo en el camino de asignación; si ve un proceso liberando cuando el log de auditoría no tiene una concesión correspondiente, el privilegio ya estaba habilitado en el token padre — investigue de dónde vino.
Ejemplos de syscalls directos
cFree AWE pages and the windowing region
// Pair with the NtAllocateUserPhysicalPages example: tear down the AWE working
// set so that the shellcode-bearing physical pages are returned to the free list
// before the implant exits.
#include <windows.h>
void awe_free(ULONG_PTR* pfn, SIZE_T pages, PVOID windowBase) {
// First unmap from the window so no live VAD references the PFNs.
MapUserPhysicalPages(windowBase, pages, NULL);
VirtualFree(windowBase, 0, MEM_RELEASE);
ULONG_PTR n = pages;
FreeUserPhysicalPages(GetCurrentProcess(), &n, pfn);
// Wrapper resolves to NtFreeUserPhysicalPages internally.
}asmx64 direct stub (Win11 24H2 / Server 2025, SSN 0xF6)
NtFreeUserPhysicalPages PROC
mov r10, rcx
mov eax, 0F6h
syscall
ret
NtFreeUserPhysicalPages ENDPMapeos MITRE ATT&CK
Last verified: 2026-05-20