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

NtSetSystemEnvironmentValueEx

Schreibt oder löscht eine UEFI-Variable, identifiziert durch (Name, Hersteller-GUID) — die kanonische Firmware-Persistenz-Oberfläche.

Prototyp

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

Argumente

NameTypeDirDescription
VariableNamePUNICODE_STRINGinName der ziel-UEFI-Variable (z. B. L"BootOrder", L"dbx", ein eigener OEM-Schlüssel).
VendorGuidLPGUIDinHersteller-Namespace-GUID. EFI_GLOBAL_VARIABLE für Boot-Konfiguration; EFI_IMAGE_SECURITY_DATABASE_GUID für Secure-Boot-Schlüssel-DBs.
ValuePVOIDinZeiger auf die neuen Wertebytes. NULL mit ValueLength=0 löscht die Variable (sofern die Attribute es erlauben).
ValueLengthULONGinGröße des Wertepuffers in Bytes. Null (mit NULL Value) fordert Löschung an.
AttributesULONGinEFI-Variablen-Attribute: NON_VOLATILE, BOOTSERVICE_ACCESS, RUNTIME_ACCESS, AUTHENTICATED_WRITE_ACCESS, TIME_BASED_AUTH, APPEND_WRITE.

Syscall-IDs pro Windows-Version

Windows-VersionSyscall-IDBuild
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

Kernel-Modul

ntoskrnl.exeNtSetSystemEnvironmentValueEx

Verwandte APIs

SetFirmwareEnvironmentVariableExWSetFirmwareEnvironmentVariableWNtQuerySystemEnvironmentValueExNtSetSystemEnvironmentValueRtlAdjustPrivilege

Syscall-Stub

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

Undokumentierte Hinweise

Das hochwertige Gegenstück zu NtQuerySystemEnvironmentValueEx. Dies ist der Kernelpfad, der `bcdedit /set` für viele plattformweite Schalter trägt (Test-Signing, Debug-Modus, Hypervisor-Launch-Typ), Secure-Boot-Policy-Variablen schreibt (PK/KEK/db/dbx — die meisten erfordern authentifizierte Schreibvorgänge, die das OS nicht selbst erzeugen kann), und die Oberfläche ist, zu der ein Angreifer greift, um **Firmware-Persistenz** zu setzen: eine EFI-Variable schreiben, die der OS-Loader oder ein verwundbarer Shim beim Boot liest. Erfordert SeSystemEnvironmentPrivilege. Die Firmware selbst, nicht nur der Kernel, erzwingt Signaturprüfungen für authentifizierte Schreibvorgänge — Modifikationen an PK/KEK/db ohne gültige Auth-Payload werden an der SMM-Grenze abgelehnt.

Häufige Malware-Nutzung

Schwerpunkt des UEFI-Persistenz-Tradecrafts. **LoJax** (Sednit / APT28, 2018) war das wegweisende Beispiel: eine Usermode-Komponente bewaffnete RWEverythings RwDrv, um direkt in den SPI-Flash zu schreiben, aber parallele Implants nutzen NtSetSystemEnvironmentValueEx, um Payload-Pointer oder Hilfszustände in NVRAM zu platzieren, die der Bootkit nach Umlenkung der Boot-Kette liest. **BlackLotus** (2022, CVE-2022-21894 „baton drop") schreibt angreifergewählte MOK/dbx-Einträge und nutzt NVRAM, um die Konfiguration seines Second-Stage-Bootkits abzulegen. **ESPecter**, **CosmicStrand** und **MoonBounce** (APT41) stützen sich alle auf die breitere UEFI-Variablenoberfläche für Konfigurationsspeicherung oder Staging. Defensive Notiz: dbx schreiben — d. h. Loader-Hashes *widerrufen* — wurde sowohl legitim (KB5025885-dbx-Push) als auch offensiv (DoS des eigenen Bootloaders) verwendet.

Erkennungs­möglichkeiten

ETW Microsoft-Windows-Kernel-General emittiert Firmware-Schreibereignisse mit Variablenname, GUID und Ergebnis. Verteidiger sollten drei Dinge verfolgen: (1) welche Prozesse das überhaupt aufrufen, (2) den Privilegien-Aktivierungsübergang bei SeSystemEnvironmentPrivilege und (3) jeden Schreibvorgang auf GUIDs, die EFI_GLOBAL_VARIABLE oder EFI_IMAGE_SECURITY_DATABASE_GUID entsprechen, aus Nicht-TrustedInstaller-/Nicht-OEM-Binaries. Microsoft Defender for Endpoint und CrowdStrike Falcon exponieren UEFI-Variablen-Schreibvorgänge in ihren Device-Control-/Boot-Integrity-Dashboards. Auf Windows 11 24H2 und Server 2025 vergleicht das OS zusätzlich mit dem System-Guard-Secure-Launch-Zustand; Abweichungen erscheinen in Microsoft-Windows-Kernel-Boot.

Direkte Syscall-Beispiele

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(())
}

MITRE ATT&CK-Mappings

Last verified: 2026-05-20