NtUnloadKey
Détache une ruche du registre précédemment chargée du gestionnaire de configuration.
Prototype
NTSTATUS NtUnloadKey( POBJECT_ATTRIBUTES TargetKey );
Arguments
| Name | Type | Dir | Description |
|---|---|---|---|
| TargetKey | POBJECT_ATTRIBUTES | in | Attributs d'objet nommant la clé racine de la ruche à décharger, ex. \Registry\User\TempHive. |
IDs de syscalls par version de Windows
| Version de Windows | ID de syscall | Build |
|---|---|---|
| Win10 1507 | 0x1AA | win10-1507 |
| Win10 1607 | 0x1B3 | win10-1607 |
| Win10 1703 | 0x1B9 | win10-1703 |
| Win10 1709 | 0x1BD | win10-1709 |
| Win10 1803 | 0x1BF | win10-1803 |
| Win10 1809 | 0x1C0 | win10-1809 |
| Win10 1903 | 0x1C1 | win10-1903 |
| Win10 1909 | 0x1C1 | win10-1909 |
| Win10 2004 | 0x1C7 | win10-2004 |
| Win10 20H2 | 0x1C7 | win10-20h2 |
| Win10 21H1 | 0x1C7 | win10-21h1 |
| Win10 21H2 | 0x1C9 | win10-21h2 |
| Win10 22H2 | 0x1C9 | win10-22h2 |
| Win11 21H2 | 0x1D3 | win11-21h2 |
| Win11 22H2 | 0x1D7 | win11-22h2 |
| Win11 23H2 | 0x1D7 | win11-23h2 |
| Win11 24H2 | 0x1DA | win11-24h2 |
| Server 2016 | 0x1B3 | winserver-2016 |
| Server 2019 | 0x1C0 | winserver-2019 |
| Server 2022 | 0x1CF | winserver-2022 |
| Server 2025 | 0x1DA | winserver-2025 |
Module noyau
APIs liées
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 ENDPcOffline 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