> Windows Syscalls
ntoskrnl.exeT1055T1055.002T1055.012

NtWriteVirtualMemory

Écrit un tampon de l'appelant dans l'espace d'adressage virtuel d'un processus cible.

Prototype

NTSTATUS NtWriteVirtualMemory(
  HANDLE  ProcessHandle,
  PVOID   BaseAddress,
  PVOID   Buffer,
  SIZE_T  NumberOfBytesToWrite,
  PSIZE_T NumberOfBytesWritten
);

Arguments

NameTypeDirDescription
ProcessHandleHANDLEinHandle vers le processus cible avec les droits PROCESS_VM_WRITE | PROCESS_VM_OPERATION.
BaseAddressPVOIDinAdresse virtuelle de destination dans le processus cible.
BufferPVOIDinPointeur vers le tampon source dans l'espace d'adressage de l'appelant.
NumberOfBytesToWriteSIZE_TinNombre d'octets à copier depuis Buffer vers BaseAddress.
NumberOfBytesWrittenPSIZE_ToutOptionnel. Reçoit le nombre d'octets effectivement écrits. Peut être NULL.

IDs de syscalls par version de Windows

Version de WindowsID de syscallBuild
Win10 15070x3Awin10-1507
Win10 16070x3Awin10-1607
Win10 17030x3Awin10-1703
Win10 17090x3Awin10-1709
Win10 18030x3Awin10-1803
Win10 18090x3Awin10-1809
Win10 19030x3Awin10-1903
Win10 19090x3Awin10-1909
Win10 20040x3Awin10-2004
Win10 20H20x3Awin10-20h2
Win10 21H10x3Awin10-21h1
Win10 21H20x3Awin10-21h2
Win10 22H20x3Awin10-22h2
Win11 21H20x3Awin11-21h2
Win11 22H20x3Awin11-22h2
Win11 23H20x3Awin11-23h2
Win11 24H20x3Awin11-24h2
Server 20160x3Awinserver-2016
Server 20190x3Awinserver-2019
Server 20220x3Awinserver-2022
Server 20250x3Awinserver-2025

Module noyau

ntoskrnl.exeNtWriteVirtualMemory

APIs liées

WriteProcessMemoryNtReadVirtualMemoryNtAllocateVirtualMemoryNtProtectVirtualMemoryZwWriteVirtualMemory

Stub du syscall

4C 8B D1            mov r10, rcx
B8 3A 00 00 00      mov eax, 0x3A
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

Notes non documentées

Implémenté par `MmCopyVirtualMemory` dans ntoskrnl.exe avec `PreviousMode = UserMode`, ce qui force un probe-and-lock complet des deux tampons. Le SSN `0x3A` est resté inchangé sur chaque build Win10/11 livré — la disposition de la table n'a tout simplement pas bougé pour cette entrée. Le noyau effectue la copie via un mapping transitoire ; il ne requiert pas que la page cible soit accessible en écriture depuis l'userland, seulement que les permissions de section autorisent l'écriture côté noyau. C'est pourquoi l'injection dans des pages en lecture seule réussit souvent.

Usage courant par les malwares

L'étape `Write` de la chaîne classique OpenProcess → VirtualAllocEx → WriteProcessMemory → CreateRemoteThread. Utilisée pour déposer du shellcode, un PE avec IAT déjà résolue ou un stub de loader réflexif dans un processus distant. Au-delà de l'injection de shellcode, elle est aussi détournée pour réécrire des prologues de fonction afin d'installer des inline hooks, patcher `AMSI.dll!AmsiScanBuffer` ou `clr.jit!compileMethod` pour bypasser AMSI/ETW, et comme primitive d'écriture dans les variantes de PPID-spoofing et de process doppelgänging. Brute Ratel, Cobalt Strike, Sliver, Qakbot et IcedID en dépendent tous dans leur chaîne de loader.

Opportunités de détection

Les écritures inter-processus sont principalement remontées par Sysmon Event ID 10 (`ProcessAccess`) lorsque le masque d'accès contient `PROCESS_VM_WRITE` (0x0020) ou `PROCESS_VM_OPERATION` (0x0008). Le fournisseur ETW Threat Intelligence émet aussi des notifications d'écriture pour certains processus protégés. Les EDR hookent `NtWriteVirtualMemory` dans ntdll ; les syscalls directs contournent le hook, mais le noyau met toujours à jour les compteurs de référence VAD cibles et peut être observé via les callbacks `PsSetCreateProcessNotifyRoutineEx` si l'écriture déclenche une création de thread ultérieure. Les écritures visant des processus d'une autre session, ou ciblant `lsass.exe` ou des binaires de navigateur, méritent un score élevé.

Exemples de syscalls directs

asmx64 direct stub

; Direct syscall stub for NtWriteVirtualMemory (SSN 0x3A, stable across all Win10/11)
NtWriteVirtualMemory PROC
    mov  r10, rcx          ; syscall convention
    mov  eax, 3Ah          ; SSN
    syscall
    ret
NtWriteVirtualMemory ENDP

cRemote shellcode injection

// Write shellcode into a previously-allocated remote region.
SIZE_T written = 0;
NTSTATUS st = NtWriteVirtualMemory(hRemote, remote_base,
                                   shellcode, shellcode_len, &written);
if (!NT_SUCCESS(st) || written != shellcode_len) {
    // partial write: typically PAGE_NOACCESS encountered halfway
    return st;
}

rustHell's Gate indirect syscall

// Cargo: ntapi = "0.4"
use std::arch::asm;

#[unsafe(naked)]
unsafe extern "system" fn nt_write_virtual_memory_stub() {
    // SSN 0x3A across every supported build.
    asm!(
        "mov r10, rcx",
        "mov eax, 0x3A",
        "syscall",
        "ret",
        options(noreturn),
    );
}

Mappings MITRE ATT&CK

Last verified: 2026-05-20