> Windows Syscalls
ntoskrnl.exeT1497T1486T1106

NtUnlockFile

Libera un bloqueo de rango de bytes previamente adquirido sobre un archivo abierto.

Prototipo

NTSTATUS NtUnlockFile(
  HANDLE           FileHandle,
  PIO_STATUS_BLOCK IoStatusBlock,
  PLARGE_INTEGER   ByteOffset,
  PLARGE_INTEGER   Length,
  ULONG            Key
);

Argumentos

NameTypeDirDescription
FileHandleHANDLEinHandle al archivo abierto que posee el bloqueo. Debe ser el mismo handle que lo adquirió.
IoStatusBlockPIO_STATUS_BLOCKoutRecibe el NTSTATUS final e Information (siempre 0 para NtUnlockFile).
ByteOffsetPLARGE_INTEGERinDesplazamiento inicial en bytes del rango a liberar. Debe coincidir exactamente con la llamada NtLockFile original.
LengthPLARGE_INTEGERinLongitud en bytes del rango a liberar. Debe coincidir exactamente con el NtLockFile original.
KeyULONGinCookie que identifica qué bloqueo liberar entre los solapados retenidos por el mismo handle.

IDs de syscalls por versión de Windows

Versión de WindowsID de syscallBuild
Win10 15070x1ADwin10-1507
Win10 16070x1B6win10-1607
Win10 17030x1BCwin10-1703
Win10 17090x1C0win10-1709
Win10 18030x1C2win10-1803
Win10 18090x1C3win10-1809
Win10 19030x1C4win10-1903
Win10 19090x1C4win10-1909
Win10 20040x1CAwin10-2004
Win10 20H20x1CAwin10-20h2
Win10 21H10x1CAwin10-21h1
Win10 21H20x1CCwin10-21h2
Win10 22H20x1CCwin10-22h2
Win11 21H20x1D6win11-21h2
Win11 22H20x1DAwin11-22h2
Win11 23H20x1DAwin11-23h2
Win11 24H20x1DDwin11-24h2
Server 20160x1B6winserver-2016
Server 20190x1C3winserver-2019
Server 20220x1D2winserver-2022
Server 20250x1DDwinserver-2025

Módulo del kernel

ntoskrnl.exeNtUnlockFile

APIs relacionadas

UnlockFileUnlockFileExNtLockFileNtClose

Stub del syscall

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

Espejo de NtLockFile. El rango a desbloquear debe coincidir *exactamente* con uno previamente adquirido — no hay desbloqueo de solape parcial, ni atajo de "desbloquearlo todo". Una llamada con un rango no coincidente devuelve STATUS_RANGE_NOT_LOCKED. Los bloqueos también se liberan implícitamente al cerrar el FileHandle (NtClose), que es como ocurre casi toda liberación legítima — el NtUnlockFile explícito lo usan sobre todo motores de base de datos de larga duración y aplicaciones Office que liberan bloqueos por registro manteniendo el fichero abierto.

Uso común por malware

Por sí solo, NtUnlockFile es poco interesante — es simplemente la mitad de limpieza de un abuso de NtLockFile. En la práctica aparece como paso final de dos patrones: (a) el stub anti-análisis auto-bloqueado desbloquea antes de salir para que un desinstalador / updater pueda reescribir el binario limpio, y (b) un ransomware que bloqueó la nota durante el cifrado desbloquea una vez completada la fase para que la víctima pueda leerla. Señal honesta: prácticamente no hay informes públicos que conviertan a NtUnlockFile en un IOC discriminante por sí solo, y la mayoría de los ransomware omiten el desbloqueo explícito y simplemente cierran el handle.

Oportunidades de detección

Perfil de volumen y narrativa de detección idénticos a NtLockFile — mismo proveedor ETW, mismo punto de hook minifilter FltMgr (IRP_MJ_LOCK_CONTROL cubre lock y unlock). El valor blue-team de las alertas NtUnlockFile es bajo; el valor está en la *correlación* con un NtLockFile precedente sobre un objetivo sensible. Los EDR que ya alertan en el evento lock no ganan mucho alertando también en el unlock. Vale la pena rastrearlo en investigación post-incidente de ransomware: una ráfaga súbita de NtUnlockFile contra muchos handles de documentos del usuario correlaciona con el final del barrido de cifrado.

Ejemplos de syscalls directos

cRelease a whole-file lock before exit

// Pair-of-pair: matches the NtLockFile call that took the whole-file lock.
IO_STATUS_BLOCK iosb;
LARGE_INTEGER off = { .QuadPart = 0 };
LARGE_INTEGER len = { .QuadPart = 0x7FFFFFFFFFFFFFFFLL };

NTSTATUS st = NtUnlockFile(hSelf, &iosb, &off, &len, 0);
if (st == STATUS_RANGE_NOT_LOCKED) {
    // Some other code already unlocked it (or the offsets don't match).
}

asmx64 direct stub (Win11 24H2 SSN 0x1DD)

NtUnlockFile PROC
    mov  r10, rcx
    mov  eax, 1DDh
    syscall
    ret
NtUnlockFile ENDP

rustRelease ransom-note lock after encryption sweep

// Cargo: ntapi = "0.4"
unsafe {
    let off: i64 = 0;
    let len: i64 = i64::MAX;
    let mut iosb = zeroed();
    let st = NtUnlockFile(h_note,
        &mut iosb,
        &off as *const _ as _,
        &len as *const _ as _,
        0);
    // STATUS_RANGE_NOT_LOCKED is benign here — handle close would have done it anyway.
    let _ = st;
}

Mapeos MITRE ATT&CK

Last verified: 2026-05-20