NtSetInformationVirtualMemory
Aplica una clase de información a una lista de rangos de memoria virtual: prefetch, prioridad de página u opt-in CFG.
Prototipo
NTSTATUS NtSetInformationVirtualMemory( HANDLE ProcessHandle, VIRTUAL_MEMORY_INFORMATION_CLASS VmInformationClass, ULONG_PTR NumberOfEntries, PMEMORY_RANGE_ENTRY VirtualAddresses, PVOID VmInformation, ULONG VmInformationLength );
Argumentos
| Name | Type | Dir | Description |
|---|---|---|---|
| ProcessHandle | HANDLE | in | Handle del proceso objetivo. NtCurrentProcess() ((HANDLE)-1) para sí mismo. |
| VmInformationClass | VIRTUAL_MEMORY_INFORMATION_CLASS | in | Operación: VmPrefetchInformation, VmPagePriorityInformation, VmCfgCallTargetInformation, VmPageDirtyStateInformation, VmImageHotPatchInformation. |
| NumberOfEntries | ULONG_PTR | in | Cantidad de entradas en VirtualAddresses. Varias clases (notablemente CfgCallTarget) exigen exactamente 1. |
| VirtualAddresses | PMEMORY_RANGE_ENTRY | in | Arreglo de rangos {VirtualAddress, NumberOfBytes} sobre los que actuar. |
| VmInformation | PVOID | in | Carga específica de la clase: banderas ULONG para prefetch, MEMORY_PRIORITY_INFORMATION para prioridad, CFG_CALL_TARGET_INFO[] para CFG. |
| VmInformationLength | ULONG | in | Tamaño en bytes de la carga útil VmInformation. |
IDs de syscalls por versión de Windows
| Versión de Windows | ID de syscall | Build |
|---|---|---|
| Win10 1507 | 0x182 | win10-1507 |
| Win10 1607 | 0x18B | win10-1607 |
| Win10 1703 | 0x191 | win10-1703 |
| Win10 1709 | 0x194 | win10-1709 |
| Win10 1803 | 0x196 | win10-1803 |
| Win10 1809 | 0x197 | win10-1809 |
| Win10 1903 | 0x198 | win10-1903 |
| Win10 1909 | 0x198 | win10-1909 |
| Win10 2004 | 0x19E | win10-2004 |
| Win10 20H2 | 0x19E | win10-20h2 |
| Win10 21H1 | 0x19E | win10-21h1 |
| Win10 21H2 | 0x1A0 | win10-21h2 |
| Win10 22H2 | 0x1A0 | win10-22h2 |
| Win11 21H2 | 0x1A9 | win11-21h2 |
| Win11 22H2 | 0x1AD | win11-22h2 |
| Win11 23H2 | 0x1AD | win11-23h2 |
| Win11 24H2 | 0x1B0 | win11-24h2 |
| Server 2016 | 0x18B | winserver-2016 |
| Server 2019 | 0x197 | winserver-2019 |
| Server 2022 | 0x1A6 | winserver-2022 |
| Server 2025 | 0x1B0 | winserver-2025 |
Módulo del kernel
APIs relacionadas
Stub del syscall
4C 8B D1 mov r10, rcx B8 B0 01 00 00 mov eax, 0x1B0 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
Introducido como parte de la API de aviso VM multi-rango de Windows 10 y extendido sustancialmente con cada release. Las clases de información operativamente más interesantes son: **VmPrefetchInformation** — calienta un vector de rangos de direcciones antes del acceso (la columna vertebral del kernel de PrefetchVirtualMemory/OfferVirtualMemory); **VmPagePriorityInformation** — establece hints de prioridad de paginación; **VmCfgCallTargetInformation** — marca direcciones específicas dentro de un módulo con CFG como destinos válidos de llamada indirecta, el mecanismo en kernel detrás de SetProcessValidCallTargets y la única vía sancionada para que un JIT publique código recién emitido en un proceso protegido por CFG; **VmImageHotPatchInformation** — usado por HotPatch en Server 2022+ / 24H2.
Uso común por malware
Dos historias de abuso distintas. (1) **Investigación de bypass CFG**: VmCfgCallTargetInformation es la puerta legítima para decirle a CFG que una dirección arbitraria es ahora un destino válido. Si un atacante que ya logró read/write arbitrario en un proceso protegido por CFG puede llamarlo — o falsificar la actualización del bitmap en lado del kernel — puede rehabilitar gadgets arbitrarios como destinos legítimos de llamada indirecta. Varios writeups públicos de bypass CFG (Yarden Shafir, notas antiguas de j00ru) cubren la superficie. El endurecimiento moderno empareja CFG con Xtended Flow Guard (XFG) y Arbitrary Code Guard precisamente para neutralizar esto. (2) **Cobertura de inyección JIT**: los navegadores y .NET legítimos usan VmCfgCallTargetInformation constantemente, así que un loader de payload que imita la misma forma de llamada (allocate ejecutable, escribir código, llamar NtSetInformationVirtualMemory con VmCfgCallTargetInformation) se mezcla con la telemetría de referencia dentro de un proceso clase navegador. Menos común pero documentado.
Oportunidades de detección
Los EDR deberían marcar llamadas VmCfgCallTargetInformation desde procesos sin JIT (cualquier cosa fuera de chrome.exe, msedge.exe, firefox.exe, dotnet.exe / apps hospedadas en coreclr, javaw.exe, hosts con JScript9.dll). ETW Microsoft-Windows-Threat-Intelligence emite eventos de cambio de estado de memoria al emparejarse con transiciones de páginas ejecutables. Sysmon no cubre directamente esta syscall; depender del callback de syscall del sensor EDR (Kernel-Microsoft-Antimalware-AMFilter en sistemas con AMSI instrumentado). La superficie Win32 (SetProcessValidCallTargets, PrefetchVirtualMemory) está hookeada por todos los EDR principales.
Ejemplos de syscalls directos
asmx64 direct stub (Win11 24H2)
; Direct syscall stub for NtSetInformationVirtualMemory (SSN 0x1B0, Win11 24H2)
NtSetInformationVirtualMemory PROC
mov r10, rcx
mov eax, 1B0h
syscall
ret
NtSetInformationVirtualMemory ENDPcJIT opt-in for CFG (VmCfgCallTargetInformation)
// JIT just emitted callable code at `jit_buf` (size = jit_len). Tell CFG it's legal
// to indirect-call into the new entrypoints. Mirrors what V8 / CoreCLR do.
typedef struct _CFG_CALL_TARGET_INFO {
ULONG_PTR Offset;
ULONG_PTR Flags;
} CFG_CALL_TARGET_INFO;
typedef struct _MEMORY_RANGE_ENTRY {
PVOID VirtualAddress;
SIZE_T NumberOfBytes;
} MEMORY_RANGE_ENTRY;
#define VmCfgCallTargetInformation 2
#define CFG_CALL_TARGET_VALID 0x01
MEMORY_RANGE_ENTRY range = { jit_buf, jit_len };
CFG_CALL_TARGET_INFO targets[] = {
{ (ULONG_PTR)entry0 - (ULONG_PTR)jit_buf, CFG_CALL_TARGET_VALID },
{ (ULONG_PTR)entry1 - (ULONG_PTR)jit_buf, CFG_CALL_TARGET_VALID },
};
NTSTATUS s = NtSetInformationVirtualMemory(
NtCurrentProcess(),
VmCfgCallTargetInformation,
1,
&range,
targets,
sizeof(targets));rustPrefetchVirtualMemory equivalent via VmPrefetchInformation
// Warm a list of memory ranges before the latency-sensitive work touches them.
// VmPrefetchInformation = 0.
#[repr(C)]
struct MemoryRangeEntry { addr: *mut u8, bytes: usize }
extern "system" {
fn NtSetInformationVirtualMemory(
process: isize,
class: u32,
n: usize,
ranges: *const MemoryRangeEntry,
info: *const u32,
info_len: u32,
) -> i32;
}
unsafe fn prefetch(ranges: &[MemoryRangeEntry]) -> i32 {
let flags: u32 = 0;
NtSetInformationVirtualMemory(
-1, 0, ranges.len(), ranges.as_ptr(),
&flags, std::mem::size_of::<u32>() as u32)
}Mapeos MITRE ATT&CK
Last verified: 2026-05-20