NtAllocateVirtualMemoryEx
Reserves or commits virtual memory with extended parameters (preferred NUMA node, CFG, address requirements).
Prototype
NTSTATUS NtAllocateVirtualMemoryEx( HANDLE ProcessHandle, PVOID *BaseAddress, PSIZE_T RegionSize, ULONG AllocationType, ULONG PageProtection, PMEM_EXTENDED_PARAMETER ExtendedParameters, ULONG ExtendedParameterCount );
Arguments
| Name | Type | Dir | Description |
|---|---|---|---|
| ProcessHandle | HANDLE | in | Handle to the target process. Use NtCurrentProcess() ((HANDLE)-1) for self. |
| BaseAddress | PVOID* | in/out | Pointer to the requested base address. NULL lets the kernel choose. Updated on return. |
| RegionSize | PSIZE_T | in/out | Pointer to the desired size, rounded up to a page boundary on return. |
| AllocationType | ULONG | in | Allocation flags. MEM_COMMIT | MEM_RESERVE most common; MEM_RESERVE_PLACEHOLDER and MEM_REPLACE_PLACEHOLDER supported. |
| PageProtection | ULONG | in | Memory protection constant. PAGE_TARGETS_INVALID can be OR-ed to bypass CFG for the allocated pages. |
| ExtendedParameters | PMEM_EXTENDED_PARAMETER | in | Optional array of MEM_EXTENDED_PARAMETER entries (NUMA node, address requirements, attribute flags). |
| ExtendedParameterCount | ULONG | in | Number of entries in ExtendedParameters, or 0 if not used. |
Syscall IDs by Windows version
| Windows version | Syscall ID | Build |
|---|---|---|
| Win10 1803 | 0x74 | win10-1803 |
| Win10 1809 | 0x74 | win10-1809 |
| Win10 1903 | 0x74 | win10-1903 |
| Win10 1909 | 0x74 | win10-1909 |
| Win10 2004 | 0x76 | win10-2004 |
| Win10 20H2 | 0x76 | win10-20h2 |
| Win10 21H1 | 0x76 | win10-21h1 |
| Win10 21H2 | 0x76 | win10-21h2 |
| Win10 22H2 | 0x76 | win10-22h2 |
| Win11 21H2 | 0x76 | win11-21h2 |
| Win11 22H2 | 0x76 | win11-22h2 |
| Win11 23H2 | 0x76 | win11-23h2 |
| Win11 24H2 | 0x78 | win11-24h2 |
| Server 2019 | 0x74 | winserver-2019 |
| Server 2022 | 0x76 | winserver-2022 |
| Server 2025 | 0x78 | winserver-2025 |
Kernel module
Related APIs
Syscall stub
4C 8B D1 mov r10, rcx B8 78 00 00 00 mov eax, 0x78 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
NtAllocateVirtualMemoryEx was introduced in Windows 10 1803 to back the new VirtualAlloc2 / VirtualAlloc2FromApp APIs. Its SSN has shifted three times within the supported window — 0x74 on RS4–19H2, 0x76 from 20H1 through 23H2, and 0x78 on 24H2 / Server 2025 — so dynamic resolution (Hell's Gate / Halo's Gate / Tartarus' Gate) is mandatory when targeting more than one channel. Compared with NtAllocateVirtualMemory it adds the MEM_EXTENDED_PARAMETER array and supports placeholder allocations (used by Windows for sparse mappings and memfd-style file mappings).
Common malware usage
Increasingly preferred over the classic NtAllocateVirtualMemory for three reasons. (1) **Less-hooked surface**: many EDRs only patch NtAllocateVirtualMemory, leaving the Ex variant uninstrumented; recent BumbleBee, IcedID, and Latrodectus loaders explicitly call the Ex path for this reason. (2) **PAGE_TARGETS_INVALID** marks the new region as not-a-valid-indirect-call-target under CFG, then the loader switches it to PAGE_EXECUTE_READ; this is the documented way to bypass CFG when the host enables it. (3) **MEM_EXTENDED_PARAMETER with MemExtendedParameterAddressRequirements** lets the implant request an allocation at a precise high-entropy address, useful for spoofing module-like base addresses in remote processes.
Detection opportunities
Cross-process allocations via the Ex path with non-zero ExtendedParameterCount are uncommon outside graphics drivers and AppContainer brokers — alertable. ETW Threat Intelligence's `EtwTiLogAllocExecVm` callback fires on the Ex path as well as the classic call. Sysmon does not natively distinguish the two; correlation with NtWriteVirtualMemory / NtCreateThreadEx remains the cheapest behavioural signal. EDR vendors that hook only NtAllocateVirtualMemory will miss this entirely — verify hook coverage on both names during purple-team work.
Direct syscall examples
asmx64 direct stub (Win11 24H2)
; Direct syscall stub for NtAllocateVirtualMemoryEx (SSN 0x78 on 24H2/2025)
NtAllocateVirtualMemoryEx PROC
mov r10, rcx ; syscall convention
mov eax, 78h ; SSN — RESOLVE DYNAMICALLY for multi-build targeting
syscall
ret
NtAllocateVirtualMemoryEx ENDPcCFG bypass via PAGE_TARGETS_INVALID
// Allocate RWX page that CFG will not accept as a valid indirect call target.
// Then flip to RX before calling — defeats CFG-only mitigations.
#include <windows.h>
typedef NTSTATUS (NTAPI *pNtAllocateVirtualMemoryEx)(
HANDLE, PVOID*, PSIZE_T, ULONG, ULONG, PMEM_EXTENDED_PARAMETER, ULONG);
PVOID base = NULL;
SIZE_T size = 0x1000;
pNtAllocateVirtualMemoryEx Fn = (pNtAllocateVirtualMemoryEx)
GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtAllocateVirtualMemoryEx");
Fn((HANDLE)-1, &base, &size,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE | PAGE_TARGETS_INVALID,
NULL, 0);rustVirtualAlloc2 wrapper
// Cargo: windows-sys = "0.59" (Win32_System_Memory)
use windows_sys::Win32::System::Memory::{
VirtualAlloc2, MEM_COMMIT, MEM_RESERVE, PAGE_EXECUTE_READWRITE,
};
pub unsafe fn alloc_rwx(size: usize) -> *mut core::ffi::c_void {
VirtualAlloc2(0, core::ptr::null_mut(), size,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE,
core::ptr::null_mut(), 0)
}MITRE ATT&CK mappings
Last verified: 2026-05-20