> Windows Syscalls
ntoskrnl.exeT1497T1486T1106

NtUnlockFile

Libère un verrou de plage d'octets précédemment acquis sur un fichier ouvert.

Prototype

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

Arguments

NameTypeDirDescription
FileHandleHANDLEinHandle vers le fichier ouvert détenant le verrou. Doit être le même handle qui l'a acquis.
IoStatusBlockPIO_STATUS_BLOCKoutReçoit le NTSTATUS final et Information (toujours 0 pour NtUnlockFile).
ByteOffsetPLARGE_INTEGERinOffset de début (en octets) de la plage à libérer. Doit correspondre exactement à l'appel NtLockFile original.
LengthPLARGE_INTEGERinLongueur en octets de la plage à libérer. Doit correspondre exactement au NtLockFile original.
KeyULONGinCookie identifiant quel verrou libérer parmi les verrous chevauchants détenus par le même handle.

IDs de syscalls par version de Windows

Version 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

Module noyau

ntoskrnl.exeNtUnlockFile

APIs liées

UnlockFileUnlockFileExNtLockFileNtClose

Stub du 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

Notes non documentées

Miroir de NtLockFile. La plage à débloquer doit *exactement* correspondre à une plage précédemment acquise — pas de déblocage de chevauchement partiel, pas de raccourci « tout débloquer ». Un appel avec une plage non-correspondante renvoie STATUS_RANGE_NOT_LOCKED. Les verrous sont aussi libérés implicitement à la fermeture du FileHandle (NtClose), c'est ainsi que se passe presque toute libération légitime — NtUnlockFile explicite est surtout utilisé par les moteurs de base de données long-running et Office qui libèrent des verrous par enregistrement en gardant le fichier ouvert.

Usage courant par les malwares

Seul, NtUnlockFile est peu intéressant — c'est juste la moitié nettoyage d'un abus de NtLockFile. En pratique il apparaît comme étape finale de deux motifs : (a) le stub anti-analyse auto-verrouillé déverrouille avant de sortir pour qu'un désinstalleur / updater puisse réécrire le binaire proprement, et (b) un rançongiciel qui a verrouillé sa note pendant le chiffrement déverrouille une fois la phase de chiffrement terminée pour que la victime puisse la lire. Signal honnête : il n'existe quasiment aucun rapport public faisant de NtUnlockFile un IOC discriminant par lui-même, et la plupart des rançongiciels sautent le déverrouillage explicite et se contentent de fermer le handle.

Opportunités de détection

Profil de volume et histoire de détection identiques à NtLockFile — même fournisseur ETW, même point de hook minifilter FltMgr (IRP_MJ_LOCK_CONTROL couvre lock et unlock). La valeur blue-team d'alertes NtUnlockFile est faible ; la valeur est dans la *corrélation* avec un NtLockFile précédent sur une cible sensible. Les EDR qui alertent déjà sur l'événement lock ne gagnent pas grand-chose à alerter aussi sur l'unlock. À suivre lors d'investigations post-incident rançongiciel : une rafale soudaine de NtUnlockFile contre de nombreux handles de documents utilisateur corrèle avec la fin de la passe de chiffrement.

Exemples de syscalls directs

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;
}

Mappings MITRE ATT&CK

Last verified: 2026-05-20