NtLockFile
Acquires a byte-range lock on an open file, optionally exclusive and optionally asynchronous.
Prototype
NTSTATUS NtLockFile( HANDLE FileHandle, HANDLE Event, PIO_APC_ROUTINE ApcRoutine, PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, PLARGE_INTEGER ByteOffset, PLARGE_INTEGER Length, ULONG Key, BOOLEAN FailImmediately, BOOLEAN ExclusiveLock );
Arguments
| Name | Type | Dir | Description |
|---|---|---|---|
| FileHandle | HANDLE | in | Handle to an open file opened with at least FILE_READ_DATA or FILE_WRITE_DATA. |
| Event | HANDLE | in | Optional event signalled on async completion. NULL if ApcRoutine or sync wait is used. |
| ApcRoutine | PIO_APC_ROUTINE | in | Optional user-mode APC fired when the lock is granted asynchronously. |
| ApcContext | PVOID | in | Context value passed back to ApcRoutine. |
| IoStatusBlock | PIO_STATUS_BLOCK | out | Receives final NTSTATUS and Information (always 0 for NtLockFile). |
| ByteOffset | PLARGE_INTEGER | in | Starting byte offset of the range to lock. |
| Length | PLARGE_INTEGER | in | Length in bytes of the range to lock. 0x7FFFFFFFFFFFFFFF effectively covers the whole file. |
| Key | ULONG | in | Cookie correlating this lock with a later NtUnlockFile / NtLockFile by the same key. |
| FailImmediately | BOOLEAN | in | If TRUE, return STATUS_LOCK_NOT_GRANTED instead of waiting when the range is already locked. |
| ExclusiveLock | BOOLEAN | in | If TRUE, request an exclusive (write) lock; FALSE asks for a shared (read) lock. |
Syscall IDs by Windows version
| Windows version | Syscall ID | Build |
|---|---|---|
| Win10 1507 | 0xF9 | win10-1507 |
| Win10 1607 | 0xFE | win10-1607 |
| Win10 1703 | 0x102 | win10-1703 |
| Win10 1709 | 0x103 | win10-1709 |
| Win10 1803 | 0x104 | win10-1803 |
| Win10 1809 | 0x104 | win10-1809 |
| Win10 1903 | 0x105 | win10-1903 |
| Win10 1909 | 0x105 | win10-1909 |
| Win10 2004 | 0x10A | win10-2004 |
| Win10 20H2 | 0x10A | win10-20h2 |
| Win10 21H1 | 0x10A | win10-21h1 |
| Win10 21H2 | 0x10B | win10-21h2 |
| Win10 22H2 | 0x10B | win10-22h2 |
| Win11 21H2 | 0x111 | win11-21h2 |
| Win11 22H2 | 0x112 | win11-22h2 |
| Win11 23H2 | 0x112 | win11-23h2 |
| Win11 24H2 | 0x114 | win11-24h2 |
| Server 2016 | 0xFE | winserver-2016 |
| Server 2019 | 0x104 | winserver-2019 |
| Server 2022 | 0x110 | winserver-2022 |
| Server 2025 | 0x114 | winserver-2025 |
Kernel module
Related APIs
Syscall stub
4C 8B D1 mov r10, rcx B8 14 01 00 00 mov eax, 0x114 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
Undocumented notes
Byte-range locks are *mandatory* on Windows (unlike most Unixes' advisory locks): an unrelated process attempting an overlapping read/write hits STATUS_FILE_LOCK_CONFLICT (ERROR_LOCK_VIOLATION 33) even if it never tried to lock. Locks are released when the file handle closes, but a process holding an open handle plus a 0…INT64_MAX exclusive lock effectively forbids any other process from reading the file content. The Key parameter lets a single owner stack many overlapping locks and release them by cookie.
Common malware usage
Two distinct abuses. (1) **Anti-analysis self-locking**: the implant opens its own backing file with FILE_SHARE_READ stripped and then takes an exclusive lock over the entire range. AV scanners attempting to read the file for static signature matching hit ERROR_LOCK_VIOLATION; many naive scanners log and skip. Combined with the lock-on-execute property of FILE_EXECUTE handles, this makes in-place deletion fail until the process exits. (2) **Ransomware UX manipulation**: encryptors take exclusive locks on the ransom-note file during the encryption phase so the victim cannot delete the note out of the way; once encryption completes the lock is released and the note becomes visible/modifiable. Some wipers also lock the MFT-equivalent metadata file to delay recovery tooling.
Detection opportunities
NtLockFile is *high volume* — every Office document, SQLite database, Outlook PST, and Git pack file uses it constantly. Useful filters: locks held over executable files (.exe/.dll/.scr) by the *same* process that opened the file are unusual outside of debuggers. ETW Microsoft-Windows-Kernel-FileIO emits FileIo/OperationEnd with Lock/Unlock subtypes but is volume-heavy. The most actionable telemetry is the *failure* side: a spike of ERROR_LOCK_VIOLATION (33) responses to legitimate scanners is a downstream indicator that something is self-locking. EDR minifilters can observe IRP_MJ_LOCK_CONTROL directly and correlate with the locking process image.
Direct syscall examples
cSelf-lock anti-analysis stub
// Re-open our own image without FILE_SHARE_READ, then take a whole-file
// exclusive lock. AV scanners that try to read it for static signature
// matching hit STATUS_FILE_LOCK_CONFLICT and move on.
HANDLE hSelf = CreateFileW(g_selfPath,
GENERIC_READ, 0 /* no share */, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
IO_STATUS_BLOCK iosb;
LARGE_INTEGER off = { .QuadPart = 0 };
LARGE_INTEGER len = { .QuadPart = 0x7FFFFFFFFFFFFFFFLL };
NtLockFile(hSelf, NULL, NULL, NULL, &iosb,
&off, &len, 0,
TRUE, // FailImmediately
TRUE); // ExclusiveLockasmx64 direct stub (Win11 24H2 SSN 0x114)
NtLockFile PROC
mov r10, rcx
mov eax, 114h
syscall
ret
NtLockFile ENDPrustRansom-note lock during encryption
// Cargo: ntapi = "0.4", windows-sys = "0.59"
// Hold an exclusive lock on the note while we burn through the file tree.
unsafe {
let off: i64 = 0;
let len: i64 = i64::MAX;
let mut iosb = zeroed();
let st = NtLockFile(h_note,
null_mut(), None, null_mut(),
&mut iosb,
&off as *const _ as _, &len as *const _ as _,
0,
1, // FailImmediately
1); // ExclusiveLock
assert!(st == 0);
}
// ...encrypt files...
unsafe { NtUnlockFile(h_note, &mut iosb, &off as *const _ as _, &len as *const _ as _, 0); }MITRE ATT&CK mappings
Last verified: 2026-05-20