> Windows Syscalls
ntoskrnl.exeT1574T1055T1106

NtSetInformationVirtualMemory

Applies an information class to a list of virtual-memory ranges: prefetch, page priority, or CFG call-target opt-in.

Prototype

NTSTATUS NtSetInformationVirtualMemory(
  HANDLE                          ProcessHandle,
  VIRTUAL_MEMORY_INFORMATION_CLASS VmInformationClass,
  ULONG_PTR                       NumberOfEntries,
  PMEMORY_RANGE_ENTRY             VirtualAddresses,
  PVOID                           VmInformation,
  ULONG                           VmInformationLength
);

Arguments

NameTypeDirDescription
ProcessHandleHANDLEinTarget process handle. NtCurrentProcess() ((HANDLE)-1) for self.
VmInformationClassVIRTUAL_MEMORY_INFORMATION_CLASSinOperation: VmPrefetchInformation, VmPagePriorityInformation, VmCfgCallTargetInformation, VmPageDirtyStateInformation, VmImageHotPatchInformation.
NumberOfEntriesULONG_PTRinCount of entries in VirtualAddresses. Several classes (notably CfgCallTarget) require exactly 1.
VirtualAddressesPMEMORY_RANGE_ENTRYinArray of {VirtualAddress, NumberOfBytes} ranges to act upon.
VmInformationPVOIDinClass-specific payload: ULONG flags for prefetch, MEMORY_PRIORITY_INFORMATION for priority, CFG_CALL_TARGET_INFO[] for CFG.
VmInformationLengthULONGinSize in bytes of the VmInformation payload.

Syscall IDs by Windows version

Windows versionSyscall IDBuild
Win10 15070x182win10-1507
Win10 16070x18Bwin10-1607
Win10 17030x191win10-1703
Win10 17090x194win10-1709
Win10 18030x196win10-1803
Win10 18090x197win10-1809
Win10 19030x198win10-1903
Win10 19090x198win10-1909
Win10 20040x19Ewin10-2004
Win10 20H20x19Ewin10-20h2
Win10 21H10x19Ewin10-21h1
Win10 21H20x1A0win10-21h2
Win10 22H20x1A0win10-22h2
Win11 21H20x1A9win11-21h2
Win11 22H20x1ADwin11-22h2
Win11 23H20x1ADwin11-23h2
Win11 24H20x1B0win11-24h2
Server 20160x18Bwinserver-2016
Server 20190x197winserver-2019
Server 20220x1A6winserver-2022
Server 20250x1B0winserver-2025

Kernel module

ntoskrnl.exeNtSetInformationVirtualMemory

Related APIs

PrefetchVirtualMemoryOfferVirtualMemorySetProcessValidCallTargetsVirtualAllocFromAppNtAllocateVirtualMemoryExNtProtectVirtualMemory

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

Undocumented notes

Introduced as part of the Windows 10 multi-range VM advisory API and substantially extended each release. The most operationally interesting information classes are: **VmPrefetchInformation** — warms a vector of address ranges before access (the kernel backbone of PrefetchVirtualMemory/OfferVirtualMemory); **VmPagePriorityInformation** — sets paging-priority hints; **VmCfgCallTargetInformation** — marks specific addresses inside a CFG-enabled module as valid indirect-call targets, the in-kernel mechanism behind SetProcessValidCallTargets and the only sanctioned way for a JIT to publish freshly-emitted code to a CFG-protected process; **VmImageHotPatchInformation** — used by HotPatch on Server 2022+ / 24H2.

Common malware usage

Two distinct abuse stories. (1) **CFG bypass research**: VmCfgCallTargetInformation is the legitimate door for telling CFG that an arbitrary address is now a valid call target. If an attacker who has already achieved arbitrary read/write in a CFG-protected process can call this — or can forge its kernel-side bitmap update — they can rehabilitate arbitrary gadgets as legitimate indirect-call destinations. Several public CFG-bypass writeups (Yarden Shafir's, j00ru's older notes) cover the surface. Most modern hardening pairs CFG with Xtended Flow Guard (XFG) and Arbitrary Code Guard precisely to neutralise this. (2) **JIT injection cover**: legitimate browsers and .NET use VmCfgCallTargetInformation constantly, so a payload-loader that mimics the same call shape (allocate executable, write code, call NtSetInformationVirtualMemory with VmCfgCallTargetInformation) blends into baseline telemetry inside a browser-class process. Less common but documented.

Detection opportunities

EDRs should flag VmCfgCallTargetInformation calls from processes that have no JIT (everything outside chrome.exe, msedge.exe, firefox.exe, dotnet.exe / coreclr-hosted apps, javaw.exe, JScript9.dll-hosting hosts). ETW Microsoft-Windows-Threat-Intelligence emits memory-state-change events when paired with executable-page transitions. Sysmon does not directly cover this syscall; rely on EDR sensor's syscall callback (Kernel-Microsoft-Antimalware-AMFilter on AMSI-instrumented systems). The Win32 surface (SetProcessValidCallTargets, PrefetchVirtualMemory) is hooked by every major EDR.

Direct syscall examples

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 ENDP

cJIT 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