> Windows Syscalls
ntoskrnl.exeT1542.003T1082T1542

NtEnumerateBootEntries

Retourne un tableau compact de structures BOOT_ENTRY décrivant chaque option de démarrage firmware enregistrée.

Prototype

NTSTATUS NtEnumerateBootEntries(
  PVOID  Buffer,
  PULONG BufferLength
);

Arguments

NameTypeDirDescription
BufferPVOIDoutBuffer alloué par l'appelant qui reçoit une séquence compactée d'enregistrements BOOT_ENTRY_LIST (chacun contenant un NextEntryOffset et une BOOT_ENTRY inline). Peut être NULL pour interroger la taille requise.
BufferLengthPULONGin/outEn entrée : taille de Buffer en octets. En sortie : octets écrits, ou taille requise lorsque STATUS_BUFFER_TOO_SMALL est retourné.

IDs de syscalls par version de Windows

Version de WindowsID de syscallBuild
Win10 15070xCFwin10-1507
Win10 16070xD2win10-1607
Win10 17030xD5win10-1703
Win10 17090xD6win10-1709
Win10 18030xD7win10-1803
Win10 18090xD8win10-1809
Win10 19030xD9win10-1903
Win10 19090xD9win10-1909
Win10 20040xDEwin10-2004
Win10 20H20xDEwin10-20h2
Win10 21H10xDEwin10-21h1
Win10 21H20xDFwin10-21h2
Win10 22H20xDFwin10-22h2
Win11 21H20xE4win11-21h2
Win11 22H20xE5win11-22h2
Win11 23H20xE5win11-23h2
Win11 24H20xE7win11-24h2
Server 20160xD2winserver-2016
Server 20190xD8winserver-2019
Server 20220xE3winserver-2022
Server 20250xE7winserver-2025

Module noyau

ntoskrnl.exeNtEnumerateBootEntries

APIs liées

BcdEnumerateObjects (bcd.dll)bcdedit.exe /enum FIRMWAREGetFirmwareEnvironmentVariableW (BootOrder / Boot####)NtQueryBootEntryOrderNtAddBootEntry

Stub du syscall

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

Schéma classique en deux appels : invoquer avec `Buffer = NULL` pour récupérer la taille requise, allouer, puis rappeler. La sortie est une liste chaînée — chaque `BOOT_ENTRY_LIST.NextEntryOffset` est un delta en octets depuis le début de l'enregistrement courant vers le suivant (zéro termine). En coulisses, le noyau parcourt la liste côté firmware : `BootOrder` NVRAM + variables `Boot####` sur UEFI, énumération de la ruche BCD sur BIOS legacy. SeSystemEnvironmentPrivilege est requis même en *lecture*.

Usage courant par les malwares

L'énumération est l'étape de reconnaissance de toute kill chain de persistance BCD : le malware a besoin de l'ID `{bootmgr}` existant avant de pouvoir appeler NtModifyBootEntry, et il lui faut l'ordre courant avant NtSetBootEntryOrder. Le module UEFI de TrickBoot commence notoirement par une passe d'inventaire appelant l'équivalent de `BcdEnumerateObjects` + `NtEnumerateBootEntries` pour empreindre le firmware (fournisseur, versions vulnérables de bootmgr, présence d'entrées recovery) et la rapporter au C2 avant de décider de déployer ou non l'étage bootkit.

Opportunités de détection

L'énumération en lecture seule est rarement intéressante isolée — des outils légitimes (`bcdedit`, Windows Update, configuration BitLocker) l'appellent en routine. La suspicion vient du **contexte** : un processus non-installeur, non en contexte système, qui ouvre un handle réclamant SeSystemEnvironmentPrivilege *et* énumère immédiatement les entrées BCD, est anormal. Détection : interroger les événements d'audit 4673 pour l'usage de SeSystemEnvironmentPrivilege, puis corréler avec le chemin image du processus. Les EDR qui télémétrent les transitions NtSetInformationToken / NtAdjustPrivilegesToken vers SE_PRIVILEGE_ENABLED pour SeSystemEnvironment peuvent attraper l'étape d'acquisition de privilège qui précède tout appel d'écriture.

Exemples de syscalls directs

asmx64 direct stub (Win11 24H2, SSN 0xE7)

NtEnumerateBootEntries PROC
    mov  r10, rcx          ; PVOID Buffer
    mov  eax, 0E7h         ; Win11 24H2
    syscall
    ret
NtEnumerateBootEntries ENDP

cTwo-call size query + walk

// Enumerate every firmware boot entry, print FriendlyName.
extern NTSTATUS NTAPI NtEnumerateBootEntries(PVOID, PULONG);

void dump_entries(void) {
    ULONG size = 0;
    NtEnumerateBootEntries(NULL, &size);     // STATUS_BUFFER_TOO_SMALL
    if (size == 0) return;
    PBYTE buf = (PBYTE)HeapAlloc(GetProcessHeap(), 0, size);
    if (NT_SUCCESS(NtEnumerateBootEntries(buf, &size))) {
        PBYTE p = buf;
        while (p < buf + size) {
            PBOOT_ENTRY_LIST list = (PBOOT_ENTRY_LIST)p;
            PBOOT_ENTRY be = &list->BootEntry;
            wprintf(L"id=%08X name=%s\n", be->Id,
                    (LPCWSTR)((PBYTE)be + be->FriendlyNameOffset));
            if (!list->NextEntryOffset) break;
            p += list->NextEntryOffset;
        }
    }
    HeapFree(GetProcessHeap(), 0, buf);
}

Mappings MITRE ATT&CK

Last verified: 2026-05-20