> Windows Syscalls
ntoskrnl.exeT1003.002T1003.004T1106

NtLoadKey

Mountet eine Registry-Hive-Datei unter einem Zielschlüssel — der Syscall hinter dem Offline-Laden von SAM/SYSTEM.

Prototyp

NTSTATUS NtLoadKey(
  POBJECT_ATTRIBUTES  KeyObjectAttributes,
  POBJECT_ATTRIBUTES  FileObjectAttributes
);

Argumente

NameTypeDirDescription
KeyObjectAttributesPOBJECT_ATTRIBUTESinZiel-Registry-Pfad, unter dem die Hive gemountet wird (z. B. \Registry\Machine\OFFLINE_SAM).
FileObjectAttributesPOBJECT_ATTRIBUTESinNT-Pfad zur Hive-Datei auf der Platte (z. B. \??\C:\Windows\System32\config\SAM).

Syscall-IDs pro Windows-Version

Windows-VersionSyscall-IDBuild
Win10 15070xF6win10-1507
Win10 16070xFBwin10-1607
Win10 17030xFFwin10-1703
Win10 17090x100win10-1709
Win10 18030x101win10-1803
Win10 18090x101win10-1809
Win10 19030x102win10-1903
Win10 19090x102win10-1909
Win10 20040x107win10-2004
Win10 20H20x107win10-20h2
Win10 21H10x107win10-21h1
Win10 21H20x108win10-21h2
Win10 22H20x108win10-22h2
Win11 21H20x10Dwin11-21h2
Win11 22H20x10Ewin11-22h2
Win11 23H20x10Ewin11-23h2
Win11 24H20x110win11-24h2
Server 20160xFBwinserver-2016
Server 20190x101winserver-2019
Server 20220x10Cwinserver-2022
Server 20250x110winserver-2025

Kernel-Modul

ntoskrnl.exeNtLoadKey

Verwandte APIs

RegLoadKeyWRegUnLoadKeyWNtLoadKey2NtLoadKeyExNtUnloadKeyNtSaveKey

Syscall-Stub

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

NtLoadKey mountet eine Registry-Hive-Datei (binäres On-Disk-Format von `SAM`, `SECURITY`, `SYSTEM`, `SOFTWARE`, `NTUSER.DAT`) unter einem beliebigen Unterschlüssel von `\Registry\Machine\` oder `\Registry\User\`. Der Caller benötigt **SeRestorePrivilege** (und üblicherweise SeBackupPrivilege). NtLoadKey ist die Zwei-Argument-Originalversion; NtLoadKey2 / NtLoadKeyEx fügen Flags wie `REG_HIVE_NO_RM` (kein transaktionales Registry / KTM) und explizite Trust-Class-Parameter hinzu. Die SSN driftet bei jedem Feature-Update.

Häufige Malware-Nutzung

Der kanonische Missbrauch ist **Offline-Credential-Dumping**. Ein Angreifer, der bereits eine Kopie der Hive-Dateien `SAM`, `SYSTEM` und `SECURITY` besitzt (via VSS, rohem NTFS-Lesen oder gestohlenem Backup), mountet sie auf einer anderen Maschine mit NtLoadKey unter einem gewählten Unterschlüssel (oft `HKLM\OFFLINE_SAM`) und liest dann die verschlüsselten Hashes und das SYSKEY-Material mit normalen Registry-APIs — exakt der Weg des Offline-Modus von Impackets `secretsdump.py` und von Mimikatz' `lsadump::sam /system:SYSTEM /sam:SAM`. Sekundärnutzung: *stealthy persistence* — Malware legt eine kleine Hive an einem nicht-standardisierten Ort ab und mountet sie zur Laufzeit, um Konfiguration oder Run-Key-Äquivalente vor Registry-Baselinern zu verstecken, die nur die bekannten Hives auditieren.

Erkennungs­möglichkeiten

Sysmon Event ID 12/13 feuert bei Registry-Objekterzeugung / Wert-Set unter ungewöhnlichen Mountpunkten (`HKLM\OFFLINE*`, `HKLM\TEMP_*`). Der ETW-Provider `Microsoft-Windows-Kernel-Registry` liefert Hive-Load-Events mit Mount-Key und Quellpfad. Anpassung von SeRestorePrivilege unmittelbar vor einem Load ist ein starker Frühindikator (Sysmon Event 4673 mit Privilegienliste). Loads von Hive-Dateien außerhalb von `%SystemRoot%\System32\config\` und `%UserProfile%\NTUSER.DAT` sind nahezu immer alarmwürdig.

Direkte Syscall-Beispiele

asmx64 direct stub (Win11 24H2)

; Direct syscall stub for NtLoadKey (SSN 0x110 on Win11 24H2 — drifts per build)
NtLoadKey PROC
    mov  r10, rcx          ; KeyObjectAttributes
    mov  eax, 110h         ; SSN
    syscall
    ret
NtLoadKey ENDP

cOffline SAM credential extraction sketch

// Mount a stolen SAM hive offline and let LSAlib decrypt the NTLM hashes.
// Requires SeRestorePrivilege + SeBackupPrivilege.
#include <windows.h>
#include <winternl.h>

static void EnablePriv(LPCWSTR p) {
    HANDLE tok; TOKEN_PRIVILEGES tp = {0};
    OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &tok);
    LookupPrivilegeValueW(NULL, p, &tp.Privileges[0].Luid);
    tp.PrivilegeCount = 1; tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    AdjustTokenPrivileges(tok, FALSE, &tp, 0, NULL, NULL);
    CloseHandle(tok);
}

int main(void) {
    EnablePriv(SE_RESTORE_NAME);
    EnablePriv(SE_BACKUP_NAME);
    // RegLoadKeyW is the documented wrapper that ends at NtLoadKey under the hood.
    LONG s = RegLoadKeyW(HKEY_LOCAL_MACHINE, L"OFFLINE_SAM",
                          L"C:\\stolen\\SAM");
    if (s == ERROR_SUCCESS) {
        // Now HKLM\OFFLINE_SAM\SAM\Domains\Account\Users\... is readable.
        RegUnLoadKeyW(HKEY_LOCAL_MACHINE, L"OFFLINE_SAM");
    }
    return s;
}

rustDirect NtLoadKey via ntapi

// Cargo: ntapi = "0.4", winapi = { version = "0.3", features = ["ntdef"] }
use ntapi::ntregapi::NtLoadKey;
use ntapi::ntrtl::{RtlInitUnicodeString};
use winapi::shared::ntdef::{OBJECT_ATTRIBUTES, UNICODE_STRING, OBJ_CASE_INSENSITIVE};

unsafe fn load_offline_hive(mount: &str, file: &str) -> i32 {
    let mut k_us: UNICODE_STRING = std::mem::zeroed();
    let mut f_us: UNICODE_STRING = std::mem::zeroed();
    let mw: Vec<u16> = mount.encode_utf16().chain([0]).collect();
    let fw: Vec<u16> = file.encode_utf16().chain([0]).collect();
    RtlInitUnicodeString(&mut k_us, mw.as_ptr());
    RtlInitUnicodeString(&mut f_us, fw.as_ptr());
    let mut k_oa: OBJECT_ATTRIBUTES = std::mem::zeroed();
    let mut f_oa: OBJECT_ATTRIBUTES = std::mem::zeroed();
    k_oa.Length = std::mem::size_of::<OBJECT_ATTRIBUTES>() as u32;
    k_oa.ObjectName = &mut k_us; k_oa.Attributes = OBJ_CASE_INSENSITIVE;
    f_oa.Length = std::mem::size_of::<OBJECT_ATTRIBUTES>() as u32;
    f_oa.ObjectName = &mut f_us; f_oa.Attributes = OBJ_CASE_INSENSITIVE;
    NtLoadKey(&mut k_oa, &mut f_oa)
}

MITRE ATT&CK-Mappings

Last verified: 2026-05-20