> Windows Syscalls
ntoskrnl.exeT1003.002T1112T1070

NtUnloadKey

Détache une ruche du registre précédemment chargée du gestionnaire de configuration.

Prototype

NTSTATUS NtUnloadKey(
  POBJECT_ATTRIBUTES TargetKey
);

Arguments

NameTypeDirDescription
TargetKeyPOBJECT_ATTRIBUTESinAttributs d'objet nommant la clé racine de la ruche à décharger, ex. \Registry\User\TempHive.

IDs de syscalls par version de Windows

Version de WindowsID de syscallBuild
Win10 15070x1AAwin10-1507
Win10 16070x1B3win10-1607
Win10 17030x1B9win10-1703
Win10 17090x1BDwin10-1709
Win10 18030x1BFwin10-1803
Win10 18090x1C0win10-1809
Win10 19030x1C1win10-1903
Win10 19090x1C1win10-1909
Win10 20040x1C7win10-2004
Win10 20H20x1C7win10-20h2
Win10 21H10x1C7win10-21h1
Win10 21H20x1C9win10-21h2
Win10 22H20x1C9win10-22h2
Win11 21H20x1D3win11-21h2
Win11 22H20x1D7win11-22h2
Win11 23H20x1D7win11-23h2
Win11 24H20x1DAwin11-24h2
Server 20160x1B3winserver-2016
Server 20190x1C0winserver-2019
Server 20220x1CFwinserver-2022
Server 20250x1DAwinserver-2025

Module noyau

ntoskrnl.exeNtUnloadKey

APIs liées

RegUnLoadKeyWRegLoadKeyWNtLoadKeyNtLoadKey2NtUnloadKey2NtUnloadKeyEx

Stub du syscall

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

NtUnloadKey détache de force une ruche du registre précédemment attachée via NtLoadKey / `reg load`. L'appelant doit détenir SeRestorePrivilege ; sinon la requête échoue avec STATUS_PRIVILEGE_NOT_HELD. En interne, CmUnloadKey parcourt la chaîne CM_KEY_CONTROL_BLOCK et écrit les modifications en attente avant de démanteler la ruche — un déchargement bâclé d'une ruche encore utilisée peut entrer en concurrence avec des handles d'autres threads et renvoyer STATUS_CANNOT_DELETE. La variante NtUnloadKey2 prend un argument `Flags` pour forcer le détachement.

Usage courant par les malwares

Utilisé comme moitié nettoyage d'un abus de ruche hors-ligne : un opérateur monte `reg load HKU\Tmp C:\Users\victime\NTUSER.DAT`, extrait des credentials / récupère les clés Run de la ruche montée, puis appelle NtUnloadKey pour détacher la ruche et effacer l'indicateur évident. Le même primitive sert d'anti-forensique léger — en déchargeant une ruche qu'un outil de triage vient de mapper, l'implant peut affamer la vue de l'analyste. Certains loaders déchargent aussi des ruches temporairement montées pour falsifier AppCompat / TypedURL.

Opportunités de détection

Sysmon n'a pas d'événement dédié au déchargement de ruche, mais le fournisseur ETW Microsoft-Windows-Kernel-Registry émet l'Event ID 1 (HiveLoad) et l'Event ID 2 (HiveUnload) avec le chemin de la ruche. La sous-catégorie d'audit "Registry" (4657 / 4660) ne couvre pas le load/unload de ruche ; il faut « Other System Events » ou le fournisseur ETW Kernel-Registry directement. Defender ATP lève une alerte haute-fidélité sur `reg unload` de ruches situées hors %SystemRoot%\System32\config — anormal lorsqu'il est précédé d'un load contre le profil d'un autre utilisateur.

Exemples de syscalls directs

asmx64 direct stub (Win11 24H2 SSN 0x1DA)

NtUnloadKey PROC
    mov  r10, rcx
    mov  eax, 1DAh
    syscall
    ret
NtUnloadKey ENDP

cOffline hive cleanup after credential harvest

// After mounting another user's NTUSER.DAT via NtLoadKey and harvesting,
// detach it so `reg query HKU` no longer reveals the staged hive.
UNICODE_STRING name;
RtlInitUnicodeString(&name, L"\\Registry\\User\\TempHive");

OBJECT_ATTRIBUTES oa;
InitializeObjectAttributes(&oa, &name, OBJ_CASE_INSENSITIVE, NULL, NULL);

NTSTATUS st = NtUnloadKey(&oa);
if (st == STATUS_CANNOT_DELETE) {
    // Some handle is still open; force via NtUnloadKey2 with REG_FORCE_UNLOAD.
}

rustwindows-sys + SeRestorePrivilege enablement

// Cargo: windows-sys = "0.59" (Win32_Security, Win32_System_Registry)
// Caller must first enable SeRestorePrivilege via NtAdjustPrivilegesToken.
unsafe {
    let mut name = make_unicode_string("\\Registry\\User\\TempHive");
    let mut oa = OBJECT_ATTRIBUTES {
        Length: size_of::<OBJECT_ATTRIBUTES>() as u32,
        ObjectName: &mut name,
        Attributes: OBJ_CASE_INSENSITIVE,
        ..zeroed()
    };
    let st = NtUnloadKey(&mut oa);
    assert!(st == 0, "NtUnloadKey failed: {st:#x}");
}

Mappings MITRE ATT&CK

Last verified: 2026-05-20