NtCompareSigningLevels
Compare deux valeurs SE_SIGNING_LEVEL selon l'ordre de policy Code Integrity et indique si la première domine la seconde.
Prototype
NTSTATUS NtCompareSigningLevels( SE_SIGNING_LEVEL FirstSigningLevel, SE_SIGNING_LEVEL SecondSigningLevel );
Arguments
| Name | Type | Dir | Description |
|---|---|---|---|
| FirstSigningLevel | SE_SIGNING_LEVEL | in | Premier niveau de signature à comparer (0 Unchecked, 4 Authenticode, 6 Store, 8 Antimalware, 12 Microsoft, 14 Windows). |
| SecondSigningLevel | SE_SIGNING_LEVEL | in | Second niveau de signature à comparer au premier. |
IDs de syscalls par version de Windows
| Version 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 |
Module noyau
APIs liées
Stub du 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
Notes non documentées
Un helper de comparaison pure exposé comme syscall purement pour que la policy d'ordre de Code Integrity vive en *un* seul endroit — le noyau — plutôt que d'être ré-implémentée dans chaque composant user-mode qui en a besoin. Renvoie `STATUS_SUCCESS` (0) quand `FirstSigningLevel` est supérieur ou égal à `SecondSigningLevel` dans le treillis CI, et `STATUS_NOT_FOUND` (0xC0000225) sinon. Important : l'ordre n'est *pas* numérique : bien que les constantes soient par hasard monotones (`0 < 4 < 6 < 8 < 12 < 14`), le noyau passe par `CiCompareSigningLevels` dans `ci.dll`, qui applique des équivalences définies par policy (par exemple `SE_SIGNING_LEVEL_STORE` et `SE_SIGNING_LEVEL_ANTIMALWARE` peuvent être traités comme équivalents sous certains drapeaux de policy WHQL). D'où le syscall — pour centraliser cette policy. Introduit dans Windows 10 1703 avec le reste de la surface API Code Integrity moderne ; non présent sur Windows 10 1507 / 1607.
Usage courant par les malwares
Valeur offensive quasi nulle. C'est une fonction *pure* (sans effet de bord, sans privilège, sans accès à des ressources) qui prend deux entiers et renvoie un status — il n'y a rien à abuser. La seule raison réelle pour qu'un attaquant l'appelle est la bookkeeping : les loaders red-team ayant construit une primitive CIG-bypass peuvent l'appeler pour revérifier que le niveau qu'ils ont forgé domine le niveau requis par la cible, avant de s'engager dans l'injection. Sinon aucune famille de malware n'est documentée comme l'utilisant, et les EDR l'ignorent.
Opportunités de détection
Pas un point de détection significatif. La fonction n'a aucun effet de bord observable, l'ETW ne logge pas d'évènement par appel pour elle, et le volume issu de l'auto-usage légitime de `ci.dll` est suffisamment élevé pour que le hunt basé ratio soit impraticable. L'effort de détection est mieux dépensé sur ses *appelants* — toute personne appelant `NtCompareSigningLevels` depuis l'extérieur de `ci.dll`, `wldp.dll`, `lsass.exe` et une poignée de composants Defender est inhabituel mais pas nécessairement malicieux. Avec de la télémétrie syscall user-mode, joindre les appels `NtCompareSigningLevels` avec les appels adjacents `NtSetCachedSigningLevel` / `NtGetCachedSigningLevel` depuis le même thread est un signal bien plus fort que cet appel isolé.
Exemples de syscalls directs
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 ENDPMappings MITRE ATT&CK
Last verified: 2026-05-20