NtSetInformationVirtualMemory
Wendet eine Informationsklasse auf eine Liste virtueller Speicherbereiche an: Prefetch, Seitenpriorität oder CFG-Opt-in.
Prototyp
NTSTATUS NtSetInformationVirtualMemory( HANDLE ProcessHandle, VIRTUAL_MEMORY_INFORMATION_CLASS VmInformationClass, ULONG_PTR NumberOfEntries, PMEMORY_RANGE_ENTRY VirtualAddresses, PVOID VmInformation, ULONG VmInformationLength );
Argumente
| Name | Type | Dir | Description |
|---|---|---|---|
| ProcessHandle | HANDLE | in | Handle des Zielprozesses. NtCurrentProcess() ((HANDLE)-1) für den eigenen Prozess. |
| VmInformationClass | VIRTUAL_MEMORY_INFORMATION_CLASS | in | Operation: VmPrefetchInformation, VmPagePriorityInformation, VmCfgCallTargetInformation, VmPageDirtyStateInformation, VmImageHotPatchInformation. |
| NumberOfEntries | ULONG_PTR | in | Anzahl der Einträge in VirtualAddresses. Mehrere Klassen (insbesondere CfgCallTarget) verlangen genau 1. |
| VirtualAddresses | PMEMORY_RANGE_ENTRY | in | Array von {VirtualAddress, NumberOfBytes}-Bereichen, auf die gewirkt werden soll. |
| VmInformation | PVOID | in | Klassenspezifische Nutzlast: ULONG-Flags für Prefetch, MEMORY_PRIORITY_INFORMATION für Priorität, CFG_CALL_TARGET_INFO[] für CFG. |
| VmInformationLength | ULONG | in | Größe der VmInformation-Nutzlast in Bytes. |
Syscall-IDs pro Windows-Version
| Windows-Version | Syscall-ID | 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 |
Kernel-Modul
Verwandte APIs
Syscall-Stub
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
Undokumentierte Hinweise
Eingeführt als Teil der Multi-Range-VM-Advisory-API von Windows 10 und in jedem Release substanziell erweitert. Die operativ interessantesten Informationsklassen sind: **VmPrefetchInformation** — wärmt einen Vektor von Adressbereichen vor dem Zugriff (das Kernel-Rückgrat von PrefetchVirtualMemory/OfferVirtualMemory); **VmPagePriorityInformation** — setzt Paging-Prioritätshinweise; **VmCfgCallTargetInformation** — markiert spezifische Adressen in einem CFG-aktivierten Modul als gültige indirekte Aufrufziele, der kernelseitige Mechanismus hinter SetProcessValidCallTargets und der einzige sanktionierte Weg, wie ein JIT frisch emittierten Code in einem CFG-geschützten Prozess veröffentlichen kann; **VmImageHotPatchInformation** — wird von HotPatch auf Server 2022+ / 24H2 verwendet.
Häufige Malware-Nutzung
Zwei unterschiedliche Missbrauchsgeschichten. (1) **CFG-Bypass-Forschung**: VmCfgCallTargetInformation ist die legitime Tür, um CFG mitzuteilen, dass eine beliebige Adresse jetzt ein gültiges Aufrufziel ist. Wenn ein Angreifer, der in einem CFG-geschützten Prozess bereits Arbitrary-Read/Write hat, diesen Aufruf ausführen kann — oder das kernelseitige Bitmap-Update fälschen kann — kann er beliebige Gadgets als legitime indirekte Aufrufziele rehabilitieren. Mehrere öffentliche CFG-Bypass-Writeups (Yarden Shafir, j00rus ältere Notizen) decken die Oberfläche ab. Modernes Hardening kombiniert CFG mit Xtended Flow Guard (XFG) und Arbitrary Code Guard genau dafür. (2) **JIT-Injection-Tarnung**: legitime Browser und .NET nutzen VmCfgCallTargetInformation ständig, sodass ein Payload-Loader, der die gleiche Aufrufform imitiert (ausführbare Allocation, Code schreiben, NtSetInformationVirtualMemory mit VmCfgCallTargetInformation aufrufen), in Baseline-Telemetrie innerhalb eines Browser-Klasse-Prozesses untergeht. Seltener, aber dokumentiert.
Erkennungsmöglichkeiten
EDRs sollten VmCfgCallTargetInformation-Aufrufe aus Prozessen ohne JIT markieren (alles außerhalb chrome.exe, msedge.exe, firefox.exe, dotnet.exe / coreclr-gehostete Apps, javaw.exe, JScript9.dll-Hosts). ETW Microsoft-Windows-Threat-Intelligence emittiert Memory-State-Change-Events, wenn sie mit Übergängen ausführbarer Seiten gekoppelt sind. Sysmon deckt diesen Syscall nicht direkt ab; sich auf den Syscall-Callback des EDR-Sensors verlassen (Kernel-Microsoft-Antimalware-AMFilter auf AMSI-instrumentierten Systemen). Die Win32-Oberfläche (SetProcessValidCallTargets, PrefetchVirtualMemory) wird von jedem großen EDR gehakt.
Direkte Syscall-Beispiele
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)
}MITRE ATT&CK-Mappings
Last verified: 2026-05-20