NtSetCachedSigningLevel
Writes a Code Integrity cached signing-level result into an NTFS extended attribute on the target file.
Prototype
NTSTATUS NtSetCachedSigningLevel( ULONG Flags, SE_SIGNING_LEVEL InputSigningLevel, PHANDLE SourceFiles, ULONG SourceFileCount, HANDLE TargetFile );
Arguments
| Name | Type | Dir | Description |
|---|---|---|---|
| Flags | ULONG | in | Bitmask controlling cache behaviour; 0x4 forces re-evaluation, 0x8 skips trust verification. |
| InputSigningLevel | SE_SIGNING_LEVEL | in | Requested signing level to stamp: 0=Unchecked, 4=Authenticode, 6=Store, 8=Antimalware, 12=Microsoft, 14=Windows. |
| SourceFiles | PHANDLE | in | Array of file handles whose cached signing level should be inherited; usually NULL. |
| SourceFileCount | ULONG | in | Number of entries in SourceFiles; 0 when SourceFiles is NULL. |
| TargetFile | HANDLE | in | Handle to the file whose $Kernel.Purge.ESBCache extended attribute will be written. |
Syscall IDs by Windows version
| Windows version | Syscall ID | Build |
|---|---|---|
| Win10 1507 | 0x16E | win10-1507 |
| Win10 1607 | 0x176 | win10-1607 |
| Win10 1703 | 0x17C | win10-1703 |
| Win10 1709 | 0x17F | win10-1709 |
| Win10 1803 | 0x181 | win10-1803 |
| Win10 1809 | 0x182 | win10-1809 |
| Win10 1903 | 0x183 | win10-1903 |
| Win10 1909 | 0x183 | win10-1909 |
| Win10 2004 | 0x189 | win10-2004 |
| Win10 20H2 | 0x189 | win10-20h2 |
| Win10 21H1 | 0x189 | win10-21h1 |
| Win10 21H2 | 0x18B | win10-21h2 |
| Win10 22H2 | 0x18B | win10-22h2 |
| Win11 21H2 | 0x193 | win11-21h2 |
| Win11 22H2 | 0x196 | win11-22h2 |
| Win11 23H2 | 0x196 | win11-23h2 |
| Win11 24H2 | 0x198 | win11-24h2 |
| Server 2016 | 0x176 | winserver-2016 |
| Server 2019 | 0x182 | winserver-2019 |
| Server 2022 | 0x191 | winserver-2022 |
| Server 2025 | 0x198 | winserver-2025 |
Kernel module
Related APIs
Syscall stub
4C 8B D1 mov r10, rcx B8 98 01 00 00 mov eax, 0x198 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
Undocumented notes
Part of the **Code Integrity (CI)** infrastructure introduced with Windows 10. CI maintains an EA-cached signing verdict on every executable file so that subsequent image loads avoid the expensive Authenticode hash walk. The cache lives in the NTFS extended attribute `$Kernel.Purge.ESBCache` (the EA is hidden — `fsutil file queryea` won't show it by default; CI uses a private FSCTL). `NtSetCachedSigningLevel` is the *writer* — it asks CI (`ci.dll` driver inside ntoskrnl's CI module) to compute or accept a signing level and stamp it into the EA. The companion `NtGetCachedSigningLevel` is the *reader*. The privileged levels (8 Antimalware, 12 Microsoft, 14 Windows) require `SeTcbPrivilege` *and* the calling process itself to be running at an equal or higher CI level — so a normal user-mode process cannot just stamp `SE_SIGNING_LEVEL_WINDOWS`. The call became attractive to researchers because *some* of its parameter validation lives in CI policy, not in `Nt*` itself, and historical bugs in policy resolution have been used to coerce CI into accepting unsigned files.
Common malware usage
Not a commodity-malware syscall, but a recurring **research vector for sandbox escapes and CIG (Code Integrity Guard) bypasses**. James Forshaw (Project Zero) documented multiple paths where `NtSetCachedSigningLevel` could be coerced into stamping an attacker-controlled DLL with a `Microsoft`-level cache entry — sufficient to slip the DLL past a process protected with `ProcessSignaturePolicy = MicrosoftSignedOnly`. Edge/Chrome sandbox-escape PoCs (CVE-2020-0938 era and follow-ups) used this to load attacker code into the browser broker after RCE in the renderer. The Print Spooler / `Win32k` bug classes that intersect with App Container also touch this path. **Real-world malware essentially never calls this** — abuse appears in red-team / research code, **PPLFault** (Gabriel Landau, 2022), and Process-Mitigation-Policy bypasses. If you see a non-Microsoft-signed process calling `NtSetCachedSigningLevel(SE_SIGNING_LEVEL_WINDOWS)` you are looking at exploitation, not normal behaviour.
Detection opportunities
ETW `Microsoft-Windows-CodeIntegrity` provider (GUID `{4ee76bd8-3cf4-44a0-a0ac-3937643e37a3}`) emits event 3023 / 3033 on cache writes, including the requested signing level and the calling process image path. CI also logs to the Application & Services / `CodeIntegrity-Operational` channel — events 3033 (file blocked) and 3089 (signature info) reference cache lookups. The strongest signal is: any process whose own image is not signed at or above `SE_SIGNING_LEVEL_MICROSOFT` calling `NtSetCachedSigningLevel` at all, and especially with `InputSigningLevel >= 8`. Defender's `Block credential stealing from the Windows local security authority subsystem` ASR rule indirectly catches some abuse paths because the post-exploitation step usually injects into a protected process.
Direct syscall examples
asmx64 direct stub (Win11 24H2)
; Direct syscall stub for NtSetCachedSigningLevel (SSN 0x198 on Win11 24H2 / Server 2025)
NtSetCachedSigningLevel PROC
mov r10, rcx ; syscall convention
mov eax, 198h ; SSN — drifts; resolve dynamically for portability
syscall
ret
NtSetCachedSigningLevel ENDPcCIG-bypass research pattern
// Forshaw-style CI cache stamping. Requires the calling process to already
// run at SE_SIGNING_LEVEL_MICROSOFT or above — the abuse vector is *getting
// that level* via a policy resolution bug, not this call itself.
#define SE_SIGNING_LEVEL_UNCHECKED 0
#define SE_SIGNING_LEVEL_AUTHENTICODE 4
#define SE_SIGNING_LEVEL_STORE 6
#define SE_SIGNING_LEVEL_ANTIMALWARE 8
#define SE_SIGNING_LEVEL_MICROSOFT 12
#define SE_SIGNING_LEVEL_WINDOWS 14
HANDLE hFile = /* opened with FILE_READ_DATA | FILE_WRITE_EA */;
NTSTATUS st = NtSetCachedSigningLevel(
/* Flags */ 0x4, // force recompute
/* InputSigningLevel */ SE_SIGNING_LEVEL_MICROSOFT,
/* SourceFiles */ NULL,
/* SourceFileCount */ 0,
/* TargetFile */ hFile);cRead the resulting EA with NtGetCachedSigningLevel
// Verify the cache stamp succeeded by reading it back.
ULONG flags = 0, level = 0, thumbAlg = 0, thumbSize = 32;
BYTE thumb[32];
LARGE_INTEGER lastBlackList = { 0 }, lastTimeStamp = { 0 };
NTSTATUS st = NtGetCachedSigningLevel(
hFile, &flags, (PSE_SIGNING_LEVEL)&level,
thumb, &thumbSize, &thumbAlg,
&lastBlackList, &lastTimeStamp);
// level should now equal what NtSetCachedSigningLevel stamped, e.g. 12 (Microsoft).MITRE ATT&CK mappings
Last verified: 2026-05-20