NtCompareSigningLevels
Compara dos valores SE_SIGNING_LEVEL usando el orden de policy de Code Integrity e indica si el primero domina al segundo.
Prototipo
NTSTATUS NtCompareSigningLevels( SE_SIGNING_LEVEL FirstSigningLevel, SE_SIGNING_LEVEL SecondSigningLevel );
Argumentos
| Name | Type | Dir | Description |
|---|---|---|---|
| FirstSigningLevel | SE_SIGNING_LEVEL | in | Primer nivel de firma a comparar (0 Unchecked, 4 Authenticode, 6 Store, 8 Antimalware, 12 Microsoft, 14 Windows). |
| SecondSigningLevel | SE_SIGNING_LEVEL | in | Segundo nivel de firma a comparar contra el primero. |
IDs de syscalls por versión de Windows
| Versión de Windows | ID de syscall | Build |
|---|---|---|
| Win10 1703 | 0x98 | win10-1703 |
| Win10 1709 | 0x99 | win10-1709 |
| Win10 1803 | 0x9A | win10-1803 |
| Win10 1809 | 0x9A | win10-1809 |
| Win10 1903 | 0x9A | win10-1903 |
| Win10 1909 | 0x9A | win10-1909 |
| Win10 2004 | 0x9C | win10-2004 |
| Win10 20H2 | 0x9C | win10-20h2 |
| Win10 21H1 | 0x9C | win10-21h1 |
| Win10 21H2 | 0x9C | win10-21h2 |
| Win10 22H2 | 0x9C | win10-22h2 |
| Win11 21H2 | 0x9E | win11-21h2 |
| Win11 22H2 | 0x9E | win11-22h2 |
| Win11 23H2 | 0x9E | win11-23h2 |
| Win11 24H2 | 0xA0 | win11-24h2 |
| Server 2019 | 0x9A | winserver-2019 |
| Server 2022 | 0x9E | winserver-2022 |
| Server 2025 | 0xA0 | winserver-2025 |
Módulo del kernel
APIs relacionadas
Stub del syscall
4C 8B D1 mov r10, rcx B8 A0 00 00 00 mov eax, 0xA0 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
Un helper de comparación pura expuesto como syscall puramente para que la policy de ordenamiento de Code Integrity viva en *un* solo lugar — el kernel — en vez de re-implementarse en cada componente user-mode que la necesita. Devuelve `STATUS_SUCCESS` (0) cuando `FirstSigningLevel` es mayor o igual a `SecondSigningLevel` en el retículo CI, y `STATUS_NOT_FOUND` (0xC0000225) en caso contrario. Importante: el ordenamiento *no* es numérico: aunque las constantes resultan ser monotónicas (`0 < 4 < 6 < 8 < 12 < 14`), el kernel pasa por `CiCompareSigningLevels` en `ci.dll`, que aplica equivalencias definidas por policy (por ejemplo `SE_SIGNING_LEVEL_STORE` y `SE_SIGNING_LEVEL_ANTIMALWARE` pueden tratarse como equivalentes bajo banderas específicas de policy WHQL). De ahí el syscall — para centralizar esa policy. Introducido en Windows 10 1703 junto con el resto de la superficie API de Code Integrity moderna; no presente en Windows 10 1507 / 1607.
Uso común por malware
Valor ofensivo casi nulo. Es una función *pura* (sin efectos secundarios, sin privilegios, sin acceso a recursos) que toma dos enteros y devuelve un status — no hay nada que abusar. La única razón real para que un atacante la llame es bookkeeping: loaders red-team que han construido una primitiva CIG-bypass pueden llamarla para revisar dos veces que el nivel que han forjado domina el nivel requerido por el objetivo, antes de comprometerse con la inyección. Por lo demás ninguna familia de malware está documentada usándola, y los EDR la ignoran.
Oportunidades de detección
No es un punto de detección significativo. La función no tiene efecto secundario observable, ETW no loguea eventos por llamada para ella, y el volumen del autouso legítimo de `ci.dll` es suficientemente alto como para que el hunt basado en ratio sea impracticable. El esfuerzo de detección se gasta mejor en sus *llamantes* — cualquiera que llame `NtCompareSigningLevels` desde fuera de `ci.dll`, `wldp.dll`, `lsass.exe` y un puñado de componentes Defender es inusual pero no necesariamente malicioso. Con telemetría de syscall user-mode, unir las llamadas `NtCompareSigningLevels` con llamadas adyacentes `NtSetCachedSigningLevel` / `NtGetCachedSigningLevel` desde el mismo hilo es una señal mucho más fuerte que esta llamada aislada.
Ejemplos de syscalls directos
cDoes the cached level satisfy the target's CIG requirement?
// Used internally by ci.dll and by some EDRs to short-circuit policy decisions.
// Returns STATUS_SUCCESS if `have` dominates `need`, STATUS_NOT_FOUND otherwise.
#include <windows.h>
#include <winternl.h>
typedef NTSTATUS (NTAPI* pNtCompareSigningLevels)(UCHAR, UCHAR);
BOOL satisfies(UCHAR have, UCHAR need) {
pNtCompareSigningLevels f = (pNtCompareSigningLevels)GetProcAddress(
GetModuleHandleA("ntdll.dll"), "NtCompareSigningLevels");
return NT_SUCCESS(f(have, need));
}
// Example: do we meet ProcessSignaturePolicy.MicrosoftSignedOnly = 1?
// satisfies(SE_SIGNING_LEVEL_MICROSOFT, SE_SIGNING_LEVEL_MICROSOFT) == TRUE
// satisfies(SE_SIGNING_LEVEL_AUTHENTICODE, SE_SIGNING_LEVEL_MICROSOFT) == FALSEasmx64 direct stub (Win11 24H2 / Server 2025, SSN 0xA0)
NtCompareSigningLevels PROC
mov r10, rcx
mov eax, 0A0h
syscall
ret
NtCompareSigningLevels ENDPMapeos MITRE ATT&CK
Last verified: 2026-05-20