NtCompareObjects
Devuelve STATUS_SUCCESS cuando dos handles refieren al mismo objeto kernel subyacente.
Prototipo
NTSTATUS NtCompareObjects( HANDLE FirstObjectHandle, HANDLE SecondObjectHandle );
Argumentos
| Name | Type | Dir | Description |
|---|---|---|---|
| FirstObjectHandle | HANDLE | in | Primer handle a comparar; se acepta cualquier máscara de acceso. |
| SecondObjectHandle | HANDLE | in | Segundo handle a comparar; se acepta cualquier máscara de acceso. |
IDs de syscalls por versión de Windows
| Versión de Windows | ID de syscall | 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 |
Módulo del kernel
APIs relacionadas
Stub del syscall
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
Notas no documentadas
Añadido en Windows 10 1507 para dar al user-mode un test honesto de igualdad de objetos kernel. Antes de este syscall, la única forma fiable de saber si dos handles aliasaban el mismo objeto era una pila de heurísticas falibles (comparar nombres de objeto cuando disponibles, comparar PIDs para handles de proceso, etc.). Internamente `NtCompareObjects` resuelve ambos handles a su `_OBJECT_HEADER *` subyacente vía `ObReferenceObjectByHandle`, prueba igualdad de punteros y luego dereferencia ambos. La implementación está en `ObCompareObjectsForCommonSecurity`. El retorno es binario: `STATUS_SUCCESS` (mismo objeto), `STATUS_NOT_SAME_OBJECT` (objetos diferentes), o alguna variante usual de `STATUS_INVALID_HANDLE`. Los tipos no necesitan coincidir — comparar un handle `Process` con un handle `Token` simplemente devuelve `STATUS_NOT_SAME_OBJECT`. *No* hay wrapper Win32 público; lo más cercano es `CompareObjectHandles` enviado solo vía Windows SDK como inline ucrt que llama al `CompareObjectHandles` no documentado de `KernelBase.dll` (Win10 1809+).
Uso común por malware
Señal de malware baja. Usado en dos lugares donde aparece: (1) código de introspección EDR que quiere confirmar que un handle duplicado aún apunta al mismo objeto objetivo, y (2) ciertos diseños de implante (BOFs de Cobalt Strike, algunas extensiones Sliver) lo usan como check OPSEC ligero antes de, por ejemplo, llamar `NtDuplicateObject` sobre un handle que sospechan ya fue injertado por un proceso controlado por EDR. También es un test conveniente de igualdad handle-a-handle en herramientas post-explotación que recorren la tabla de handles del sistema (`NtQuerySystemInformation(SystemHandleInformation)`) y quieren deduplicar aliases. *No* bypassea nada por sí mismo — es una primitiva usada por otras primitivas.
Oportunidades de detección
No hay provider ETW que emita telemetría por llamada para este syscall y Sysmon no lo expone. Los defensores generalmente no alertan sobre `NtCompareObjects` porque el uso legítimo es raro-pero-real (KernelBase!CompareObjectHandles es llamado por partes de `Win32k`, `ImmersiveShell` y `RuntimeBroker.exe`). El único punto donde se vuelve interesante es cuando un módulo no firmado o recién cargado emite de repente muchos `NtCompareObjects` en bucle apretado contra la tabla de handles del sistema — ese patrón es la firma de handle-walk post-explotación, pero la llamada en sí es un mal indicador primario.
Ejemplos de syscalls directos
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 }
}Mapeos MITRE ATT&CK
Last verified: 2026-05-20