NtDuplicateObject
Dupliziert ein Handle aus einem Quellprozess in einen Zielprozess, optional mit Anpassung der Zugriffsrechte oder Schließung der Quelle.
Prototyp
NTSTATUS NtDuplicateObject( HANDLE SourceProcessHandle, HANDLE SourceHandle, HANDLE TargetProcessHandle, PHANDLE TargetHandle, ACCESS_MASK DesiredAccess, ULONG HandleAttributes, ULONG Options );
Argumente
| Name | Type | Dir | Description |
|---|---|---|---|
| SourceProcessHandle | HANDLE | in | Handle auf den Prozess, dem SourceHandle gehört. Erfordert PROCESS_DUP_HANDLE. |
| SourceHandle | HANDLE | in | Das zu duplizierende Handle im Quellprozess. |
| TargetProcessHandle | HANDLE | in | Handle auf den Zielprozess. NULL nur erlaubt, wenn Options DUPLICATE_CLOSE_SOURCE enthält. |
| TargetHandle | PHANDLE | out | Empfängt das neue, im Zielprozess gültige Handle. Optional. |
| DesiredAccess | ACCESS_MASK | in | Zugriffsmaske für das neue Handle. Wird ignoriert, wenn Options DUPLICATE_SAME_ACCESS enthält. |
| HandleAttributes | ULONG | in | Flags wie OBJ_INHERIT oder OBJ_PROTECT_CLOSE für das neue Handle. |
| Options | ULONG | in | Bitmaske aus DUPLICATE_CLOSE_SOURCE und/oder DUPLICATE_SAME_ACCESS / DUPLICATE_SAME_ATTRIBUTES. |
Syscall-IDs pro Windows-Version
| Windows-Version | Syscall-ID | Build |
|---|---|---|
| Win10 1507 | 0x3C | win10-1507 |
| Win10 1607 | 0x3C | win10-1607 |
| Win10 1703 | 0x3C | win10-1703 |
| Win10 1709 | 0x3C | win10-1709 |
| Win10 1803 | 0x3C | win10-1803 |
| Win10 1809 | 0x3C | win10-1809 |
| Win10 1903 | 0x3C | win10-1903 |
| Win10 1909 | 0x3C | win10-1909 |
| Win10 2004 | 0x3C | win10-2004 |
| Win10 20H2 | 0x3C | win10-20h2 |
| Win10 21H1 | 0x3C | win10-21h1 |
| Win10 21H2 | 0x3C | win10-21h2 |
| Win10 22H2 | 0x3C | win10-22h2 |
| Win11 21H2 | 0x3C | win11-21h2 |
| Win11 22H2 | 0x3C | win11-22h2 |
| Win11 23H2 | 0x3C | win11-23h2 |
| Win11 24H2 | 0x3C | win11-24h2 |
| Server 2016 | 0x3C | winserver-2016 |
| Server 2019 | 0x3C | winserver-2019 |
| Server 2022 | 0x3C | winserver-2022 |
| Server 2025 | 0x3C | winserver-2025 |
Kernel-Modul
Verwandte APIs
Syscall-Stub
4C 8B D1 mov r10, rcx B8 3C 00 00 00 mov eax, 0x3C 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
NtDuplicateObject trägt SSN `0x3C` von Windows 10 1507 bis Windows 11 24H2 — eine in der Tabelle seltene flache Kurve, die hartcodierte direkte Syscall-Stubs einfach macht. Intern verläuft der Pfad über ObDuplicateObject, das PROCESS_DUP_HANDLE am Quellprozess prüft und das zugrundeliegende Objekt in die Handletable des Ziels re-referenziert. Die `Options`-Flags (DUPLICATE_SAME_ACCESS, DUPLICATE_SAME_ATTRIBUTES, DUPLICATE_CLOSE_SOURCE) sind offensiv die interessantesten: DUPLICATE_SAME_ACCESS umgeht DACL-Trimming bei der Duplikation, DUPLICATE_CLOSE_SOURCE ist die billige Primitive für Handle-Diebstahl.
Häufige Malware-Nutzung
Zwei Muster dominieren. Erstens, **Handle-Schmuggel** zwischen Prozessen: ein Implant in einem niedrigprivilegierten Prozess öffnet ein höherprivilegiertes Ziel mit PROCESS_DUP_HANDLE und nutzt NtDuplicateObject, um ein Token- oder Prozess-Handle herauszuziehen (in Kombination mit NtOpenProcessToken ein klassischer Baustein für Cobalt Strikes `steal_token`). Zweitens, **Handle-Diebstahl für Credential Access**: hält ein high-integrity-Dienst bereits ein LSASS-Handle (Antimalware, EDR, in einigen Fällen lsm.exe), vermeidet die Duplikation jeden NtOpenProcess auf lsass.exe — und schlägt damit die meisten PPL/EDR-Detektionen. Dokumentiert in Hidden Bees Handle-Inheritance-Trick, im `--use-valid-sig`-Modus von NanoDump und missbraucht von Lazarus und FIN7. DUPLICATE_CLOSE_SOURCE wird zudem von Ransomware genutzt, um Datei-Locks von Office/SQL vor Verschlüsselung zwangsweise zu schließen.
Erkennungsmöglichkeiten
EDRs hooken NtDuplicateObject in ntdll für Cross-Process-Handle-Events; kernelseitig feuert ObRegisterCallbacks (ObjectPreCallback / ObjectPostCallback) auf PsProcessType und PsThreadType unabhängig vom User-Mode-Hooking und ist der maßgebliche Pivot. Sysmon Event ID 10 (ProcessAccess) meldet das Quell-Open mit PROCESS_DUP_HANDLE-Zugriff, oft verräterisch wenn die Quelle lsass.exe ist. Auf Prozesse achten, die mit DUP_HANDLE geöffnet, aber nie mit VM_OPERATION/VM_READ angesprochen werden — klassisches Handle-Schmuggel-Muster. ETW Microsoft-Windows-Kernel-Audit-API-Calls (GUID `e02a841c-75a3-4fa7-afc8-ae09cf9b7f23`) emittiert ein Duplicate-Event mit Quell- und Ziel-PID.
Direkte Syscall-Beispiele
asmx64 direct stub
; Direct syscall stub for NtDuplicateObject (SSN 0x3C, all builds)
NtDuplicateObject PROC
mov r10, rcx ; syscall convention
mov eax, 3Ch ; SSN
syscall
ret
NtDuplicateObject ENDPcToken-handle steal via DUPLICATE_SAME_ACCESS
// Pull a primary token handle out of a target process without ever
// calling NtOpenProcessToken on it directly. hTarget must have been
// opened with PROCESS_DUP_HANDLE.
#include <windows.h>
#define DUPLICATE_SAME_ACCESS 0x00000002
typedef NTSTATUS (NTAPI *pNtDuplicateObject)(
HANDLE, HANDLE, HANDLE, PHANDLE, ACCESS_MASK, ULONG, ULONG);
HANDLE StealTokenHandle(HANDLE hTargetProcess, HANDLE hRemoteToken) {
pNtDuplicateObject NtDuplicateObject = (pNtDuplicateObject)
GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtDuplicateObject");
HANDLE hLocal = NULL;
NTSTATUS s = NtDuplicateObject(
hTargetProcess, // SourceProcessHandle
hRemoteToken, // SourceHandle (token in target)
(HANDLE)-1, // TargetProcessHandle = self
&hLocal, // TargetHandle
0, 0,
DUPLICATE_SAME_ACCESS); // keep original ACCESS_MASK
return (s >= 0) ? hLocal : NULL;
}rustDUPLICATE_CLOSE_SOURCE handle eviction
// Cargo: ntapi = "0.4", windows-sys = "0.59"
// Force-close a file handle held by another process (e.g. break an
// exclusive lock before encryption / overwrite).
use ntapi::ntobapi::NtDuplicateObject;
use windows_sys::Win32::Foundation::HANDLE;
const DUPLICATE_CLOSE_SOURCE: u32 = 0x0000_0001;
pub unsafe fn evict_handle(target_proc: HANDLE, victim: HANDLE) {
let mut sink: HANDLE = std::ptr::null_mut();
let _ = NtDuplicateObject(
target_proc as _,
victim as _,
std::ptr::null_mut(), // TargetProcessHandle = NULL
&mut sink as *mut _ as _, // TargetHandle (discarded)
0, 0,
DUPLICATE_CLOSE_SOURCE,
);
}MITRE ATT&CK-Mappings
Last verified: 2026-05-20