NtClose
Schließt ein Kernel-Objekt-Handle (Datei, Schlüssel, Event, Prozess, Thread, Section usw.).
Prototyp
NTSTATUS NtClose( HANDLE Handle );
Argumente
| Name | Type | Dir | Description |
|---|---|---|---|
| Handle | HANDLE | in | Handle des zu schließenden Kernel-Objekts. Muss ein gültiges offenes Handle im aktuellen Prozess sein. |
Syscall-IDs pro Windows-Version
| Windows-Version | Syscall-ID | Build |
|---|---|---|
| Win10 1507 | 0xF | win10-1507 |
| Win10 1607 | 0xF | win10-1607 |
| Win10 1703 | 0xF | win10-1703 |
| Win10 1709 | 0xF | win10-1709 |
| Win10 1803 | 0xF | win10-1803 |
| Win10 1809 | 0xF | win10-1809 |
| Win10 1903 | 0xF | win10-1903 |
| Win10 1909 | 0xF | win10-1909 |
| Win10 2004 | 0xF | win10-2004 |
| Win10 20H2 | 0xF | win10-20h2 |
| Win10 21H1 | 0xF | win10-21h1 |
| Win10 21H2 | 0xF | win10-21h2 |
| Win10 22H2 | 0xF | win10-22h2 |
| Win11 21H2 | 0xF | win11-21h2 |
| Win11 22H2 | 0xF | win11-22h2 |
| Win11 23H2 | 0xF | win11-23h2 |
| Win11 24H2 | 0xF | win11-24h2 |
| Server 2016 | 0xF | winserver-2016 |
| Server 2019 | 0xF | winserver-2019 |
| Server 2022 | 0xF | winserver-2022 |
| Server 2025 | 0xF | winserver-2025 |
Kernel-Modul
Verwandte APIs
Syscall-Stub
4C 8B D1 mov r10, rcx B8 0F 00 00 00 mov eax, 0xF 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
NtClose trägt SSN `0xF` unverändert von Windows 10 1507 bis Windows 11 24H2 — eine der stabilsten Nummern in der Tabelle. Es löst die Referenz auf das zugrunde liegende Objekt über ObCloseHandle aus und wirft die Debug-Exception STATUS_INVALID_HANDLE (0xC0000008), wenn es mit einem ungültigen Handle bei angehängtem Debugger und gesetztem FLG_ENABLE_CLOSE_EXCEPTIONS aufgerufen wird. Trivial in der Form, aber einer der am häufigsten aufgerufenen Syscalls auf einem laufenden System — schlechtes Signal-Rausch-Verhältnis für die Detektion und ein bequemes Versteck für Missbrauch.
Häufige Malware-Nutzung
Zwei klare Nutzungen: (1) Routinehygiene — jeder Loader, Injektor und Credential-Stealer muss seine Handles freigeben, sonst leakt er Referenzen und alarmiert Telemetrie; (2) Anti-Debugging — Aufruf von NtClose mit einem ungültigen Handle (z. B. 0xDEADBEEF) unter einem User-Mode-Debugger erzeugt STATUS_INVALID_HANDLE, das der Debugger zuerst abfängt, woraufhin Malware abbricht oder in Decoy-Logik abzweigt. Der Check ist billig, benötigt keine Imports außerhalb von ntdll und überlebt die meisten automatischen Unpacker.
Erkennungsmöglichkeiten
NtClose allein ist zu rauschig, um darauf zu alarmieren. Nützliche Pivots: hochfrequente NtClose-Sequenzen mit Nicht-SUCCESS-Returns (Heuristik für Anti-Debug-Probing), unausgeglichene NtOpen*/NtClose-Verhältnisse pro Prozess (Handle-Leaks aus schlampigen Injektoren) und ETW Microsoft-Windows-Kernel-Audit-API-Calls. EDRs hooken NtClose in ntdll häufig wegen Referenzzählungskonsistenz; direkte Syscalls umgehen den Hook, hinterlassen aber ObCloseHandle-Spuren, die für Minifilter oder Kernel-Callbacks sichtbar bleiben.
Direkte Syscall-Beispiele
asmx64 direct stub
; Direct syscall stub for NtClose (SSN 0xF, all builds)
NtClose PROC
mov r10, rcx ; syscall convention
mov eax, 0Fh ; SSN
syscall
ret
NtClose ENDPcINVALID_HANDLE anti-debug probe
// If a debugger is attached and FLG_ENABLE_CLOSE_EXCEPTIONS is set in the
// process flags, NtClose on a bogus handle raises STATUS_INVALID_HANDLE.
// The debugger swallows the exception first; the malware sees a return code.
#include <windows.h>
typedef NTSTATUS (NTAPI *pNtClose)(HANDLE);
BOOL IsDebuggerPresentViaNtClose(void) {
pNtClose NtClose = (pNtClose)GetProcAddress(
GetModuleHandleA("ntdll.dll"), "NtClose");
__try {
NtClose((HANDLE)0xDEADBEEF);
} __except (EXCEPTION_EXECUTE_HANDLER) {
return FALSE; // exception delivered to us -> no debugger
}
return TRUE; // debugger ate the exception
}rustwindows-sys cleanup
// Cargo: windows-sys = "0.59" (Win32_Foundation, Win32_System_Threading)
use windows_sys::Win32::Foundation::{CloseHandle, HANDLE};
pub struct OwnedHandle(pub HANDLE);
impl Drop for OwnedHandle {
fn drop(&mut self) {
if !self.0.is_null() && self.0 as isize != -1 {
// CloseHandle wraps NtClose; use NtClose directly to bypass
// ntdll user-mode hooks on EDR-monitored hosts.
unsafe { CloseHandle(self.0) };
}
}
}MITRE ATT&CK-Mappings
Last verified: 2026-05-20