NtCompareObjects
Liefert STATUS_SUCCESS, wenn zwei Handles auf dasselbe zugrundeliegende Kernel-Objekt verweisen.
Prototyp
NTSTATUS NtCompareObjects( HANDLE FirstObjectHandle, HANDLE SecondObjectHandle );
Argumente
| Name | Type | Dir | Description |
|---|---|---|---|
| FirstObjectHandle | HANDLE | in | Erstes zu vergleichendes Handle; jede Access-Maske wird akzeptiert. |
| SecondObjectHandle | HANDLE | in | Zweites zu vergleichendes Handle; jede Access-Maske wird akzeptiert. |
Syscall-IDs pro Windows-Version
| Windows-Version | Syscall-ID | Build |
|---|---|---|
| Win10 1507 | 0x95 | win10-1507 |
| Win10 1607 | 0x96 | win10-1607 |
| Win10 1703 | 0x97 | win10-1703 |
| Win10 1709 | 0x98 | win10-1709 |
| Win10 1803 | 0x99 | win10-1803 |
| Win10 1809 | 0x99 | win10-1809 |
| Win10 1903 | 0x99 | win10-1903 |
| Win10 1909 | 0x99 | win10-1909 |
| Win10 2004 | 0x9B | win10-2004 |
| Win10 20H2 | 0x9B | win10-20h2 |
| Win10 21H1 | 0x9B | win10-21h1 |
| Win10 21H2 | 0x9B | win10-21h2 |
| Win10 22H2 | 0x9B | win10-22h2 |
| Win11 21H2 | 0x9D | win11-21h2 |
| Win11 22H2 | 0x9D | win11-22h2 |
| Win11 23H2 | 0x9D | win11-23h2 |
| Win11 24H2 | 0x9F | win11-24h2 |
| Server 2016 | 0x96 | winserver-2016 |
| Server 2019 | 0x99 | winserver-2019 |
| Server 2022 | 0x9D | winserver-2022 |
| Server 2025 | 0x9F | winserver-2025 |
Kernel-Modul
Verwandte APIs
Syscall-Stub
4C 8B D1 mov r10, rcx B8 9F 00 00 00 mov eax, 0x9F 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
Eingeführt in Windows 10 1507, um User-Mode einen ehrlichen Gleichheitstest für Kernel-Objekte zu geben. Vor diesem Syscall war der einzige verlässliche Weg, zu wissen, ob zwei Handles dasselbe Objekt aliasten, ein Stapel fehleranfälliger Heuristiken (Objektnamen vergleichen, wo verfügbar, PIDs für Prozess-Handles vergleichen, usw.). Intern löst `NtCompareObjects` beide Handles über `ObReferenceObjectByHandle` zu ihrem zugrundeliegenden `_OBJECT_HEADER *` auf, testet Pointer-Gleichheit und dereferenziert dann beide. Die Implementierung steht in `ObCompareObjectsForCommonSecurity`. Rückgabe ist binär: `STATUS_SUCCESS` (gleiches Objekt), `STATUS_NOT_SAME_OBJECT` (verschiedene Objekte) oder eine der üblichen `STATUS_INVALID_HANDLE`-Varianten. Typen müssen nicht übereinstimmen — vergleicht man ein `Process`-Handle mit einem `Token`-Handle, kommt einfach `STATUS_NOT_SAME_OBJECT`. Es gibt *keinen* öffentlichen Win32-Wrapper; das Nächste ist `CompareObjectHandles`, ausschließlich über das Windows SDK als ucrt-Inline ausgeliefert, das in das undokumentierte `CompareObjectHandles` von `KernelBase.dll` aufruft (Win10 1809+).
Häufige Malware-Nutzung
Geringes Malware-Signal. Wird an zwei Stellen verwendet: (1) EDR-Introspektionscode, der bestätigen will, dass ein dupliziertes Handle noch dasselbe Zielobjekt referenziert, und (2) bestimmte Implant-Designs (Cobalt-Strike-BOFs, einige Sliver-Erweiterungen) nutzen ihn als leichten OPSEC-Check, bevor sie z. B. `NtDuplicateObject` auf einem Handle aufrufen, das sie verdächtigen, bereits von einem EDR-kontrollierten Prozess gegrabbt worden zu sein. Auch ein praktischer Handle-zu-Handle-Gleichheitstest in Post-Exploitation-Tooling, das die System-Handle-Tabelle (`NtQuerySystemInformation(SystemHandleInformation)`) durchläuft und Aliase deduplizieren will. Er *umgeht* nichts selbst — ist eine Primitive für andere Primitiven.
Erkennungsmöglichkeiten
Kein ETW-Provider emittiert Per-Call-Telemetrie für diesen Syscall, und Sysmon legt ihn nicht offen. Defender alarmieren generell nicht auf `NtCompareObjects`, weil legitime Nutzung selten-aber-echt ist (KernelBase!CompareObjectHandles wird von Teilen von `Win32k`, `ImmersiveShell` und `RuntimeBroker.exe` aufgerufen). Interessant wird es nur, wenn ein unsigniertes oder frisch geladenes Modul plötzlich viele `NtCompareObjects` in einer engen Schleife gegen die System-Handle-Tabelle absetzt — dieses Muster ist die Post-Exploitation-Handle-Walk-Signatur, der Aufruf selbst ist aber ein schlechter Primärindikator.
Direkte Syscall-Beispiele
asmx64 direct stub (Win11 24H2)
; Direct syscall stub for NtCompareObjects (SSN 0x9F on Win11 24H2 / Server 2025)
NtCompareObjects PROC
mov r10, rcx ; syscall convention
mov eax, 9Fh ; SSN — drifts; resolve dynamically for portability
syscall
ret
NtCompareObjects ENDPcDeduplicate aliased handles after NtQuerySystemInformation
// Walks a handle list and collapses entries that alias the same object —
// a common post-exploitation cleanup before reporting unique findings.
for (ULONG i = 0; i < count; ++i) {
for (ULONG j = i + 1; j < count; ++j) {
if (NtCompareObjects(handles[i], handles[j]) == STATUS_SUCCESS) {
// aliased -> drop handles[j] from the result set
handles[j] = NULL;
}
}
}rustCompareObjectHandles wrapper (windows-sys)
// Cargo: windows-sys = "0.59" (Win32_Foundation, Win32_System_Threading)
// CompareObjectHandles is a thin KernelBase wrapper over NtCompareObjects.
use windows_sys::Win32::Foundation::{CompareObjectHandles, HANDLE};
fn same_object(a: HANDLE, b: HANDLE) -> bool {
unsafe { CompareObjectHandles(a, b) != 0 }
}MITRE ATT&CK-Mappings
Last verified: 2026-05-20