> Windows Syscalls
ntoskrnl.exeT1542.001T1542.003T1547

NtSetSystemEnvironmentValueEx

Escribe o elimina una variable UEFI identificada por (nombre, GUID de proveedor) — la superficie canónica de persistencia en firmware.

Prototipo

NTSTATUS NtSetSystemEnvironmentValueEx(
  PUNICODE_STRING VariableName,
  LPGUID          VendorGuid,
  PVOID           Value,
  ULONG           ValueLength,
  ULONG           Attributes
);

Argumentos

NameTypeDirDescription
VariableNamePUNICODE_STRINGinNombre de la variable UEFI objetivo (p. ej. L"BootOrder", L"dbx", una clave OEM personalizada).
VendorGuidLPGUIDinGUID de espacio de proveedor. EFI_GLOBAL_VARIABLE para configuración de arranque; EFI_IMAGE_SECURITY_DATABASE_GUID para BDs de claves Secure Boot.
ValuePVOIDinPuntero a los bytes del nuevo valor. NULL con ValueLength=0 elimina la variable (cuando los atributos lo permiten).
ValueLengthULONGinTamaño del búfer de valor en bytes. Cero (con Value NULL) solicita borrado.
AttributesULONGinAtributos de variable EFI: NON_VOLATILE, BOOTSERVICE_ACCESS, RUNTIME_ACCESS, AUTHENTICATED_WRITE_ACCESS, TIME_BASED_AUTH, APPEND_WRITE.

IDs de syscalls por versión de Windows

Versión de WindowsID de syscallBuild
Win10 15070x18Dwin10-1507
Win10 16070x196win10-1607
Win10 17030x19Cwin10-1703
Win10 17090x19Fwin10-1709
Win10 18030x1A1win10-1803
Win10 18090x1A2win10-1809
Win10 19030x1A3win10-1903
Win10 19090x1A3win10-1909
Win10 20040x1A9win10-2004
Win10 20H20x1A9win10-20h2
Win10 21H10x1A9win10-21h1
Win10 21H20x1ABwin10-21h2
Win10 22H20x1ABwin10-22h2
Win11 21H20x1B4win11-21h2
Win11 22H20x1B8win11-22h2
Win11 23H20x1B8win11-23h2
Win11 24H20x1BBwin11-24h2
Server 20160x196winserver-2016
Server 20190x1A2winserver-2019
Server 20220x1B1winserver-2022
Server 20250x1BBwinserver-2025

Módulo del kernel

ntoskrnl.exeNtSetSystemEnvironmentValueEx

APIs relacionadas

SetFirmwareEnvironmentVariableExWSetFirmwareEnvironmentVariableWNtQuerySystemEnvironmentValueExNtSetSystemEnvironmentValueRtlAdjustPrivilege

Stub del syscall

4C 8B D1            mov r10, rcx
B8 BB 01 00 00      mov eax, 0x1BB
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

El hermano de alto impacto de NtQuerySystemEnvironmentValueEx. Es la ruta del kernel que respalda `bcdedit /set` para muchos interruptores a nivel plataforma (test signing, modo debug, tipo de lanzamiento del hypervisor), escribe variables de política Secure Boot (PK/KEK/db/dbx — la mayoría requieren escrituras autenticadas que el SO no puede forjar por sí mismo), y es la superficie a la que recurre un atacante para plantar **persistencia a nivel firmware**: escribir una variable EFI que el cargador del SO o un shim vulnerable lee al arranque. Requiere SeSystemEnvironmentPrivilege. El firmware en sí, no solo el kernel, aplica las comprobaciones de firma para escrituras autenticadas — modificaciones de PK/KEK/db sin payload de autenticación válido son rechazadas en la frontera SMM.

Uso común por malware

Centro de gravedad del tradecraft de persistencia UEFI. **LoJax** (Sednit / APT28, 2018) fue el ejemplo seminal: un componente de modo usuario armaba RwDrv de RWEverything para escribir directamente en la flash SPI, pero implants paralelos en estado salvaje usan NtSetSystemEnvironmentValueEx para plantar punteros de payload o estado auxiliar en NVRAM que el bootkit lee tras redirigir la cadena de arranque. **BlackLotus** (2022, CVE-2022-21894 "baton drop") escribe entradas MOK/dbx elegidas por el atacante y usa NVRAM para preparar la configuración de su bootkit de segunda etapa. **ESPecter**, **CosmicStrand** y **MoonBounce** (APT41) se apoyan todos en la superficie UEFI más amplia para almacenamiento de configuración o staging. Nota defensiva: escribir dbx — i.e. *revocar* hashes de cargadores — se ha usado tanto legítimamente (push dbx KB5025885) como ofensivamente (DoS del propio bootloader del usuario).

Oportunidades de detección

Microsoft-Windows-Kernel-General ETW emite eventos de escritura de firmware con nombre de variable, GUID y resultado. Los defensores deben rastrear tres cosas: (1) qué procesos invocan esto, (2) la transición de habilitación de privilegio en SeSystemEnvironmentPrivilege, y (3) cualquier escritura a GUIDs que coincidan con EFI_GLOBAL_VARIABLE o EFI_IMAGE_SECURITY_DATABASE_GUID desde binarios no-trusted-installer / no-OEM. Microsoft Defender for Endpoint y CrowdStrike Falcon exponen escrituras de variables UEFI en sus paneles device-control / boot-integrity. En Windows 11 24H2 y Server 2025, el SO además contrasta contra el estado System Guard Secure Launch; las discrepancias aparecen en Microsoft-Windows-Kernel-Boot.

Ejemplos de syscalls directos

asmx64 direct stub (Win11 24H2)

; Direct syscall stub for NtSetSystemEnvironmentValueEx (SSN 0x1BB, Win11 24H2)
NtSetSystemEnvironmentValueEx PROC
    mov  r10, rcx
    mov  eax, 1BBh
    syscall
    ret
NtSetSystemEnvironmentValueEx ENDP

cLoJax-style NVRAM staging skeleton (NOT a working exploit — educational)

// Sketch only: a real bootkit ALSO needs a vulnerable / unsigned bootloader on the
// machine, a SecureBoot-disabled or signature-bypass state, and a SMM-friendly
// firmware. Modern attested boot, Secure Launch and KB5025885-class dbx pushes
// foreclose most of this on patched 2024+ systems.
#include <windows.h>

#define EFI_VARIABLE_NON_VOLATILE       0x00000001
#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x00000002
#define EFI_VARIABLE_RUNTIME_ACCESS     0x00000004

static const wchar_t* kVendorGuid =
    L"{8BE4DF61-93CA-11D2-AA0D-00E098032B8C}"; // EFI_GLOBAL_VARIABLE

BOOL plant_persistence_marker(const wchar_t* name, const void* blob, DWORD len) {
    BOOLEAN old;
    // Privilege is the first gate. Without admin + SeSystemEnvironmentPrivilege
    // the syscall returns STATUS_PRIVILEGE_NOT_HELD immediately.
    if (RtlAdjustPrivilege(SE_SYSTEM_ENVIRONMENT_PRIVILEGE, TRUE, FALSE, &old) < 0)
        return FALSE;

    return SetFirmwareEnvironmentVariableExW(
        name, kVendorGuid, (PVOID)blob, len,
        EFI_VARIABLE_NON_VOLATILE |
        EFI_VARIABLE_BOOTSERVICE_ACCESS |
        EFI_VARIABLE_RUNTIME_ACCESS);
}

rustDelete a custom NVRAM marker

// Cargo: windows = { version = "0.59", features = ["Win32_System_Environment"] }
use windows::core::*;
use windows::Win32::System::Environment::*;

fn delete_marker(name: &str, guid: &str) -> windows::core::Result<()> {
    let hn = HSTRING::from(name);
    let hg = HSTRING::from(guid);
    // ValueLength = 0 + Value = NULL + attributes = 0 => delete (per UEFI 2.x spec).
    unsafe {
        SetFirmwareEnvironmentVariableExW(
            PCWSTR(hn.as_ptr()),
            PCWSTR(hg.as_ptr()),
            None,
            0,
            0,
        )
    }?;
    Ok(())
}

Mapeos MITRE ATT&CK

Last verified: 2026-05-20