> Windows Syscalls
ntoskrnl.exeT1547.001T1112T1106

NtFlushKey

Force l'écriture sur disque de toutes les modifications en attente d'une clé de registre.

Prototype

NTSTATUS NtFlushKey(
  HANDLE  KeyHandle
);

Arguments

NameTypeDirDescription
KeyHandleHANDLEinHandle ouvert vers une clé quelconque de la ruche à flusher ; toute la ruche contenant la clé est commitée.

IDs de syscalls par version de Windows

Version de WindowsID de syscallBuild
Win10 15070xDAwin10-1507
Win10 16070xDDwin10-1607
Win10 17030xE0win10-1703
Win10 17090xE1win10-1709
Win10 18030xE2win10-1803
Win10 18090xE3win10-1809
Win10 19030xE4win10-1903
Win10 19090xE4win10-1909
Win10 20040xE9win10-2004
Win10 20H20xE9win10-20h2
Win10 21H10xE9win10-21h1
Win10 21H20xEAwin10-21h2
Win10 22H20xEAwin10-22h2
Win11 21H20xEFwin11-21h2
Win11 22H20xF0win11-22h2
Win11 23H20xF0win11-23h2
Win11 24H20xF2win11-24h2
Server 20160xDDwinserver-2016
Server 20190xE3winserver-2019
Server 20220xEEwinserver-2022
Server 20250xF2winserver-2025

Module noyau

ntoskrnl.exeNtFlushKey

APIs liées

RegFlushKeyNtFlushBuffersFileNtSaveKeyNtSetValueKey

Stub du syscall

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

NtFlushKey commit toutes les modifications en mémoire pour la *ruche* contenant la clé fournie vers son fichier disque. Malgré la signature par clé, le noyau flushe toute la ruche — le configuration manager ne maintient pas de dirty bits par clé à la granularité du flush. Différence avec `NtFlushBuffersFile` : ce dernier cible un fichier via FILE_OBJECT, NtFlushKey cible une ruche via un handle de clé. Le wrapper Win32 est `RegFlushKey`. Le lazy flusher écrit normalement les ruches sales toutes les cinq secondes (`CmpLazyFlushIntervalInSeconds`) ; NtFlushKey force l'écriture immédiate.

Usage courant par les malwares

Le pattern d'abus principal est l'**anti-forensique autour de la persistance** : après écriture d'une Run key, config de tâche planifiée, entrée de service ou valeur de COM-hijack, le malware appelle NtFlushKey pour garantir que la modification est persistée sur disque *avant* le prochain reboot non propre — important quand la fenêtre de lazy-flush est plus large que la fenêtre d'opportunité attendue (exploit déclenchant un BSOD, hard reset planifié, ransomware tueur d'hôte). Pattern secondaire : certains droppers flushent une entrée autorun fraîchement écrite, puis suppriment immédiatement le binaire référencé sur disque, laissant les métadonnées Run-key pointer sur un contenu peut-être absent au boot — petite nuisance anti-analyse.

Opportunités de détection

Seul, NtFlushKey est sans intérêt — explorer.exe et de nombreux drivers l'appellent en routine. Le signal utile est la *séquence* : Sysmon Event 13 (set de valeur registre) sur une clé de persistance connue, immédiatement suivi d'un flush depuis le même PID, surtout si ce PID est non signé ou récemment créé. L'ETW `Microsoft-Windows-Kernel-Registry` expose les événements flush avec chemin de clé et PID. Corréler avec Sysmon Event 11 (create / delete de fichier) sur le binaire référencé par l'entrée de persistance.

Exemples de syscalls directs

asmx64 direct stub (Win11 24H2)

; Direct syscall stub for NtFlushKey (SSN 0xF2 on Win11 24H2 — drifts per build)
NtFlushKey PROC
    mov  r10, rcx          ; KeyHandle
    mov  eax, 0F2h         ; SSN
    syscall
    ret
NtFlushKey ENDP

cPersistence write-then-flush

// Write a Run key and immediately flush — defeats lazy-flush race on hard reboot.
#include <windows.h>

LONG PersistAndFlush(LPCWSTR name, LPCWSTR cmd) {
    HKEY hRun = NULL;
    LONG s = RegCreateKeyExW(HKEY_CURRENT_USER,
        L"Software\\Microsoft\\Windows\\CurrentVersion\\Run",
        0, NULL, 0, KEY_SET_VALUE, NULL, &hRun, NULL);
    if (s != ERROR_SUCCESS) return s;
    s = RegSetValueExW(hRun, name, 0, REG_SZ,
                       (const BYTE*)cmd,
                       (DWORD)((wcslen(cmd) + 1) * sizeof(WCHAR)));
    if (s == ERROR_SUCCESS) RegFlushKey(hRun);   // ← NtFlushKey under the hood
    RegCloseKey(hRun);
    return s;
}

rustDirect NtFlushKey

// Cargo: ntapi = "0.4"
use ntapi::ntregapi::NtFlushKey;
use winapi::shared::ntdef::{HANDLE, NTSTATUS};

unsafe fn flush(key: HANDLE) -> NTSTATUS {
    NtFlushKey(key)
}

Mappings MITRE ATT&CK

Last verified: 2026-05-20