NtAllocateVirtualMemory
Reserves, commits or both a region of virtual memory in a target process.
Prototype
NTSTATUS NtAllocateVirtualMemory( HANDLE ProcessHandle, PVOID *BaseAddress, ULONG_PTR ZeroBits, PSIZE_T RegionSize, ULONG AllocationType, ULONG Protect );
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. |
| ZeroBits | ULONG_PTR | in | Number of high-order zero bits in BaseAddress. Typically 0. |
| 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 is the most common combination. |
| Protect | ULONG | in | Memory protection constant, e.g. PAGE_READWRITE, PAGE_EXECUTE_READWRITE. |
Syscall IDs by Windows version
| Windows version | Syscall ID | Build |
|---|---|---|
| Win10 1909 | 0x18 | win10-1909 |
| Win10 2004 | 0x18 | win10-2004 |
| Win10 20H2 | 0x18 | win10-20h2 |
| Win10 21H1 | 0x18 | win10-21h1 |
| Win10 21H2 | 0x18 | win10-21h2 |
| Win10 22H2 | 0x18 | win10-22h2 |
| Win11 21H2 | 0x18 | win11-21h2 |
| Win11 22H2 | 0x18 | win11-22h2 |
| Win11 23H2 | 0x18 | win11-23h2 |
| Win11 24H2 | 0x18 | win11-24h2 |
| Server 2019 | 0x18 | winserver-2019 |
| Server 2022 | 0x18 | winserver-2022 |
| Server 2025 | 0x18 | winserver-2025 |
Kernel module
Related APIs
Syscall stub
4C 8B D1 mov r10, rcx B8 18 00 00 00 mov eax, 0x18 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
The syscall number for NtAllocateVirtualMemory has been remarkably stable across Windows 10 1909 through Windows 11 24H2 — `0x18`. This is *not* the case for many adjacent syscalls (e.g. NtCreateThreadEx), which is exactly why dynamic resolution via Hell's Gate or Halo's Gate is preferred over hardcoded numbers when targeting multiple builds. The function dispatches through KiSystemServiceCopyEnd → MmAllocateVirtualMemory inside ntoskrnl.exe.
Common malware usage
Foundational for almost every shellcode loader: allocate RW or RWX memory in the current or a remote process, copy payload, then transition protection with NtProtectVirtualMemory. Used by Cobalt Strike beacon spawn, Sliver implants, classic process injection, and most off-the-shelf droppers. Combined with NtWriteVirtualMemory + NtCreateThreadEx it forms the canonical CreateRemoteThread-equivalent direct-syscall chain.
Detection opportunities
On its own, NtAllocateVirtualMemory is *extremely* common in legitimate software and not a useful signal. What matters is *how* and *where* it is called: RWX allocations, allocations in remote processes, and short-lived RW→RX→Execute sequences. EDRs hook NtAllocateVirtualMemory in ntdll.dll; direct syscalls bypass that hook but leave kernel-visible artifacts (VAD entries, ETW Threat Intelligence events, PsSetCreateProcessNotifyRoutineEx callbacks for remote thread creation). Sysmon Event ID 8 (CreateRemoteThread) and Event ID 10 (ProcessAccess with VM_OPERATION/VM_WRITE) are the most useful blue-team telemetry — they fire on the *consequences* of the allocation, not the allocation itself.
Direct syscall examples
asmx64 direct stub
; Direct syscall stub for NtAllocateVirtualMemory (SSN 0x18, Win10 1909+)
NtAllocateVirtualMemory PROC
mov r10, rcx ; syscall convention
mov eax, 18h ; SSN
syscall
ret
NtAllocateVirtualMemory ENDPcHell's Gate dynamic lookup
// Resolve SSN dynamically from ntdll export at runtime — survives KB updates.
DWORD GetSyscallNumber(PVOID pNtFunction) {
BYTE* p = (BYTE*)pNtFunction;
if (p[0] == 0x4C && p[1] == 0x8B && p[2] == 0xD1 && p[3] == 0xB8) {
return *(DWORD*)(p + 4);
}
return 0;
}
// usage
HMODULE hNtdll = GetModuleHandleA("ntdll.dll");
DWORD ssn = GetSyscallNumber(GetProcAddress(hNtdll, "NtAllocateVirtualMemory"));rustwindows-sys + naked asm
// Cargo: windows-sys = "0.59" (Win32_Foundation, Win32_System_Memory)
use std::arch::asm;
#[unsafe(naked)]
unsafe extern "system" fn nt_alloc_virtual_memory_stub() {
asm!(
"mov r10, rcx",
"mov eax, 0x18",
"syscall",
"ret",
options(noreturn),
);
}MITRE ATT&CK mappings
- T1055Process Injection
- T1055.002Portable Executable Injection
- T1620Reflective Code Loading
Last verified: 2026-05-20