> Windows Syscalls
ntoskrnl.exeT1055T1055.002T1055.012

NtWriteVirtualMemory

Escribe un búfer del invocador en el espacio de direcciones virtuales de un proceso objetivo.

Prototipo

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

Argumentos

NameTypeDirDescription
ProcessHandleHANDLEinHandle al proceso objetivo con derechos PROCESS_VM_WRITE | PROCESS_VM_OPERATION.
BaseAddressPVOIDinDirección virtual de destino en el proceso objetivo.
BufferPVOIDinPuntero al búfer fuente en el espacio de direcciones del invocador.
NumberOfBytesToWriteSIZE_TinNúmero de bytes a copiar desde Buffer hacia BaseAddress.
NumberOfBytesWrittenPSIZE_ToutOpcional. Recibe el número de bytes realmente escritos. Puede ser NULL.

IDs de syscalls por versión de Windows

Versión 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

Módulo del kernel

ntoskrnl.exeNtWriteVirtualMemory

APIs relacionadas

WriteProcessMemoryNtReadVirtualMemoryNtAllocateVirtualMemoryNtProtectVirtualMemoryZwWriteVirtualMemory

Stub del 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

Notas no documentadas

Implementado por `MmCopyVirtualMemory` en ntoskrnl.exe con `PreviousMode = UserMode`, lo que fuerza un probe-and-lock completo de ambos búferes. La SSN `0x3A` ha permanecido sin cambios en cada build Win10/11 lanzada — la disposición de la tabla simplemente no ha movido esta entrada. El kernel realiza la copia mediante un mapeo transitorio; no requiere que la página destino sea escribible desde userland, solo que los permisos de sección permitan escritura desde el kernel. Por eso la inyección en páginas de solo lectura tiene éxito en muchos casos.

Uso común por malware

El paso `Write` de la cadena clásica OpenProcess → VirtualAllocEx → WriteProcessMemory → CreateRemoteThread. Se usa para depositar shellcode, un PE con IAT ya resuelta o un stub de loader reflexivo en un proceso remoto. Más allá de la inyección de shellcode también se abusa para sobrescribir prólogos de función para inline hooks, parchear `AMSI.dll!AmsiScanBuffer` o `clr.jit!compileMethod` para bypasses AMSI/ETW, y como primitiva de escritura en variantes de PPID spoofing y process doppelgänging. Brute Ratel, Cobalt Strike, Sliver, Qakbot e IcedID dependen de ella en su cadena de loader.

Oportunidades de detección

Las escrituras entre procesos se observan principalmente vía Sysmon Event ID 10 (`ProcessAccess`) cuando la máscara de acceso solicitada contiene `PROCESS_VM_WRITE` (0x0020) o `PROCESS_VM_OPERATION` (0x0008). El proveedor ETW Threat Intelligence también emite notificaciones de escritura para ciertos procesos protegidos. Los EDR hookean `NtWriteVirtualMemory` en ntdll; los syscalls directos eluden el hook pero el kernel sigue actualizando los contadores VAD del objetivo y puede observarse mediante callbacks `PsSetCreateProcessNotifyRoutineEx` si la escritura desencadena una creación de hilo posterior. Las escrituras dirigidas a procesos de otra sesión o que apuntan a `lsass.exe` o binarios de navegador merecen una puntuación elevada.

Ejemplos de syscalls directos

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),
    );
}

Mapeos MITRE ATT&CK

Last verified: 2026-05-20