> Windows Syscalls
ntoskrnl.exeT1027.011T1027T1106

NtUnlockVirtualMemory

Hebt eine zuvor von NtLockVirtualMemory gesetzte Working-Set-Sperre auf.

Prototyp

NTSTATUS NtUnlockVirtualMemory(
  HANDLE  ProcessHandle,
  PVOID  *BaseAddress,
  PSIZE_T RegionSize,
  ULONG   MapType
);

Argumente

NameTypeDirDescription
ProcessHandleHANDLEinHandle des Zielprozesses. Fast immer NtCurrentProcess() ((HANDLE)-1).
BaseAddressPVOID*in/outZeiger auf die Basis der zu entsperrenden Region. Beim Rückkehren seitenausgerichtet.
RegionSizePSIZE_Tin/outZeiger auf die Größe in Bytes. Beim Rückkehren auf ein Vielfaches der Seitengröße aufgerundet.
MapTypeULONGinMuss dem MapType beim Sperren entsprechen — MAP_PROCESS (1) für die VirtualUnlock-Semantik, MAP_SYSTEM (2) für kernel-verankerte Seiten.

Syscall-IDs pro Windows-Version

Windows-VersionSyscall-IDBuild
Win10 15070x1AEwin10-1507
Win10 16070x1B7win10-1607
Win10 17030x1BDwin10-1703
Win10 17090x1C1win10-1709
Win10 18030x1C3win10-1803
Win10 18090x1C4win10-1809
Win10 19030x1C5win10-1903
Win10 19090x1C5win10-1909
Win10 20040x1CBwin10-2004
Win10 20H20x1CBwin10-20h2
Win10 21H10x1CBwin10-21h1
Win10 21H20x1CDwin10-21h2
Win10 22H20x1CDwin10-22h2
Win11 21H20x1D7win11-21h2
Win11 22H20x1DBwin11-22h2
Win11 23H20x1DBwin11-23h2
Win11 24H20x1DEwin11-24h2
Server 20160x1B7winserver-2016
Server 20190x1C4winserver-2019
Server 20220x1D3winserver-2022
Server 20250x1DEwinserver-2025

Kernel-Modul

ntoskrnl.exeNtUnlockVirtualMemory

Verwandte APIs

VirtualUnlockVirtualLockNtLockVirtualMemoryNtFreeVirtualMemoryNtProtectVirtualMemory

Syscall-Stub

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

Das strikte Pendant zu NtLockVirtualMemory. Die SSN driftet bei jedem Major-Release (`0x1C5` Win10 1903, `0x1CB` 2004, `0x1DE` Win11 24H2 / Server 2025). Der Aufruf auf einer nicht gesperrten Region gibt `STATUS_NOT_LOCKED` (0xC000002A) zurück — harmlos; mit nicht passendem `MapType` gibt es `STATUS_INVALID_PARAMETER`. Die Kernel-Implementierung senkt das Working-Set-Minimum wieder ab, lagert die Region aber nicht aktiv aus — sie erlaubt nur den Trim. Praktisch jeder legitime Einsatz ist eins-zu-eins mit einem vorherigen NtLockVirtualMemory gepaart.

Häufige Malware-Nutzung

Die Aufwach-Seite des unter NtLockVirtualMemory dokumentierten Sleep-Mask-Musters. Ekko, Foliage und die Cobalt-Strike-`Sleep_Mask`-Familie nutzen einen `Lock → Verschlüsseln → Schlafen → Entschlüsseln → Unlock`-Zyklus bei jedem Beacon-Callback-Intervall; das passende `Unlock` gibt das Working-Set-Quota frei, damit nachfolgende Allokationen nicht verdrängt werden. Einige Packer entsperren die OEP-Region auch nach einem Watchdog-Timeout, um ihren sichtbaren Working-Set-Fußabdruck zu reduzieren. Für sich allein ist NtUnlockVirtualMemory praktisch nie der Brennpunkt böswilligen Verhaltens — sein Auftreten ist vor allem als *Pairing*-Signal neben einem NtLockVirtualMemory desselben Threads auf derselben Range früher im Trace nützlich.

Erkennungs­möglichkeiten

Das *Paar* erkennen, nicht den einzelnen Aufruf. Die klassische hochsignalbringende Sequenz innerhalb eines Threads: `NtAllocateVirtualMemory(RWX)` → `NtLockVirtualMemory(MAP_PROCESS)` → `NtDelayExecution(lang)` → `NtUnlockVirtualMemory` → ausführen. User-Mode-VirtualUnlock-Thunk hooken und mit der vorherigen VirtualLock-Stelle sowie den Schutzbits der Allokation korrelieren. Memory-Scanner, die Working-Set-Mitgliedschaft über die Zeit snapshotten, können den Lock-dann-Unlock-Puls manchmal erfassen — viele EDRs halten diese Historie aber nicht vor. Wie bei NtLockVirtualMemory existiert kein erstklassiges ETW-Event; Verhaltensverkettung ist der Weg.

Direkte Syscall-Beispiele

asmx64 stub (Win11 24H2 SSN 0x1DE)

; Direct syscall stub for NtUnlockVirtualMemory
NtUnlockVirtualMemory PROC
    mov  r10, rcx          ; syscall convention
    mov  eax, 1DEh         ; SSN (Win11 24H2 / Server 2025)
    syscall
    ret
NtUnlockVirtualMemory ENDP

cWake side of an Ekko-style sleep

// Symmetric counterpart to the SleepWithLockedRegion example: after the
// caller wakes and decrypts in place, unlock so the working-set quota is
// returned and subsequent allocations aren't crowded.
#include <windows.h>

typedef NTSTATUS (NTAPI *pNtUnlockVirtualMemory)(HANDLE, PVOID*, PSIZE_T, ULONG);
#define MAP_PROCESS 1

NTSTATUS UnlockRegion(PVOID base, SIZE_T size) {
    pNtUnlockVirtualMemory NtUnlockVirtualMemory = (pNtUnlockVirtualMemory)
        GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtUnlockVirtualMemory");
    PVOID  b = base;
    SIZE_T s = size;
    return NtUnlockVirtualMemory((HANDLE)-1, &b, &s, MAP_PROCESS);
}

rustRAII wrapper that pairs Lock / Unlock

// Cargo: windows-sys = "0.59" (Win32_System_Memory)
use windows_sys::Win32::System::Memory::{VirtualLock, VirtualUnlock};

pub struct Pinned { ptr: *mut u8, len: usize }

impl Pinned {
    pub fn new(p: *mut u8, len: usize) -> std::io::Result<Self> {
        if unsafe { VirtualLock(p as _, len) } == 0 {
            return Err(std::io::Error::last_os_error());
        }
        Ok(Self { ptr: p, len })
    }
}

impl Drop for Pinned {
    fn drop(&mut self) {
        // VirtualUnlock — underneath, NtUnlockVirtualMemory(MAP_PROCESS).
        unsafe { VirtualUnlock(self.ptr as _, self.len); }
    }
}

MITRE ATT&CK-Mappings

Last verified: 2026-05-20