<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Parag Mali - tag: code-signing</title><description>Posts tagged code-signing.</description><link>https://paragmali.com/</link><language>en-US</language><lastBuildDate>Sun, 07 Jun 2026 04:13:11 GMT</lastBuildDate><atom:link href="https://paragmali.com/tags/code-signing/rss.xml" rel="self" type="application/rss+xml"/><item><title>The Driver That Was Signed and the Driver That Won&apos;t Load: Windows Kernel Code Integrity, 2006-2026</title><link>https://paragmali.com/blog/windows-kernel-code-integrity-2006-2026/</link><guid isPermaLink="true">https://paragmali.com/blog/windows-kernel-code-integrity-2006-2026/</guid><description>A history of Windows kernel code-signing -- KMCS, BYOVD, HVCI, the Vulnerable Driver Block List, and why a 2026 Windows kernel uses five gates to decide what loads.</description><pubDate>Thu, 14 May 2026 00:00:00 GMT</pubDate><content:encoded>
**Windows ships a list of Microsoft-signed drivers it refuses to load.** That list -- `DriverSiPolicy.p7b` -- exists because every previous generation of kernel-driver trust assumed a signed driver was a safe driver, and a twenty-year run of Bring-Your-Own-Vulnerable-Driver attacks (Stuxnet, Capcom.sys, RTCore64.sys, gdrv.sys) proved that assumption wrong. The 2026 default-on stack -- KMCS, the block list, HVCI in VTL1, Smart App Control, and Defender ASR coverage -- is five gates doing what one ideal gate cannot do: name the specific weakness, not just the publisher. The architectural gap that motivates the stack is undecidable in principle and will not close.
&lt;h2&gt;1. The Driver That Loaded&lt;/h2&gt;
&lt;p&gt;On 13 September 2016, the researcher Matt Nelson posted on his &lt;em&gt;enigma0x3&lt;/em&gt; blog that a Capcom-published kernel driver, &lt;code&gt;Capcom.sys&lt;/code&gt;, exposed IOCTL &lt;code&gt;0xAA013044&lt;/code&gt; and used it to execute a user-supplied function pointer in kernel mode, with SMEP disabled along the way [@gh-tandasat-capcom] [@gh-tandasat-capcom]. Within two weeks the technique was operational in Metasploit. Later in September 2016, Capcom pushed the same driver to Street Fighter V&apos;s entire installed base as part of an anti-cheat update; in October 2016, Satoshi Tanda published the canonical standalone exploit on GitHub. Capcom withdrew the SFV driver shortly after, but the bytes were already in the wild.The often-told version of this story compresses three distinct events into one. Matt Nelson&apos;s &lt;em&gt;Let&apos;s Be Bad Guys&lt;/em&gt; post on 13 September 2016 disclosed the IOCTL number and the function-pointer-execution primitive. OJ Reeves opened the canonical Metasploit pull request, rapid7/metasploit-framework#7363 [@gh-msf-pr-7363], shortly after; the PR was created on 27 September 2016 and merged the following day [@gh-msf-pr-7363]. Satoshi Tanda&apos;s &lt;code&gt;tandasat/ExploitCapcom&lt;/code&gt; repository was first published in October 2016 and is the canonical standalone PoC, and the artefact this article cites for the IOCTL number and SHA-1 hash.&lt;/p&gt;
&lt;p&gt;The driver was properly Authenticode-signed. It chained to a Microsoft-recognised root. It loaded cleanly on every default-configured Windows 7, 8.1, and 10 machine in the world.&lt;/p&gt;
&lt;p&gt;That is the puzzle this article exists to answer. How does an operating system whose entire kernel-loading policy is &lt;em&gt;was this binary signed?&lt;/em&gt; answer a vulnerability whose only failure mode is &lt;em&gt;yes, by a real publisher, doing exactly what the signature says it does&lt;/em&gt;?&lt;/p&gt;
&lt;h3&gt;A class, not an incident&lt;/h3&gt;
&lt;p&gt;Capcom.sys was not the first signed kernel driver with a primitive IOCTL, and it would not be the last. The pattern recurs across two decades and is the through-line of this article. The catalogue includes Micro-Star&apos;s &lt;code&gt;RTCore64.sys&lt;/code&gt; (the kernel component of MSI Afterburner), Gigabyte&apos;s &lt;code&gt;gdrv.sys&lt;/code&gt;, and the &lt;code&gt;KProcessHacker&lt;/code&gt; driver shipped with Process Hacker. Section 4 walks through each one with its primary disclosure record.&lt;/p&gt;
&lt;p&gt;The attack class has a name. &lt;em&gt;Bring Your Own Vulnerable Driver&lt;/em&gt;, or BYOVD. The adversary does not need to find a kernel zero-day. They need to find one signed driver, anywhere, whose interface is unsafe by design, and to ship it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Windows in 2026 ships a curated list of Microsoft-signed drivers it refuses to load. Understanding that list is understanding why every previous attempt to make kernel-mode trust mean &lt;em&gt;safety&lt;/em&gt; instead of just &lt;em&gt;identity&lt;/em&gt; eventually broke.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The current Windows 11 22H2 client honours &lt;code&gt;%windir%\system32\CodeIntegrity\DriverSiPolicy.p7b&lt;/code&gt;, a Microsoft-signed deny list enforced by a hypervisor-isolated code-integrity engine sitting in Virtual Trust Level 1. The same engine refuses to map any kernel page that is simultaneously writable and executable. Both behaviours are documented on Microsoft Learn&apos;s Memory Integrity page [@ms-hvci-vbs] and the Microsoft-recommended driver block rules page [@ms-driver-block-rules] [@ms-hvci-vbs] [@ms-driver-block-rules]. Neither existed in 2006.&lt;/p&gt;
&lt;p&gt;To understand why Windows now refuses to load drivers it once asked Microsoft to sign, we need to go back thirty years to the moment Windows first asked a publisher to sign anything at all.&lt;/p&gt;
&lt;h2&gt;2. Advisory Trust: 1996 to 2005&lt;/h2&gt;
&lt;p&gt;For its first decade, the Windows driver signing policy was a polite recommendation.&lt;/p&gt;
&lt;p&gt;Microsoft shipped its first user-mode code-signing primitive, &lt;a href=&quot;https://paragmali.com/blog/authenticode-and-catalog-files-the-crypto-foundation-under-w/&quot; rel=&quot;noopener&quot;&gt;Authenticode&lt;/a&gt;, in 1996, packaged for developers in the same tool kit that gave us &lt;code&gt;SignTool&lt;/code&gt;, &lt;code&gt;MakeCat&lt;/code&gt;, and &lt;code&gt;Inf2Cat&lt;/code&gt; -- the suite Microsoft Learn still documents under &quot;Cryptography tools&quot; [@ms-crypto-tools] [@ms-crypto-tools]. Authenticode wrapped a PKCS#7 signature around the SHA-1 (and later SHA-256) hash of a PE image and let a recipient walk the signer&apos;s certificate chain to a trusted root. It was the first answer to the question &lt;em&gt;who shipped this binary?&lt;/em&gt; It was, deliberately, never an answer to &lt;em&gt;is this binary safe?&lt;/em&gt;&lt;/p&gt;

Microsoft&apos;s PKCS#7-based code-signing format for Windows binaries. Authenticode attests to the publisher&apos;s identity by binding the binary&apos;s hash to a certificate chain anchored at a trusted root. It does not analyse the program&apos;s behaviour.
&lt;p&gt;For drivers, the user-mode signing primitive was paired with a separate quality program. The Windows Hardware Quality Labs programme, documented today via the Hardware Lab Kit [@ms-hlk], tested third-party drivers against a Microsoft-curated compatibility suite and rewarded passing drivers with a counter-signature, eventually surfaced as the &quot;Designed for Windows&quot; or &quot;Certified for Windows&quot; mark [@ms-hlk]. The badge was operationally meaningful for OEM badging and Windows Update distribution. It was not a load-time gate. An unsigned &lt;code&gt;.sys&lt;/code&gt; file dropped on disk by a setup script still loaded.&lt;/p&gt;

Microsoft&apos;s compatibility-test programme for third-party drivers. A driver that passes the HLK test suite receives a Microsoft counter-signature and is eligible for OEM and Windows Update distribution. The programme produces a quality signal, not a load-time enforcement decision.
&lt;h3&gt;The SetupAPI prompt&lt;/h3&gt;
&lt;p&gt;On 32-bit Windows, the gate the user actually saw was the SetupAPI driver-installation prompt. The administrator could set the system to &lt;em&gt;Ignore&lt;/em&gt;, &lt;em&gt;Warn&lt;/em&gt;, or &lt;em&gt;Block&lt;/em&gt; unsigned drivers; the default was &lt;em&gt;Warn&lt;/em&gt;. &lt;em&gt;Warn&lt;/em&gt; meant a click-through dialog at install time. An administrator who clicked &lt;em&gt;Install this driver anyway&lt;/em&gt; loaded the unsigned driver, no further questions asked. The structural truth is the one Microsoft&apos;s modern KMCS policy page [@ms-kmcs-policy] acknowledges by contrast: under advisory policy, the prompt is the policy, and a prompt is exactly as strong as the user clicking past it [@ms-kmcs-policy].&lt;/p&gt;
&lt;p&gt;The Sony BMG XCP incident in October 2005 made the structural weakness concrete. The XCP copy-protection software, shipped on retail audio CDs, autorun-installed an unsigned kernel-mode filter driver. The driver hid any file, registry key, or process whose name began with the string &lt;code&gt;$sys$&lt;/code&gt; -- a textbook rootkit by capability if not by intent. The driver loaded after an administrator clicked through the warning prompt, exactly as advisory policy allowed. The pattern is described well in Wikipedia&apos;s code-signing article [@wp-code-signing] [@wp-code-signing].The Sony BMG XCP rootkit triggered class-action lawsuits, FTC settlements, and an industry-wide reconsideration of what &quot;the user clicked OK&quot; actually authorises. From a kernel-trust perspective, the lesson is narrower: any policy that ends in a dismissible dialog has the same threat model as no policy at all, against an attacker who can show the user a dialog.&lt;/p&gt;
&lt;p&gt;The structural takeaway from 1996 through 2005 is the one the next decade tried to repair. When the signing policy is advisory, an attacker who has -- or can socially engineer -- administrator privilege only needs to dismiss a prompt to load a kernel driver. The signing primitive worked. The policy around the primitive did not.&lt;/p&gt;
&lt;p&gt;If the prompt is the only thing between an attacker and ring zero, the kernel itself has to take over. And on a brand-new x64 architecture, Microsoft could break backward compatibility to make that happen.&lt;/p&gt;
&lt;h2&gt;3. KMCS: The Vista x64 Revolution (2006-2016)&lt;/h2&gt;
&lt;p&gt;In November 2006, Vista x64 made a decision that x86 never could: it refused to load any unsigned kernel driver, full stop.&lt;/p&gt;
&lt;p&gt;The mechanism was Kernel-Mode Code Signing, or KMCS. The previous-versions Microsoft Learn page on Vista-era driver signing [@learn-microsoft-com-design-dn653567vvs85]) records the policy [@ms-dn653567]. At the point where the I/O manager called &lt;code&gt;IoLoadDriver&lt;/code&gt;, the Code Integrity module (&lt;code&gt;ci.dll&lt;/code&gt;) intercepted the load, extracted the Authenticode signature embedded in the PE image or attached via a published catalogue, walked the certificate chain, and refused to map the image if the chain did not terminate at a Microsoft-trusted root. There was no SetupAPI prompt to dismiss. If the kernel refused, the kernel refused. The decision lived below the user&apos;s reach.&lt;/p&gt;

The Vista-era mandatory load-time signature policy on 64-bit Windows. Before mapping a kernel driver&apos;s PE image, the Code Integrity module verifies that the image&apos;s Authenticode signature chains to a Microsoft-trusted root. Drivers that fail the check are refused at load time, not at install time.
&lt;p&gt;x86 kept the advisory policy. Microsoft could not break compatibility with two decades of unsigned drivers on the dominant platform. But x64 was a young architecture with a few hundred drivers in the field, and Microsoft used that moment to flip the default. The structural shift was real: kernel-driver trust on x64 became a property of the binary, decided in the kernel, against a fixed set of trusted roots.&lt;/p&gt;
&lt;h3&gt;Cross-certificates: opening the gate to the world&lt;/h3&gt;
&lt;p&gt;A Microsoft-trusted root alone would have meant Microsoft signs every driver, which Microsoft did not want. Instead Microsoft cross-certified a small set of commercial code-signing certificate authorities -- including VeriSign, DigiCert, Entrust, GlobalSign, GoDaddy, and several smaller successors enumerated on the historical cross-certificate list (2020 archive) [@ms-cross-cert-archive] -- so that a publisher could buy a code-signing certificate from a commercial CA, sign their driver, and have the chain still terminate at a Microsoft-recognised root [@ms-cross-cert-archive]. The architecture is documented on the cross-certificates for kernel-mode code signing page [@ms-cross-cert], which now opens with a sentence that did not exist in 2006: &quot;Cross-signing is no longer accepted for driver signing&quot; [@ms-cross-cert]. We will come back to that.&lt;/p&gt;

sequenceDiagram
    participant IO as I/O Manager
    participant CI as Code Integrity (ci.dll)
    participant CA as Cross-certified CA chain
    participant Root as Microsoft trusted root&lt;pre&gt;&lt;code&gt;IO-&amp;gt;&amp;gt;CI: Map PE for kernel driver
CI-&amp;gt;&amp;gt;CI: Extract Authenticode signature (PKCS#7)
CI-&amp;gt;&amp;gt;CA: Walk certificate chain
CA-&amp;gt;&amp;gt;Root: Anchor at Microsoft cross-cert
alt Chain valid and not revoked
    CI-&amp;gt;&amp;gt;IO: Allow section creation
    IO-&amp;gt;&amp;gt;IO: Load driver into kernel address space
else Chain invalid or unsigned
    CI-&amp;gt;&amp;gt;IO: STATUS_INVALID_IMAGE_HASH
    IO-&amp;gt;&amp;gt;IO: Abort load
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Documented escape hatches&lt;/h3&gt;
&lt;p&gt;KMCS shipped with three documented bypasses for developers and special cases, all enumerated on the KMCS policy page [@ms-kmcs-policy] [@ms-kmcs-policy]:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;bcdedit /set TESTSIGNING ON&lt;/code&gt; enables test-signing mode. The kernel will load drivers signed with self-issued test certificates. The cost is a desktop watermark.&lt;/li&gt;
&lt;li&gt;The F8 advanced-boot option &lt;em&gt;Disable Driver Signature Enforcement&lt;/em&gt; turns off KMCS for one boot.&lt;/li&gt;
&lt;li&gt;The legacy &lt;code&gt;nointegritychecks&lt;/code&gt; BCD flag disables enforcement entirely, but is rejected on systems where &lt;a href=&quot;https://paragmali.com/blog/secure-boot-in-windows-the-chain-from-sector-zero-to-userini/&quot; rel=&quot;noopener&quot;&gt;Secure Boot&lt;/a&gt; is on.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of these was a development workflow concession. Each of them, with admin privileges and a willingness to reboot, also serves as a kernel-driver loading path for an attacker who has already escalated. The policy holds against unprivileged adversaries. Against an attacker who already runs as administrator, the policy was already, by 2010, defending against a different threat than the one people thought it was defending against.Microsoft has been formally clear about this since at least 2016: the administrator-to-kernel transition is not a security boundary in the MSRC servicing-criteria sense. Elastic Security Labs writes the position out explicitly in their analysis of vulnerable-driver mitigations [@elastic-admin] [@elastic-admin]. The historical irony is that Vista x64 KMCS was widely read at the time as a defence against admin-level adversaries; it was actually a defence against unprivileged or pre-admin ones.&lt;/p&gt;
&lt;h3&gt;PatchGuard: the parallel runtime defence&lt;/h3&gt;
&lt;p&gt;KMCS was a load-time check. The runtime parallel arrived in 2005 with Kernel Patch Protection, informally PatchGuard or KPP, which the Wikipedia entry on Kernel Patch Protection [@wp-kpp] describes as a feature of 64-bit Windows that prevents patching of critical kernel structures [@wp-kpp]. KPP polls a set of integrity-critical kernel objects -- the System Service Descriptor Table, IDT, GDT, certain function prologues -- and triggers a bug check if it detects tampering. It is the watchdog against runtime modification of the kernel by code that has already loaded; KMCS gates what loads in the first place.&lt;/p&gt;
&lt;p&gt;What this fixed: the unsigned-driver-loading path closed on 64-bit Windows in production mode. Kernel rootkits of the early 2000s -- FU, Mailbot, Rustock, and their contemporaries, widely documented in the security-research literature of the era -- could no longer ship as bare &lt;code&gt;.sys&lt;/code&gt; files an admin script dropped on disk. The structural class of &quot;unsigned kernel rootkit&quot; effectively died on x64.&lt;/p&gt;
&lt;p&gt;But the day Vista x64 shipped, two new attack surfaces opened up. The first one Stuxnet found four years later. The second one nobody had a name for yet.&lt;/p&gt;
&lt;h2&gt;4. Stuxnet, BYOVD, and the Two Things Vista Did Not Fix&lt;/h2&gt;
&lt;p&gt;On 17 June 2010, researchers in Belarus and Iran identified Stuxnet, a worm targeting supervisory control and data acquisition systems [@wp-stuxnet] used in industrial-control environments [@wp-stuxnet]. Two of its drivers carried perfectly valid Authenticode signatures.&lt;/p&gt;
&lt;p&gt;The signatures were genuine. The certificates were not. Stuxnet had been signed with private keys stolen from semiconductor vendors whose code-signing certs chained to legitimate cross-certified roots. KMCS verified the chain, found it good, and let the drivers load.Stuxnet is widely reported to have used stolen signing keys from two real semiconductor vendors. The malware-analysis literature is consistent on the pattern; specific cert-holder attributions are reproduced in many places but the primary advisory record we cite here is the Wikipedia Stuxnet article [@wp-stuxnet] and the general framing in the Wikipedia code-signing article [@wp-code-signing] [@wp-stuxnet] [@wp-code-signing]. The reactive answer was certificate revocation, but revocation propagates through Windows on a schedule, not instantly, and the cached chain on millions of machines remained valid for days.&lt;/p&gt;
&lt;p&gt;That was the first failure mode KMCS could not block by design. The signature primitive answers &lt;em&gt;was this signed by a key that chains to a trusted root?&lt;/em&gt; It cannot answer &lt;em&gt;was the key still in the publisher&apos;s control when it signed this?&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;The Capcom.sys reframe&lt;/h3&gt;
&lt;p&gt;The second failure mode arrived publicly in 2016. A Capcom driver shipped via a Street Fighter V update exposed an IOCTL, numbered &lt;code&gt;0xAA013044&lt;/code&gt;, that took a user-supplied function pointer and executed it in kernel mode -- with Supervisor Mode Execution Prevention (SMEP) disabled while it did so. The driver was signed and chained correctly. Satoshi Tanda&apos;s standalone proof of concept at &lt;code&gt;tandasat/ExploitCapcom&lt;/code&gt; [@gh-tandasat-capcom] remains the canonical reference, including the SHA-1 of the binary (&lt;code&gt;c1d5cf8c43e7679b782630e93f5e6420ca1749a7&lt;/code&gt;) [@gh-tandasat-capcom].&lt;/p&gt;
&lt;p&gt;There was nothing for KMCS to catch. The driver did exactly what the signature said it did: ship bytes from a publisher Microsoft could identify. The signature has no opinion about the IOCTL surface.&lt;/p&gt;

A signed driver means only that someone Microsoft can identify shipped this binary. It does not mean the driver lacks a function-pointer IOCTL.
&lt;p&gt;That observation is the first of three reframes in this article and the easiest to underestimate. Up to 2010 the conventional security reading of a Microsoft-rooted Authenticode signature was that the driver had passed a review. After Stuxnet, the reading narrowed to &lt;em&gt;the publisher is identifiable&lt;/em&gt;. After Capcom.sys, it narrowed again to &lt;em&gt;the binary&apos;s identity is verifiable&lt;/em&gt;. None of these readings includes &lt;em&gt;the binary does not have a kernel-write primitive in its IOCTL handler&lt;/em&gt;.&lt;/p&gt;

An attack pattern in which an adversary, having obtained or already holding administrator privileges, installs a signed but design-vulnerable third-party kernel driver and uses its exposed primitives -- arbitrary memory read/write, port I/O, MSR access, or function-pointer dispatch -- to gain ring-zero capability. The signature primitive does not refuse the load because the driver is, on signature alone, legitimate.
&lt;h3&gt;The catalogue grows&lt;/h3&gt;
&lt;p&gt;The BYOVD catalogue accumulated through the 2010s.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;RTCore64.sys&lt;/code&gt;, the kernel component of MSI&apos;s Afterburner overclocking utility, exposed read/write access to arbitrary kernel memory, I/O ports, and Model-Specific Registers from user mode. The NVD entry for CVE-2019-16098 [@nvd-cve-2019-16098] is unusually direct: &quot;These signed drivers can also be used to bypass the Microsoft driver-signing policy to deploy malicious code.&quot; [@nvd-cve-2019-16098] The driver became a workhorse for ransomware crews. Sophos&apos;s October 2022 incident analysis of BlackByte&apos;s new variant [@sophos-blackbyte] documents the abuse: BlackByte &quot;abus[ed] a known vulnerability in the legitimate vulnerable driver RTCore64.sys&quot; to disable &quot;a whopping list of over 1,000 drivers on which security products rely to provide protection&quot; [@sophos-blackbyte].&lt;/p&gt;
&lt;p&gt;&lt;code&gt;gdrv.sys&lt;/code&gt;, the Gigabyte APP Center driver, exposed a ring-zero memcpy-equivalent that a local attacker could use to overwrite arbitrary kernel addresses. CVE-2018-19320 [@nvd-cve-2018-19320] is on CISA&apos;s Known Exploited Vulnerabilities catalogue [@nvd-cve-2018-19320]. The RobinHood ransomware abused it during the 2019 Baltimore municipal-government attack -- a connection widely documented by Sophos and CrowdStrike incident-response teams, though absent from the bare NVD record.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;KProcessHacker&lt;/code&gt;, the kernel companion to the Process Hacker administration tool, exposed a process-termination primitive that bypassed even the Protected Process Light (PPL) shielding around antivirus and EDR processes. CrowdStrike&apos;s DoppelPaymer write-up [@cs-doppelpaymer] documents the abuse explicitly: &quot;the hijacking technique ... leverages ProcessHacker&apos;s kernel driver, KProcessHacker, that has been registered under the service name KProcessHacker3 ... terminate processes, including those protected by Protected Process Light (PPL).&quot; [@cs-doppelpaymer]&lt;/p&gt;

sequenceDiagram
    participant Adv as Adversary (admin user mode)
    participant SCM as Service Control Manager
    participant CI as Code Integrity (ci.dll)
    participant Drv as Signed vulnerable driver
    participant K as Kernel state&lt;pre&gt;&lt;code&gt;Adv-&amp;gt;&amp;gt;SCM: Install signed driver as kernel service
SCM-&amp;gt;&amp;gt;CI: Request load
CI-&amp;gt;&amp;gt;CI: Authenticode check passes
CI-&amp;gt;&amp;gt;SCM: Allow
SCM-&amp;gt;&amp;gt;Drv: Load into kernel
Adv-&amp;gt;&amp;gt;Drv: IOCTL with attacker-supplied pointers
Drv-&amp;gt;&amp;gt;K: Write attacker bytes at arbitrary kernel address
K-&amp;gt;&amp;gt;K: Clear EDR notify routine / escalate token
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;The third bypass: patching the policy from kernel mode&lt;/h3&gt;
&lt;p&gt;There is a third failure mode that closes the loop. Once an attacker has a signed driver with an arbitrary kernel-write primitive, they can write directly into the in-kernel Code Integrity state. The variable of interest is &lt;code&gt;g_CiOptions&lt;/code&gt;, an integer inside &lt;code&gt;ci.dll&lt;/code&gt; whose bits gate Driver Signature Enforcement. TrustedSec describes the technique cleanly: &quot;this configuration variable has a number of flags that can be set, but typically for bypassing DSE this value is set to 0, completely disabled DSE and allows the attacker to load unsigned drivers just fine.&quot; [@trustedsec-gcioptions] Set &lt;code&gt;g_CiOptions&lt;/code&gt; to zero and the subsequent driver loads do not need signatures at all. The signed driver, in effect, is a one-shot key that opens the gate for any unsigned driver behind it. The pattern recurs through the early 2020s; specific malware-family attributions remain research-folklore, but the technique class is well attested in TrustedSec&apos;s account.&lt;/p&gt;
&lt;p&gt;The structural takeaway: KMCS verifies &lt;em&gt;who signed&lt;/em&gt;, never &lt;em&gt;what was signed&lt;/em&gt;. Once an attacker has a signed driver with a write primitive, they have ring zero. Stricter signing closes the front door for new malicious drivers. Every commercial-CA cert that was ever issued is still loadable. The policy decision has to move out of the attacker&apos;s reach. And the kernel itself has to stop being the thing that decides.&lt;/p&gt;
&lt;h2&gt;5. Microsoft as the Only Signer (2016-2024)&lt;/h2&gt;
&lt;p&gt;In August 2016, Microsoft did something the WHQL programme had refused to do for twenty years: it became the only entity that could counter-sign a new Windows kernel driver.&lt;/p&gt;
&lt;p&gt;The transition shipped with Windows 10 version 1607. The KMCS policy page [@ms-kmcs-policy] records the cut precisely: for end-entity certificates issued after 29 July 2015, the chain had to terminate at one of three Microsoft-owned roots -- &lt;em&gt;Microsoft Root Authority 2010&lt;/em&gt;, &lt;em&gt;Microsoft Root Certificate Authority&lt;/em&gt;, or &lt;em&gt;Microsoft Root Authority&lt;/em&gt; -- and the binary had to be counter-signed via the Windows Hardware Dev Center submission portal [@ms-kmcs-policy]. The commercial CAs were out. Microsoft was in, as the single point through which any new third-party kernel driver had to pass.&lt;/p&gt;
&lt;h3&gt;Two pipelines&lt;/h3&gt;
&lt;p&gt;Behind the portal sat two submission paths. The HLK/WHQL path required a full Hardware Lab Kit compatibility test pass on the publisher&apos;s hardware -- the lab kit is the modern incarnation of the WHQL programme, documented on Microsoft Learn [@ms-hlk] [@ms-hlk]. A passing run produced a &quot;Certified for Windows&quot; mark and made the driver eligible for OEM badging and Windows Update distribution. The lighter-friction path, called attestation signing [@ms-attestation], did not require an HLK run [@ms-attestation]. The publisher submitted a CAB containing the driver and supporting metadata. Microsoft&apos;s backend ran a malware scan and an automated policy check; if both passed, Microsoft applied a counter-signature. Attestation-signed drivers, the page notes, ship only to client SKUs.&lt;/p&gt;

The lower-friction post-2016 Microsoft signing path for Windows kernel drivers. The publisher uploads a CAB to the Hardware Dev Center; Microsoft runs malware scanning and an automated policy check; on pass, Microsoft applies its counter-signature. The path replaces full HLK testing for client-only drivers.
&lt;h3&gt;EV certificates as the account-binding primitive&lt;/h3&gt;
&lt;p&gt;Both paths required the publisher to hold an Extended Validation code-signing certificate. The EV cert does not sign the driver image itself; it signs and binds the Hardware Dev Center submission. That gives Microsoft a real-name handle on every kernel-driver publisher. EV certificates ride a strong identity check, cost meaningfully more than commercial OV certs, and live on a hardware token in the publisher&apos;s possession. The 2021 Microsoft Security blog announcing the Vulnerable &amp;amp; Malicious Driver Reporting Center spells the requirement out: &quot;Kernel-mode driver publishers must pass the Hardware Lab Kit (HLK) compatibility tests, malware scanning, and prove their identity through extended validation (EV) certificates.&quot; [@ms-vdrc-blog]&lt;/p&gt;

flowchart LR
    A[Publisher EV cert + driver CAB] --&amp;gt; B[Hardware Dev Center upload]
    B --&amp;gt; C[Malware scan]
    C --&amp;gt; D{HLK required?}
    D -- &quot;Yes&quot; --&amp;gt; E[HLK compatibility test pass]
    D -- &quot;No&quot; --&amp;gt; F[Attestation policy check]
    E --&amp;gt; G[Microsoft counter-sign]
    F --&amp;gt; G
    G --&amp;gt; H[Optional Windows Update distribution]
&lt;h3&gt;The legacy long tail&lt;/h3&gt;
&lt;p&gt;The pivot to Microsoft-only signing closed the door for new drivers. It did not close the door for old ones.&lt;/p&gt;

The KMCS policy page [@ms-kmcs-policy] is candid about the carve-outs: &quot;Cross-signed drivers are still permitted if any of the following are true: The PC was upgraded from an earlier release of Windows to Windows 10, version 1607. Secure Boot is off in the BIOS. Drivers was signed with an end-entity certificate issued prior to July 29th 2015 that chains to a supported cross-signed CA.&quot; [@ms-kmcs-policy]&lt;p&gt;Operationally, every signed-but-vulnerable driver from the 2006-2015 era remains loadable on a meaningful population of Windows machines: upgraded installs, devices with Secure Boot disabled in firmware, and drivers with pre-cutoff end-entity certs whose chains are still valid. &lt;code&gt;Capcom.sys&lt;/code&gt;, &lt;code&gt;RTCore64.sys&lt;/code&gt;, &lt;code&gt;gdrv.sys&lt;/code&gt;, &lt;code&gt;KProcessHacker&lt;/code&gt; -- the entire 2010s BYOVD catalogue -- continues to chain to roots Windows still accepts.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;What attestation signing catches and what it does not&lt;/h3&gt;
&lt;p&gt;The malware scan inside attestation signing looks for known dangerous behaviour. The Microsoft Security blog post on the Vulnerable &amp;amp; Malicious Driver Reporting Center enumerates the categories the backend flags: &quot;Drivers with the ability to read or write arbitrary kernel, physical, or device memory, including Port I/O and central processing unit (CPU) registers from user mode.&quot; [@ms-vdrc-blog] In other words, the scanner already understands the BYOVD pattern.&lt;/p&gt;
&lt;p&gt;What it does not catch are &lt;em&gt;novel&lt;/em&gt; design flaws. A driver whose IOCTL surface is structurally unsafe in a way the scanner does not have a signature for passes the scan and ships with a Microsoft counter-signature. The Capcom.sys pattern is in the scanner&apos;s repertoire today; the pattern in the next driver to ship is, by definition, not.&lt;/p&gt;
&lt;p&gt;A second weakness sits on the publisher side. EV-key compromise -- whether through the LAPSUS$ supply-chain leaks of 2022 or other vendor incidents -- gives the attacker the Microsoft-only-signing flavour of the Stuxnet problem. The signed-by-Microsoft chain is exactly as strong as the EV key&apos;s safekeeping at the publisher.&lt;/p&gt;
&lt;p&gt;One bottleneck for signing is an improvement. But the bottleneck still trusts the kernel that asks the question. As long as the policy engine runs in the same memory the attacker can write, the policy engine loses.&lt;/p&gt;
&lt;h2&gt;6. HVCI: Moving the Policy Out of Reach (2015-present)&lt;/h2&gt;
&lt;p&gt;In July 2015, Microsoft shipped a feature so structurally important that it took six years to become a consumer default, and so misunderstood that it still travels under three different names.&lt;/p&gt;
&lt;p&gt;The names are the easiest place to start. &lt;em&gt;Virtualization-Based Security&lt;/em&gt; (VBS) is the platform: a Hyper-V-rooted virtualisation layer that exists on every modern Windows installation that meets the hardware requirements. &lt;em&gt;Hypervisor-protected Code Integrity&lt;/em&gt; (HVCI) is the kernel-code-integrity consumer of VBS. &lt;em&gt;Memory Integrity&lt;/em&gt; is the label the Windows Security UI uses today. The Microsoft Learn page on Memory Integrity [@ms-hvci-vbs] is the canonical primary source [@ms-hvci-vbs]. TrustedSec called out the conflation explicitly in their &lt;code&gt;g_CiOptions in a virtualized world&lt;/code&gt; post [@trustedsec-gcioptions] [@trustedsec-gcioptions].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; A security check that shares a trust domain with what it is checking has, by definition, already lost. HVCI moves the check out of the attacker&apos;s trust domain. It is the answer to &lt;em&gt;who decides&lt;/em&gt;. It is not the answer to &lt;em&gt;what gets decided&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That sentence is the second of this article&apos;s three reframes, and the one that makes everything that follows make sense.&lt;/p&gt;
&lt;h3&gt;VBS and the Virtual Trust Levels&lt;/h3&gt;
&lt;p&gt;On a VBS-on Windows machine, &lt;a href=&quot;https://paragmali.com/blog/above-ring-zero-how-the-windows-hypervisor-became-a-security/&quot; rel=&quot;noopener&quot;&gt;Hyper-V&lt;/a&gt; is the Type-1 hypervisor. The bootloader brings the hypervisor up first, the hypervisor brings up two execution environments side by side, and the normal Windows kernel runs in one of them while a much smaller &lt;a href=&quot;https://paragmali.com/blog/the-windows-secure-kernel/&quot; rel=&quot;noopener&quot;&gt;Secure Kernel&lt;/a&gt; runs in the other.&lt;/p&gt;

The VBS abstraction that partitions a Windows installation into two execution environments. VTL0 is the normal Windows kernel and its drivers. VTL1 is a much smaller Secure Kernel and a curated set of &quot;trustlets&quot; -- isolated user-mode processes that hold the most sensitive secrets. VTL1 can read and write VTL0 memory; VTL0 cannot read or write VTL1 memory. Code-integrity policy lives in VTL1.
&lt;p&gt;The Code Integrity engine on an HVCI-on machine -- signature verification and policy-file consultation -- runs inside VTL1&apos;s Secure Kernel as the &lt;em&gt;Secure Kernel Code Integrity&lt;/em&gt; component, SKCI. The VTL0 kernel cannot read or write VTL1 memory by hardware construction: the hypervisor&apos;s second-level address translation tables, programmed before VTL0 ever runs, mark VTL1 pages as unreachable from VTL0. The in-memory &lt;code&gt;g_CiOptions&lt;/code&gt; state continues to reside in &lt;code&gt;ci.dll&lt;/code&gt;&apos;s VTL0 data section -- it does not relocate into VTL1 -- but on an HVCI-on machine Kernel Data Protection (KDP), exposed to VTL0 drivers as &lt;code&gt;MmProtectDriverSection&lt;/code&gt;, asks the Secure Kernel to mark the containing page read-only at the SLAT level. A fully compromised VTL0 kernel -- with kernel debugging attached, with all of ring zero&apos;s privileges -- cannot rewrite &lt;code&gt;g_CiOptions&lt;/code&gt; to zero, because the SLAT mapping refuses the write.&lt;/p&gt;

flowchart TD
    subgraph VTL1 [VTL1 -- Secure Kernel]
        SK[Secure Kernel]
        SKCI[SKCI -- Code Integrity]
        Policy[&quot;Code Integrity policy&lt;br /&gt;(DriverSiPolicy.p7b)&quot;]
        SK --&amp;gt; SKCI
        SKCI --&amp;gt; Policy
    end
    subgraph VTL0 [VTL0 -- Normal Windows]
        Kern[NT Kernel]
        Drv[Driver attempting load]
        CI[ci.dll user-side]
        Kern --&amp;gt; CI
        CI --&amp;gt; Drv
    end
    Hypervisor{&quot;Hyper-V SLAT&quot;}
    Kern --&amp;gt;|&quot;Section create&quot;| Hypervisor
    Hypervisor --&amp;gt;|&quot;Forward&quot;| SKCI
    SKCI --&amp;gt;|&quot;Allow or deny&quot;| Hypervisor
    Hypervisor --&amp;gt;|&quot;Result&quot;| Kern
&lt;h3&gt;W^X on kernel memory&lt;/h3&gt;
&lt;p&gt;There is a second, equally structural property HVCI enforces. When the VTL0 kernel tries to map an executable section -- to create a kernel-executable page from a PE image -- the hypervisor forces the request through SKCI. SKCI verifies the Authenticode signature &lt;em&gt;at section creation time&lt;/em&gt;, not only at the &lt;code&gt;IoLoadDriver&lt;/code&gt; entry point a load goes through later [@ms-hvci-vbs]. And SKCI refuses any page that is simultaneously writable and executable. The classic exploitation technique of allocating a writable kernel buffer, writing shellcode into it, and then jumping to it stops working: the page either is writable, in which case it is not executable, or is executable, in which case it is not writable.&lt;/p&gt;
&lt;p&gt;The hardware acceleration matters. The Memory Integrity page [@ms-hvci-vbs] is unusually direct about the requirement: &quot;Memory integrity works better with Intel Kabylake and higher processors with Mode-Based Execution Control, and AMD Zen 2 and higher processors with Guest Mode Execute Trap capabilities. Older processors rely on an emulation of these features, called Restricted User Mode, and will have a bigger impact on performance.&quot; [@ms-hvci-vbs]Mode-Based Execute Control (MBEC) is the Intel feature that lets the hypervisor distinguish &quot;executable in supervisor mode&quot; from &quot;executable in user mode&quot; at the page-table-entry level. AMD&apos;s Guest Mode Execute Trap (GMET) is the structurally equivalent feature. Older silicon falls back to Restricted User Mode emulation, which works correctly but pays a meaningfully larger performance tax. The hardware cutoff is a major reason HVCI defaulted off on pre-2017 OEM hardware for years.&lt;/p&gt;
&lt;h3&gt;What HVCI fixed&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;g_CiOptions&lt;/code&gt; patching family, the third bypass we met in section 4, closes on HVCI-on systems. TrustedSec&apos;s post [@trustedsec-gcioptions] gives a clean account: &lt;code&gt;g_CiOptions&lt;/code&gt; still lives in &lt;code&gt;ci.dll&lt;/code&gt;&apos;s VTL0 data section, but Kernel Data Protection -- exposed to VTL0 drivers as &lt;code&gt;MmProtectDriverSection&lt;/code&gt; -- asks the Secure Kernel in VTL1 to mark its containing page read-only at the SLAT level, so a VTL0 ring-zero write to it faults; the VTL0 kernel cannot rewrite the variable; live-kernel debuggers attached to VTL0 cannot rewrite it either [@trustedsec-gcioptions]. The arbitrary-write-to-disable-DSE pattern that worked on Windows 7 through pre-HVCI Windows 10 is, on an HVCI-on Windows 11, no longer a primitive that exists in the attacker&apos;s threat model. The trust domain that decides the policy is not the trust domain the attacker can reach.&lt;/p&gt;
&lt;h3&gt;What HVCI did not fix&lt;/h3&gt;
&lt;p&gt;It is essential to be clear about what HVCI does not catch, because misreading this is how the BYOVD class survives.&lt;/p&gt;
&lt;p&gt;HVCI verifies the &lt;em&gt;signature&lt;/em&gt; and enforces W^X. It does not analyse the driver&apos;s &lt;em&gt;behaviour&lt;/em&gt;. The 2019 &lt;code&gt;RTCore64.sys&lt;/code&gt; driver passes SKCI section-mapping unchanged: it is signed by MSI through a Microsoft-recognised chain, it has no writable-and-executable pages, and the Authenticode hash on disk matches the binary in memory. After it loads, an attacker in user mode sends an IOCTL; the driver, executing legitimately in ring zero, writes attacker-controlled bytes to an attacker-chosen kernel address; the EDR notify routine table is patched; the BYOVD attack proceeds. Everything that happens inside the IOCTL handler happens with kernel privilege, on properly-signed code paths, inside HVCI&apos;s W^X policy. The structural BYOVD class is unaffected.&lt;/p&gt;
&lt;p&gt;That is the gap the next two sections close.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The Memory Integrity page [@ms-hvci-vbs] is explicit that &quot;some applications and hardware device drivers may be incompatible with memory integrity. This incompatibility can cause devices or software to malfunction and in rare cases may result in a boot failure (blue screen).&quot; [@ms-hvci-vbs] For years OEM and gaming-system vendors shipped with HVCI off because legacy ISV drivers, anti-cheat kernel components, or older virtualisation tools could not coexist with it. On an HVCI-off system the &lt;code&gt;g_CiOptions&lt;/code&gt; patching family is back in play, the kernel-CI engine and the kernel it polices are in the same trust domain, and the analysis of section 4 applies unchanged. The 2026 default-on baseline is real, but it is not yet universal.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;HVCI is the answer to &lt;em&gt;who decides&lt;/em&gt;. It is not the answer to &lt;em&gt;what gets decided&lt;/em&gt;. We still need a way to say: this specific signed binary is one we do not trust.&lt;/p&gt;
&lt;h2&gt;7. The Block List: Naming the Weakness (2020-present)&lt;/h2&gt;
&lt;p&gt;In October 2020, Microsoft started shipping something it had spent twenty-five years avoiding: a list of specific drivers it would refuse to load by name.&lt;/p&gt;
&lt;p&gt;The artefact lives at &lt;code&gt;%windir%\system32\CodeIntegrity\DriverSiPolicy.p7b&lt;/code&gt;. The file is a PKCS#7-signed &lt;a href=&quot;https://paragmali.com/blog/wdac--hvci-code-integrity-at-every-layer-in-windows/&quot; rel=&quot;noopener&quot;&gt;App Control for Business&lt;/a&gt; policy -- &quot;WDAC&quot; by its former name -- whose body consists of deny rules expressed at the granularity of file hash, file name, or publisher. The canonical Microsoft-recommended driver block rules page [@ms-driver-block-rules] is the primary source, and is unusually rich for a Microsoft Learn page [@ms-driver-block-rules].&lt;/p&gt;

Microsoft&apos;s policy-driven application-control engine. An App Control policy is a signed XML or binary file that lists allow rules, deny rules, and signer-level rules; at load time, the policy engine consults the rules and either allows or refuses the image. `DriverSiPolicy.p7b` is itself an App Control policy whose body is all deny rules.
&lt;h3&gt;Cadence and the published-vs-shipped gap&lt;/h3&gt;
&lt;p&gt;The block list is refreshed on two cadences. Microsoft publishes the source XML on the block-rules page [@ms-driver-block-rules] on a quarterly schedule and pushes the binary &lt;code&gt;DriverSiPolicy.p7b&lt;/code&gt; to client devices through monthly Windows servicing [@ms-driver-block-rules]. Microsoft&apos;s Security Baselines team also publishes a running update post [@ms-tc-blocklist-baselines] cataloguing the changes [@ms-tc-blocklist-baselines].&lt;/p&gt;
&lt;p&gt;The candid admission on the block-rules page [@ms-driver-block-rules] is the part of the story that is most worth understanding.&lt;/p&gt;

The blocklist included in this article and in the associated downloadable files usually contains a more complete set of known vulnerable drivers than the version in the OS and delivered by Windows Update. It&apos;s often necessary for us to hold back some blocks to avoid breaking existing functionality. -- Microsoft Learn, *Microsoft-recommended driver block rules* [@ms-driver-block-rules]
&lt;p&gt;The published list is, on purpose, more inclusive than the shipped list. The reason is operational: every entry in the shipped list is a driver that would refuse to load on millions of devices, some of which have legitimate dependencies. Microsoft holds entries back when the compatibility cost is too high, even when the security signal is strong. We will come back to whether that gap is closeable in section 9.&lt;/p&gt;
&lt;h3&gt;The 22H2 cut and the Server 2016 carve-out&lt;/h3&gt;
&lt;p&gt;Two dates anchor the deployment story.&lt;/p&gt;
&lt;p&gt;The block list was an &lt;em&gt;optional&lt;/em&gt; feature in Windows 10 1809, enabled by default only on systems that ran Hypervisor-protected Code Integrity, Smart App Control, or Windows in S-mode [@ms-kb5020779] [@ms-kb5020779]. With the Windows 11 2022 Update, also known as 22H2 [@ms-blogs-win11-2022], released on 20 September 2022, default-on coverage extended to every client device, not just the HVCI-on subset [@ms-blogs-win11-2022]. The 22H2 release is the moment the block list became universal Windows client behaviour, six years after the first BYOVD primitive that motivated it.&lt;/p&gt;
&lt;p&gt;The block-rules page [@ms-driver-block-rules] notes a single explicit carve-out worth flagging.&quot;Except on Windows Server 2016, the vulnerable driver blocklist is also enforced when either memory integrity (also known as hypervisor-protected code integrity or HVCI), Smart App Control, or S mode is active.&quot; [@ms-driver-block-rules] Windows Server 2016 does not get the default-on block list even when HVCI is on. An enterprise admin managing Server 2016 has to deploy an explicit App Control policy to get the same coverage. The October 2022 preview cycle saw a documented quirk -- KB5020779 [@ms-kb5020779] explains that a preview release shipped without an actual blocklist refresh, addressed by a subsequent servicing update [@ms-kb5020779].The KB5020779 episode is a useful reminder that the in-box block list ships through the same Windows Update cycle as everything else. Preview releases do not always carry a fresh policy, and the cadence on the block-rules page [@ms-driver-block-rules] describes the intended steady state rather than every individual update [@ms-driver-block-rules].&lt;/p&gt;
&lt;h3&gt;Naming the weakness, not the publisher&lt;/h3&gt;
&lt;p&gt;For the first time in the story, the question Windows asks at load time is not only &lt;em&gt;who signed this binary?&lt;/em&gt; but also &lt;em&gt;is this specific signed binary one we have learned is unsafe?&lt;/em&gt; The block list is a step the previous generations could not have taken with the primitives they had: it requires a deny list that can be authored after the fact, distributed quickly, and enforced inside a trust domain the attacker cannot reach. KMCS supplied the load-time enforcement primitive; HVCI supplied the immune-from-VTL0 enforcement context; only with both in place could &lt;code&gt;DriverSiPolicy.p7b&lt;/code&gt; actually do its job.&lt;/p&gt;

flowchart TD
    A[Driver image requested for load] --&amp;gt; B[Hypervisor mediates section create]
    B --&amp;gt; C[SKCI verifies Authenticode chain]
    C --&amp;gt; D{&quot;Chain OK?&quot;}
    D -- &quot;No&quot; --&amp;gt; X[Refuse]
    D -- &quot;Yes&quot; --&amp;gt; E[Consult DriverSiPolicy.p7b deny rules]
    E --&amp;gt; F{&quot;Hash, name, or signer on deny list?&quot;}
    F -- &quot;Yes&quot; --&amp;gt; X
    F -- &quot;No&quot; --&amp;gt; G[Allow section creation]
    G --&amp;gt; H[Driver maps into kernel address space]
&lt;h3&gt;The Vulnerable &amp;amp; Malicious Driver Reporting Center&lt;/h3&gt;
&lt;p&gt;The block list grew faster after Microsoft built a structured channel to feed it. The December 2021 Microsoft Security blog post [@ms-vdrc-blog] announced the Vulnerable &amp;amp; Malicious Driver Reporting Center: a portal where researchers and vendors can submit kernel drivers for evaluation, backed by an automated analysis pipeline that looks for the BYOVD primitives -- &quot;the ability to read or write arbitrary kernel, physical, or device memory, including Port I/O and central processing unit (CPU) registers from user mode.&quot; [@ms-vdrc-blog] The post explicitly lists the historical CVE backdrop that motivated the centre, naming RobinHood, Uroburos, Derusbi, GrayFish, and Sauron as families that leveraged driver vulnerabilities such as CVE-2008-3431, CVE-2013-3956, CVE-2009-0824, and CVE-2010-1592 [@ms-vdrc-blog].&lt;/p&gt;
&lt;p&gt;The same post anchors the EV-certificate publisher requirement and the HLK or attestation gating that produces the block list&apos;s inputs in the first place. The reporting centre is the path by which a flagged driver moves from &quot;spotted in research&quot; to &quot;deny rule in the next quarterly XML push&quot;.&lt;/p&gt;
&lt;h3&gt;Defender ASR as the HVCI-off coverage path&lt;/h3&gt;
&lt;p&gt;There is a third surface worth knowing about. Microsoft&apos;s Attack Surface Reduction rules [@ms-asr-rules] include &quot;Block abuse of exploited vulnerable signed drivers&quot; (&lt;code&gt;56a863a9-875e-4185-98a7-b882c64b5ce5&lt;/code&gt;) as part of the standard ASR protection set [@ms-asr-rules]. For Microsoft Defender for Endpoint customers on Windows 10 E3 or E5, the rule covers machines where HVCI is not on. Microsoft notes that &quot;the same blocklist is also used by Microsoft Defender Antivirus customers&quot; via the ASR rule [@ms-vdrc-blog]. The path is narrower than HVCI-rooted enforcement -- Defender has to be running, the rule has to be enabled -- but it extends the block list to enterprise environments that have not yet flipped HVCI on.&lt;/p&gt;
&lt;h3&gt;LOLDrivers and the dual-use externality&lt;/h3&gt;
&lt;p&gt;The block list is not the only catalogue of vulnerable Windows drivers. The community-maintained LOLDrivers project [@loldrivers-io] -- &quot;Living Off The Land Drivers&quot; -- collects vulnerable, malicious, and known-malicious Windows drivers in one place. Every entry carries YAML metadata and where possible YARA, Sigma, ClamAV, and Sysmon rules, plus a pre-compiled App Control deny policy that can be deployed standalone [@gh-loldrivers] [@loldrivers-io]. As of the source verification for this article, LOLDrivers carried approximately 2,132 driver entries -- considerably more than the Microsoft-shipped list.&lt;/p&gt;
&lt;p&gt;Check Point Research called out the dual-use problem in their 2024 piece [@cpr-byovd]: a public catalogue of vulnerable drivers is also a reading list for attackers. The same researchers ran the methodology in reverse: &quot;we conducted a mass hunt for new drivers that may be vulnerable, uncovering thousands of potentially at-risk drivers.&quot; [@cpr-byovd] Defenders use the list for hardening; attackers use it for shopping. Both effects are real.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Defenders who can tolerate compatibility risk can compile the source XML from the block-rules page [@ms-driver-block-rules] into an App Control policy and deploy it directly, picking up the entries Microsoft holds back from the in-box list. Optionally layer the LOLDrivers App Control policy [@gh-loldrivers] on top for community-curated coverage. Test in audit mode first -- both lists are more aggressive than the shipped baseline and may flag drivers your environment depends on [@ms-driver-block-rules] [@gh-loldrivers].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;A WDAC rule evaluator, in miniature&lt;/h3&gt;
&lt;p&gt;The semantics of an App Control policy are simple enough to model in a few lines. Deny rules win; allow rules are consulted next; the default action handles whatever is left.&lt;/p&gt;
&lt;p&gt;{`
// Simplified model of the App Control / WDAC rule-evaluation engine.
// Deny rules win, allow rules permit the remainder, and an explicit
// default action handles images neither denied nor allowed.&lt;/p&gt;
&lt;p&gt;const policy = {
  denyByHash:    new Set([&quot;c1d5cf8c43e7679b782630e93f5e6420ca1749a7&quot;]), // Capcom.sys
  denyByName:    new Set([&quot;RTCore64.sys&quot;]),
  denyBySigner:  new Set([&quot;CN=Some Compromised Publisher, O=Example&quot;]),
  allowBySigner: new Set([&quot;CN=Microsoft Windows, O=Microsoft Corporation&quot;]),
  defaultAction: &quot;BLOCK&quot;,
};&lt;/p&gt;
&lt;p&gt;function evaluate(image, policy) {
  if (policy.denyByHash.has(image.sha1)) return &quot;BLOCK (hash on deny list)&quot;;
  if (policy.denyByName.has(image.fileName)) return &quot;BLOCK (name on deny list)&quot;;
  if (policy.denyBySigner.has(image.signer)) return &quot;BLOCK (signer on deny list)&quot;;
  if (policy.allowBySigner.has(image.signer)) return &quot;ALLOW (signer on allow list)&quot;;
  return policy.defaultAction === &quot;ALLOW&quot;
    ? &quot;ALLOW (default)&quot;
    : &quot;BLOCK (default)&quot;;
}&lt;/p&gt;
&lt;p&gt;const cases = [
  { sha1: &quot;c1d5cf8c43e7679b782630e93f5e6420ca1749a7&quot;, fileName: &quot;Capcom.sys&quot;,
    signer: &quot;CN=CAPCOM Co., Ltd.&quot; },
  { sha1: &quot;0000000000000000000000000000000000000000&quot;, fileName: &quot;RTCore64.sys&quot;,
    signer: &quot;CN=Micro-Star International Co., Ltd.&quot; },
  { sha1: &quot;1111111111111111111111111111111111111111&quot;, fileName: &quot;ntfs.sys&quot;,
    signer: &quot;CN=Microsoft Windows, O=Microsoft Corporation&quot; },
];
for (const c of cases) console.log(c.fileName, &quot;-&amp;gt;&quot;, evaluate(c, policy));
`}&lt;/p&gt;
&lt;p&gt;Naming the weakness is genuinely new. But the list only ever lists what someone has already found. The window between disclosure and enforcement is months, and Microsoft documents that the shipped list is by design weaker than the published one. What gets the rest of the way?&lt;/p&gt;
&lt;h2&gt;8. The 2026 Stack: Defence in Depth Made Concrete&lt;/h2&gt;
&lt;p&gt;On a default-configured Windows 11 22H2 machine in 2026, a kernel driver that tries to load passes through five distinct gates. Each one closes a blind spot the previous one cannot reach.&lt;/p&gt;
&lt;p&gt;The order matters, and so do the dependencies. The gates are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Kernel-Mode Code Signing.&lt;/strong&gt; The Authenticode chain must terminate at a Microsoft-owned root. The chain check rejects unsigned drivers and drivers chained to non-Microsoft roots, except under the documented grandfathering carve-outs [@ms-kmcs-policy] [@ms-kmcs-policy].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Vulnerable Driver Block List.&lt;/strong&gt; SKCI consults &lt;code&gt;DriverSiPolicy.p7b&lt;/code&gt; for hash, file-name, and signer-level deny rules. The list is default-on for every client device since Windows 11 22H2 [@ms-blogs-win11-2022], and is updated quarterly through Microsoft Learn&apos;s published source XML and monthly through Windows servicing [@ms-driver-block-rules] [@ms-blogs-win11-2022].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HVCI / SKCI.&lt;/strong&gt; The Code Integrity engine runs in VTL1, verifies signatures at section-mapping time rather than only at &lt;code&gt;IoLoadDriver&lt;/code&gt;, and enforces W^X on kernel memory. The policy engine is structurally out of reach of a fully compromised VTL0 kernel [@ms-hvci-vbs].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;App Control / Smart App Control.&lt;/strong&gt; Enterprise admins author explicit App Control allowlists; consumer devices on clean Windows 11 installs run Smart App Control [@ms-sac-faq], a Microsoft-authored allowlist policy backed by cloud reputation [@ms-sac-faq] [@ms-appcontrol].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Defender ASR.&lt;/strong&gt; On Microsoft Defender for Endpoint deployments, the &quot;Block abuse of exploited vulnerable signed drivers&quot; ASR rule extends block-list coverage to HVCI-off environments [@ms-asr-rules].&lt;/li&gt;
&lt;/ol&gt;

The Windows 11 22H2+ consumer-facing front end for App Control for Business. SAC enforces a Microsoft-authored policy and supplements it with cloud reputation lookups from the Intelligent Security Graph. SAC is only available on clean installs and is shipped in evaluation mode by default; once turned on, it also unconditionally enforces the vulnerable driver block list [@ms-sac-faq].

The cloud-backed reputation service that Smart App Control consults to predict whether a given binary is safe. When confident, ISG approves the binary; when unconfident, SAC falls back to signature checks; absent both, the binary is blocked [@ms-sac-faq].
&lt;h3&gt;Orthogonality, not redundancy&lt;/h3&gt;
&lt;p&gt;The five gates look redundant from a distance. They are not. Each closes a class of failure the others cannot reach. The orthogonality is the reason for the stack.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Gate&lt;/th&gt;
&lt;th&gt;Catches&lt;/th&gt;
&lt;th&gt;Misses&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;KMCS&lt;/td&gt;
&lt;td&gt;Unsigned and cross-cert-only-signed drivers&lt;/td&gt;
&lt;td&gt;Signed-but-vulnerable drivers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block list&lt;/td&gt;
&lt;td&gt;Known-vulnerable signed drivers (post-disclosure)&lt;/td&gt;
&lt;td&gt;Unknown-vulnerable signed drivers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HVCI / SKCI&lt;/td&gt;
&lt;td&gt;&lt;code&gt;g_CiOptions&lt;/code&gt;-patching from VTL0; writable+executable kernel pages&lt;/td&gt;
&lt;td&gt;Behavioural BYOVD inside a properly-signed driver&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WDAC / SAC&lt;/td&gt;
&lt;td&gt;Anything not on the allowlist (enterprise) or unknown-reputation (consumer)&lt;/td&gt;
&lt;td&gt;Allowlisted drivers with unknown defects&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Defender ASR&lt;/td&gt;
&lt;td&gt;Block-list entries on HVCI-off machines (where the rule is enabled)&lt;/td&gt;
&lt;td&gt;Drivers not on Microsoft&apos;s blocklist&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The matrix is the practical justification for the stack. If &lt;code&gt;DriverSiPolicy.p7b&lt;/code&gt; had perfect coverage there would be no need for SAC; if SAC had a complete allowlist there would be no need for the block list; if HVCI proved driver safety rather than driver identity there would be no need for either. None of those preconditions hold, and section 9 explains why they cannot.&lt;/p&gt;
&lt;h3&gt;Smart App Control&apos;s particulars&lt;/h3&gt;
&lt;p&gt;SAC merits a few specifics because its behaviour differs from the rest of the stack in ways that surprise readers. First, it is consumer-facing and only available on clean Windows 11 installs -- an upgrade does not get SAC. Second, SAC ships in &lt;em&gt;evaluation mode&lt;/em&gt; by default. Windows watches user behaviour, and if the user mostly runs cloud-reputable software, SAC quietly flips to &lt;em&gt;enforce&lt;/em&gt;; if the user runs a lot of niche or self-developed software, SAC quietly flips to &lt;em&gt;off&lt;/em&gt;. Third, until a 2024 servicing change [@ms-sac-faq] made SAC re-enableable from Windows Security, turning SAC off used to require a clean install to bring it back [@ms-sac-faq]. Fourth, on enterprise-managed devices, SAC turns itself off automatically after 48 hours; managed environments are expected to deploy WDAC instead [@ms-appcontrol].&lt;/p&gt;
&lt;p&gt;The cold-start failure mode is worth knowing. A small independent hardware vendor whose driver has never been seen at scale lacks a cloud reputation when SAC asks about it. The fallback is signature, but a signed driver from an unknown publisher does not always clear SAC&apos;s confidence threshold. Small IHVs occasionally find their drivers blocked on consumer hardware running SAC for that reason alone.&lt;/p&gt;

flowchart TD
    A[Driver image requested] --&amp;gt; B[Gate 1: KMCS Authenticode chain]
    B --&amp;gt; C{&quot;Microsoft-rooted?&quot;}
    C -- &quot;No&quot; --&amp;gt; X[Refuse]
    C -- &quot;Yes&quot; --&amp;gt; D[Gate 2: DriverSiPolicy.p7b]
    D --&amp;gt; E{&quot;On block list?&quot;}
    E -- &quot;Yes&quot; --&amp;gt; X
    E -- &quot;No&quot; --&amp;gt; F[Gate 3: HVCI / SKCI section mapping]
    F --&amp;gt; G{&quot;Signature OK, W^X satisfied?&quot;}
    G -- &quot;No&quot; --&amp;gt; X
    G -- &quot;Yes&quot; --&amp;gt; H[Gate 4: App Control / SAC]
    H --&amp;gt; I{&quot;On allowlist or reputable?&quot;}
    I -- &quot;No&quot; --&amp;gt; X
    I -- &quot;Yes&quot; --&amp;gt; J[Gate 5: Defender ASR rule applicable]
    J --&amp;gt; K[Driver loads into VTL0 kernel]
&lt;h3&gt;Verifying what the machine actually does&lt;/h3&gt;
&lt;p&gt;The state of the stack on any given Windows machine is observable. The Win32_DeviceGuard WMI class exposes a &lt;code&gt;SecurityServicesRunning&lt;/code&gt; array whose integer codes name the security services currently active. The aside below covers the practitioner-facing details.&lt;/p&gt;

Two commands answer most of the question. From an elevated PowerShell prompt, `Get-CimInstance -Namespace root\Microsoft\Windows\DeviceGuard -ClassName Win32_DeviceGuard` returns a structure whose `SecurityServicesRunning` array enumerates the services in operation; a value of **1** indicates **Credential Guard**, a value of **2** indicates **HVCI / Memory Integrity**, and additional values cover newer services (System Guard Secure Launch, SMM Firmware Measurement, Kernel-mode Hardware-enforced Stack Protection, and Hypervisor-Enforced Paging Translation) [@ms-hvci-vbs]. `bcdedit /enum {default}` shows whether `hypervisorlaunchtype` is set to `Auto`, the prerequisite for VBS being on at all. The block list file itself lives at `%windir%\system32\CodeIntegrity\DriverSiPolicy.p7b`; if it is missing, the in-box list is not deployed on that machine. None of these tell you whether your Defender ASR rule is active without a separate `Get-MpPreference` check.
&lt;p&gt;A toy decoder helps make the WMI surface concrete.&lt;/p&gt;
&lt;p&gt;{`
// Mirror of the integer codes the Win32_DeviceGuard WMI class reports
// for SecurityServicesRunning. Documented on Microsoft Learn under
// the Memory Integrity / HVCI guidance.&lt;/p&gt;
&lt;p&gt;const SERVICE_NAMES = {
  1: &quot;Credential Guard&quot;,
  2: &quot;Hypervisor-protected Code Integrity (HVCI / Memory Integrity)&quot;,
  3: &quot;System Guard Secure Launch&quot;,
  4: &quot;SMM Firmware Measurement&quot;,
  5: &quot;Kernel-mode Hardware-enforced Stack Protection&quot;,
  6: &quot;Kernel-mode Hardware-enforced Stack Protection (Audit mode)&quot;,
  7: &quot;Hypervisor-Enforced Paging Translation&quot;,
};&lt;/p&gt;
&lt;p&gt;function explain(servicesRunning) {
  if (!servicesRunning.length) {
    return &quot;No VBS-rooted security services are running on this device.&quot;;
  }
  return servicesRunning
    .map((code) =&amp;gt; SERVICE_NAMES[code] || (&quot;unknown service &quot; + code))
    .map((s) =&amp;gt; &quot;  - &quot; + s)
    .join(&quot;\n&quot;);
}&lt;/p&gt;
&lt;p&gt;console.log(&quot;Sample 1: HVCI on, Credential Guard on&quot;);
console.log(explain([1, 2]));
console.log(&quot;\nSample 2: nothing running&quot;);
console.log(explain([]));
console.log(&quot;\nSample 3: full stack on a Secured-core PC&quot;);
console.log(explain([1, 2, 3, 4, 5]));
`}&lt;/p&gt;
&lt;p&gt;Five gates is a lot of work to do what one ideal gate could not. The reason for the inflation is uncomfortable: the one ideal gate cannot, in principle, exist.&lt;/p&gt;
&lt;h2&gt;9. The Undecidability Wall&lt;/h2&gt;
&lt;p&gt;Why does Windows need five layers to do what one perfect signature ought to do? Because the perfect signature is mathematically impossible.&lt;/p&gt;
&lt;p&gt;The third reframe of this article is the one that turns engineering frustration into theoretical inevitability. The property of interest -- &quot;this signed driver, when exercised through its IOCTL surface, can be coerced into giving an attacker an arbitrary kernel-write primitive&quot; -- is a non-trivial semantic property of the driver&apos;s program text. Rice&apos;s theorem says that for any non-trivial semantic property of programs, the predicate is undecidable on the class of all programs. No algorithm exists that, in finite time, answers correctly for every input.&lt;/p&gt;
&lt;p&gt;A useful way to state the bound: if $P$ is the set of all kernel drivers and $\text{Unsafe}(p) = 1$ iff driver $p$ exposes a kernel-write primitive through its IOCTL handler, then no total computable function $f: P \to {0, 1}$ satisfies $f = \text{Unsafe}$. Every approximation either over-blocks ($f(p) = 1$ when $\text{Unsafe}(p) = 0$, false positives, broken drivers) or under-blocks ($f(p) = 0$ when $\text{Unsafe}(p) = 1$, false negatives, BYOVD in the wild). The signing pipeline scans for the obvious cases; sophisticated dynamic analysers will catch more of the not-obvious cases; but the unrestricted version of the problem has no complete solution.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Whether an arbitrary signed driver can be coerced into giving an attacker a kernel-write primitive is undecidable. No static signing scheme can ever block exactly the unsafe drivers. The Windows answer is therefore not a single perfect gate; it is defence in depth that narrows, but does not close, the gap.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Microsoft&apos;s formal acknowledgement&lt;/h3&gt;
&lt;p&gt;Microsoft has been formally clear about a related point for years: the administrator-to-kernel transition is not, in the MSRC servicing-criteria [@elastic-admin] sense, a security boundary [@elastic-admin]. Elastic Security Labs put the position in plain English: &quot;the blocklist&apos;s deployment model can be slow to adapt to new threats, with updates automatically deployed typically only once or twice a year. Users can manually update their blocklists, but such interventions bring us out of &apos;secure by default&apos; territory ... When determining which vulnerabilities to fix, the Microsoft Security Response Center (MSRC) uses the concept of a security boundary.&quot; [@elastic-admin]&lt;/p&gt;

Administrator-to-kernel is not a security boundary, in the MSRC servicing-criteria sense. The defence-in-depth mechanisms described here mitigate it; from the impossibility result, none can close it.
&lt;p&gt;The MSRC framing is engineering policy. The undecidability result is theoretical inevitability. They land in the same place: an attacker who has administrator privilege, who can pick from the entire history of signed Windows drivers, who is patient, is not stopped by any number of signature checks. The defence-in-depth mechanisms make the attacker work harder; they raise the cost; they shrink the surface of viable signed drivers. They do not close the structural gap.&lt;/p&gt;
&lt;h3&gt;Closeable gaps and irreducible gaps&lt;/h3&gt;
&lt;p&gt;It is worth separating two kinds of gap.&lt;/p&gt;
&lt;p&gt;The published-vs-shipped block list gap is a &lt;em&gt;policy&lt;/em&gt; decision, not an engineering limit. Microsoft documents that &quot;it&apos;s often necessary for us to hold back some blocks to avoid breaking existing functionality.&quot; [@ms-driver-block-rules]The published-vs-shipped gap is the closeable part. An administrator who can author or import an App Control policy can deploy the published XML directly and pick up Microsoft&apos;s full curation. The irreducible part of the gap sits behind it: even the published list lists only what someone has already disclosed. The undecidability result applies to &lt;em&gt;finding&lt;/em&gt; unsafe drivers, not to &lt;em&gt;listing&lt;/em&gt; known-unsafe ones. Defenders willing to accept compatibility risk can close it on their own machines today.&lt;/p&gt;
&lt;p&gt;The gap that cannot close is the one between the published list and the universe of vulnerable drivers Microsoft has not yet learned about. That is where the undecidability result bites. No amount of pipeline tightening eliminates the class of design flaws whose recognition requires understanding what the driver&apos;s IOCTL handler will do under all possible inputs.&lt;/p&gt;
&lt;h3&gt;What static methods &lt;em&gt;can&lt;/em&gt; achieve&lt;/h3&gt;
&lt;p&gt;Quantifying what the existing layers achieve is more useful than lamenting what they cannot. The complexity bounds for each layer are well-defined.&lt;/p&gt;
&lt;p&gt;Authenticode signature verification is bounded below by one public-key operation and one cryptographic hash over the PE image, regardless of policy. SKCI&apos;s per-section cost is dominated by that constant. The Memory Integrity page is conspicuously silent on a published benchmark number; in practice the overhead is small but non-zero on Intel Kabylake-and-later or AMD Zen-2-and-later silicon with MBEC/GMET hardware acceleration, and meaningfully higher on the emulated Restricted-User-Mode fallback path that older silicon falls back to [@ms-hvci-vbs].&lt;/p&gt;
&lt;p&gt;WDAC allowlist evaluation is $O(\log r)$ per image on $r$ rules with a hashed index, or $O(r)$ on the naïve linear scan; the deny-rule check in &lt;code&gt;DriverSiPolicy.p7b&lt;/code&gt; follows the same bound.&lt;/p&gt;
&lt;p&gt;The gap between achievable static enforcement and the ideal &quot;block all and only the unsafe drivers&quot; is, in the limit, irreducible.&lt;/p&gt;
&lt;h3&gt;Three axes that can be improved&lt;/h3&gt;
&lt;p&gt;If the gap cannot close, it can be narrowed along three independent axes -- and the improvements that matter, look like one of these:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Reactiveness.&lt;/strong&gt; The disclosure-to-enforcement latency is months today. Forthcoming WHCP submission-time analyses can compress it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Coverage of unknown-bad signed drivers.&lt;/strong&gt; Reputation, allowlists, and dynamic analysis at scale extend coverage beyond what a static deny list lists.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Visibility into binary contents.&lt;/strong&gt; SBOMs answer &quot;what is inside this driver?&quot; -- a question the signature alone never asked.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each axis is the answer to a different blind spot. None substitutes for another. Section 11 returns to the SBOM axis specifically because it is the one Microsoft is building into the submission flow right now.&lt;/p&gt;
&lt;p&gt;Static signing has hit a wall it cannot push through. The only way forward is to widen the question. Two of the answers exist on other operating systems. The third is being built now.&lt;/p&gt;
&lt;h2&gt;10. The Other Two Operating Systems&lt;/h2&gt;
&lt;p&gt;Linux solved the signing half and pushed the curated-denylist half down to distribution vendors. macOS solved both by making third-party drivers stop being drivers.&lt;/p&gt;
&lt;h3&gt;Linux: signatures without a curated denylist&lt;/h3&gt;
&lt;p&gt;Linux has supported in-kernel module signing since version 3.7 (December 2012), under the configuration symbol &lt;code&gt;CONFIG_MODULE_SIG&lt;/code&gt;. The kernel documentation [@docs-kernel-module-sig] catalogues the supported algorithms: &quot;The built-in facility currently only supports the RSA, NIST P-384 ECDSA and NIST FIPS-204 ML-DSA public key signing standards.&quot; [@docs-kernel-module-sig] The choice of signature scheme is a build-time decision, and the kernel can be told to use a key embedded in the kernel image, a key loaded into the trusted keyring at runtime, or a Machine Owner Key managed by &lt;code&gt;shim&lt;/code&gt; and the platform&apos;s UEFI boot stack.&lt;/p&gt;
&lt;p&gt;The structural decision that matters is the enforcement mode. &lt;code&gt;CONFIG_MODULE_SIG_FORCE&lt;/code&gt; is the toggle. The kernel documentation describes the two settings cleanly: &quot;If this is off (ie. &apos;permissive&apos;), then modules for which the key is not available and modules that are unsigned are permitted, but the kernel will be marked as being tainted ... If this is on (ie. &apos;restrictive&apos;), only modules that have a valid signature that can be verified by a public key in the kernel&apos;s possession will be loaded.&quot; [@docs-kernel-module-sig]&lt;/p&gt;
&lt;p&gt;Most mainstream distributions ship permissive: unsigned modules taint the kernel but load. The defender-shipping-restrictive-enforcement model is real on Secure-Boot-on RHEL and modern Ubuntu, paired with the Linux &lt;em&gt;lockdown&lt;/em&gt; security module, which restricts certain root-level kernel-modification paths even on signed builds.The Linux lockdown LSM is the closest mainline-Linux analogue to HVCI&apos;s policy-out-of-reach property. The &lt;code&gt;kernel_lockdown(7)&lt;/code&gt; man page [@man7-kernel-lockdown] describes lockdown as &quot;designed to prevent both direct and indirect access to a running kernel image&quot; and enumerates the restricted surfaces: &lt;code&gt;/dev/mem&lt;/code&gt;, &lt;code&gt;/dev/kmem&lt;/code&gt;, &lt;code&gt;/dev/kcore&lt;/code&gt;, kprobes, BPF, MSR alteration, ACPI table overrides, and unsigned kexec [@man7-kernel-lockdown]. It is a partial analogue, not equivalent: lockdown still runs in the same trust domain as the kernel it polices, so a sufficient kernel exploit defeats it. HVCI&apos;s VTL0/VTL1 split is structurally stronger.&lt;/p&gt;
&lt;p&gt;What Linux does not have is the equivalent of &lt;code&gt;DriverSiPolicy.p7b&lt;/code&gt;. There is no kernel-level curated denylist of &quot;we have learned this module is unsafe; refuse to load it by name&quot;. Defenders rely on per-distribution CVE trackers, on &lt;code&gt;modprobe.blacklist&lt;/code&gt;, and on &lt;code&gt;udev&lt;/code&gt; rules to keep specific modules out. The G5 generation -- naming the &lt;em&gt;weakness&lt;/em&gt; rather than the publisher -- has no mainline Linux equivalent at the kernel-loader level.&lt;/p&gt;
&lt;h3&gt;macOS: DriverKit removes the surface&lt;/h3&gt;
&lt;p&gt;Apple&apos;s answer is structurally different. Starting with macOS Catalina 10.15 [@apple-legacy-extensions] in 2019, Apple deprecated legacy kernel extensions for third parties and pushed them onto the DriverKit [@apple-driverkit] framework instead [@apple-legacy-extensions] [@apple-driverkit].&lt;/p&gt;

Apple&apos;s user-space driver framework, introduced with macOS Catalina 10.15. Third-party drivers ship as `.dext` user-space extensions linked against a curated IOKit subset; they receive IOKit messages from the kernel and respond with the same operations they used to perform in ring zero, but the code itself runs in user mode under sandbox restrictions. The kernel side of the new model exposes a controlled message surface; the third-party side cannot directly execute kernel code.
&lt;p&gt;A &lt;code&gt;.dext&lt;/code&gt; runs in user space under a sandbox profile. It can claim devices, register for IOKit interrupts, and exchange messages with kernel-side broker code -- but it cannot, in any usable sense, execute arbitrary code in the kernel address space. The Capcom.sys class of vulnerability cannot be expressed in DriverKit: there is no IOCTL surface whose handler runs in ring zero, because the handler does not run in ring zero. Apple reinforces the boundary further with System Integrity Protection [@apple-sip] (since 2015) and, on Apple Silicon, Kernel Integrity Protection (KIP), which makes the kernel page tables read-only after boot [@apple-sip].&lt;/p&gt;
&lt;p&gt;The price was paid by Apple&apos;s IHV community. Whole categories of third-party drivers -- deep audio, virtualisation, certain security tools -- spent years migrating, and some categories took multiple macOS releases before a DriverKit equivalent of a particular kext capability existed. Apple Silicon requires explicit reduced-security mode to load &lt;em&gt;any&lt;/em&gt; legacy kext at all: Apple&apos;s Platform Security guide [@apple-kext-aux] records that &quot;Kexts must be explicitly enabled for a Mac with Apple silicon by holding the power button at startup to enter into One True Recovery (1TR) mode, then downgrading to Reduced Security and checking the box to enable kernel extensions&quot; [@apple-kext-aux].&lt;/p&gt;
&lt;h3&gt;Why Windows cannot copy Apple&lt;/h3&gt;
&lt;p&gt;The reason Windows cannot make Apple&apos;s move in the short term is operational, not architectural. Windows&apos; IHV installed base is orders of magnitude larger and less centrally controlled. Microsoft does not own its hardware vendors the way Apple owns Macs. Breaking compatibility with twenty years of shipped kernel drivers would impose unbounded migration cost on third parties Microsoft cannot direct.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Windows (2026)&lt;/th&gt;
&lt;th&gt;Linux (mainline + RHEL-class hardening)&lt;/th&gt;
&lt;th&gt;macOS (Catalina+ / Apple Silicon)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Default signature enforcement&lt;/td&gt;
&lt;td&gt;Mandatory on x64 since 2006&lt;/td&gt;
&lt;td&gt;Permissive (taints kernel); restrictive on hardened distros&lt;/td&gt;
&lt;td&gt;Mandatory; legacy kexts deprecated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Curated denylist of signed-but-vulnerable artefacts&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DriverSiPolicy.p7b&lt;/code&gt;, default-on since 22H2&lt;/td&gt;
&lt;td&gt;None at kernel loader; per-distro CVE trackers&lt;/td&gt;
&lt;td&gt;Not needed -- third-party kexts removed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Policy engine isolated from kernel it polices&lt;/td&gt;
&lt;td&gt;HVCI in VTL1&lt;/td&gt;
&lt;td&gt;Lockdown LSM (same trust domain)&lt;/td&gt;
&lt;td&gt;KIP and SIP on Apple Silicon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Third-party drivers in kernel&lt;/td&gt;
&lt;td&gt;Yes, still the model&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No -- DriverKit user-space dexts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operational price of the model&lt;/td&gt;
&lt;td&gt;Compatibility carve-outs, opt-outs&lt;/td&gt;
&lt;td&gt;Permissive default&lt;/td&gt;
&lt;td&gt;Multi-year IHV migration&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Windows cannot move drivers to user space at Apple&apos;s speed. But it can look at &lt;em&gt;what is inside&lt;/em&gt; a driver in a way the signature alone never could. And it has been quietly building that capability since 2022.&lt;/p&gt;
&lt;h2&gt;11. What Comes Next: SBOM, Artifact Signing, Dynamic Analysis&lt;/h2&gt;
&lt;p&gt;If signatures cannot answer &quot;is this driver safe&quot;, and the block list can only ever answer &quot;is this driver known-unsafe&quot;, the next question Windows has to learn how to ask is &quot;what is inside this driver?&quot;&lt;/p&gt;
&lt;h3&gt;SBOM for drivers&lt;/h3&gt;
&lt;p&gt;A Software Bill of Materials is a structured inventory of the components, dependencies, and versions inside a software artefact. The mainstream community formats are SPDX (now at version 3.0) and CycloneDX; Microsoft contributes to and ships an open-source tool, microsoft/sbom-tool [@gh-sbom-tool], that produces SPDX-compatible SBOMs as part of a build pipeline [@gh-sbom-tool]. The repository description is plain: &quot;The SBOM tool is a highly scalable and enterprise ready tool to create SPDX 2.2 and SPDX 3.0 compatible SBOMs for any variety of artifacts. The tool uses the Component Detection libraries to detect components and the ClearlyDefined API to populate license information for these components.&quot; [@gh-sbom-tool]&lt;/p&gt;

A machine-readable inventory of components and dependencies inside a software artefact. For a Windows kernel driver, an SBOM lists the third-party static libraries linked into the PE, the open-source code paths bundled with the driver, and the versions of each, in a format (SPDX, CycloneDX) that automated tools can consume to answer &quot;is any component of this driver subject to a known vulnerability?&quot;
&lt;p&gt;The piece that affects Windows drivers specifically is the Windows Hardware Compatibility Program SBOM requirement. The Microsoft Q&amp;amp;A entry on Hardware Dev Center and CRA compliance [@ms-qa-cra] is candid: &quot;The WHCP SBOM requirement (Device.DevFund.Security.SoftwareBillofMaterials) has been deferred and will only be enforced starting in H2 2026.&quot; [@ms-qa-cra] The deferral aligns the WHCP rollout with the European Union&apos;s Cyber Resilience Act compliance window.&lt;/p&gt;

The EU Cyber Resilience Act sets phased compliance obligations for products with digital elements sold into the EU market. Among them is a requirement to produce a machine-readable SBOM that customers and regulators can inspect. Microsoft&apos;s WHCP SBOM mandate, scheduled for H2 2026, is the Windows-specific implementation of the same requirement, applied to kernel drivers submitted through the Hardware Dev Center. For regulated-industry IHVs, the WHCP gate and the CRA gate land at the same time and concern the same artefact [@ms-qa-cra].
&lt;p&gt;There is a structural problem an SBOM does not solve on its own. If the SBOM ships separately from the driver, an attacker who controls the distribution path can substitute a clean-looking SBOM for a contaminated driver. The WHCP submission flow is expected to bind the SBOM cryptographically to the artefact it describes so that a recipient can verify the binding, but the public documentation for the binding mechanism is still light beyond the WHCP SBOM mandate itself [@ms-qa-cra] [@ms-qa-cra].&lt;/p&gt;
&lt;h3&gt;Dynamic analysis at submission time&lt;/h3&gt;
&lt;p&gt;The other axis of improvement is reactiveness. Today, the typical disclosure-to-enforcement cycle for a new BYOVD driver looks like this: vendor ships, attacker exploits, researcher discloses, Microsoft adds to the quarterly published list, Windows servicing pushes to clients. The latency is months. Two recent research programmes show how dynamic analysis at scale can compress it.&lt;/p&gt;
&lt;p&gt;The first is the EURECOM/Politecnico di Milano NDSS 2026 paper on the authors&apos; publication page [@eurecom-paper]. The team built a DRAKVUF-based instrumentation layer called Kernelmon and traced every kernel function executed by signed drivers under malware-loaded workloads [@eurecom-paper]. The numbers are unusually concrete: the paper PDF [@eurecom-paper-pdf] reports that the team &quot;analyzed 8,779 malware samples that load 773 distinct signed drivers. It flagged suspicious behavior in 48 drivers, and subsequent manual verification led to the responsible disclosure of seven previously unknown vulnerable drivers&quot; [@eurecom-paper-pdf]. The companion S3 blog post [@eurecom-s3-blog] corroborates the 48-flagged / 7-disclosed numbers and notes that one of the seven received CVE-2024-26506 [@eurecom-s3-blog]. The technique is dynamic: it runs the driver under a hypervisor, watches what its IOCTL handlers actually do, and flags patterns characteristic of the BYOVD class.&lt;/p&gt;
&lt;p&gt;The second is Check Point Research&apos;s 2024 work [@cpr-byovd], which built a mass-hunt methodology around import-table signatures of risky kernel APIs and ran it across the global driver corpus. &quot;Using the same methodology, we conducted a mass hunt for new drivers that may be vulnerable, uncovering thousands of potentially at-risk drivers.&quot; [@cpr-byovd] The technique is static: it asks &lt;em&gt;what does the driver import?&lt;/em&gt; rather than &lt;em&gt;what does it do under exercise?&lt;/em&gt; Combined, the two approaches cover complementary halves of the surface.&lt;/p&gt;
&lt;p&gt;Neither currently gates Hardware Dev Center submissions. Both are candidates for the kind of submission-time check that would compress disclosure-to-enforcement latency from quarters to days.&lt;/p&gt;
&lt;h3&gt;Empirical patterns the defences have to recognise&lt;/h3&gt;
&lt;p&gt;Cisco Talos&apos;s BYOVD work, summarised in their &lt;em&gt;Exploring vulnerable Windows drivers&lt;/em&gt; post [@talos-byovd], classifies the post-load payloads attackers actually run [@talos-byovd]. Three behaviour classes dominate: token-swap escalation that overwrites the access token in the &lt;code&gt;_EPROCESS&lt;/code&gt; structure to reach SYSTEM; unsigned-code-loading that uses the kernel-write primitive to disable DSE or patch CI state; and EDR-killing that clears the kernel callback registrations endpoint detection products rely on. Each is a target for the dynamic analyses above, each is detectable by import-table heuristics, and each is what defenders see in the wild today.&lt;/p&gt;
&lt;p&gt;The historical roots are old. The Microsoft Security blog tracing the Vulnerable &amp;amp; Malicious Driver Reporting Center is direct: &quot;Multiple malware attacks, including RobinHood, Uroburos, Derusbi, GrayFish, and Sauron, have leveraged driver vulnerabilities (for example CVE-2008-3431, CVE-2013-3956, CVE-2009-0824, and CVE-2010-1592).&quot; [@ms-vdrc-blog] The payload classes have stayed remarkably stable for fifteen years.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The structural gap between &lt;em&gt;signed&lt;/em&gt; and &lt;em&gt;safe&lt;/em&gt; cannot close. It can be narrowed along three independent axes. Reactiveness: how long disclosure-to-enforcement takes (closeable by submission-time dynamic analysis along the lines of the EURECOM NDSS 2026 paper [@eurecom-paper] [@eurecom-paper] and Check Point&apos;s mass-hunt methodology [@cpr-byovd] [@cpr-byovd]). Coverage of unknown-bad signed drivers (extended by reputation-backed allowlists like Smart App Control and by WDAC enterprise policies). Visibility into binary contents (the H2 2026 WHCP SBOM mandate [@ms-qa-cra] and the SBOM-to-artefact binding the submission flow is expected to enforce [@ms-qa-cra]). Each axis closes a different blind spot. None substitutes for another.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Threats the stack cannot yet absorb&lt;/h3&gt;
&lt;p&gt;Three problems remain open and uncovered by the published roadmap. The Smart App Control cold-start window leaves small IHVs whose drivers have no cloud reputation to fall through to signature, and signature alone is exactly what we already established does not answer the question. BYOVD on HVCI-off environments, prevalent in older anti-cheat configurations and on enterprise machines with legacy ISV drivers, still admits the &lt;code&gt;g_CiOptions&lt;/code&gt;-patching family from VTL0 because there is no VTL1 to keep the policy out of reach. And the shipped-vs-published block list gap, while operationally rational and individually closeable by a willing administrator, is a gap any default-on customer carries.&lt;/p&gt;
&lt;p&gt;None of those closes by algorithmic improvement. Each closes only by widening the question.&lt;/p&gt;
&lt;p&gt;What started as a yes/no signature check has become a continually expanding set of questions Windows asks before it will hand a driver the keys to ring zero. None of those questions is sufficient. All of them are necessary. And the next one is already being written into the WHCP submission flow.&lt;/p&gt;
&lt;h2&gt;12. What This Means in Practice&lt;/h2&gt;
&lt;p&gt;Three audiences, three things to do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Administrators.&lt;/strong&gt; Confirm the stack is on. &lt;code&gt;Get-CimInstance -Namespace root\Microsoft\Windows\DeviceGuard -ClassName Win32_DeviceGuard&lt;/code&gt; returns a &lt;code&gt;SecurityServicesRunning&lt;/code&gt; array; a &lt;code&gt;2&lt;/code&gt; in the array confirms HVCI. A &lt;code&gt;DriverSiPolicy.p7b&lt;/code&gt; in &lt;code&gt;%windir%\system32\CodeIntegrity\&lt;/code&gt; confirms the in-box block list is deployed. If you can tolerate the compatibility risk, compile the published block-rules XML [@ms-driver-block-rules] into an App Control policy and deploy it (audit mode first) [@ms-driver-block-rules]. If you run Windows Server 2016, you have to deploy an explicit policy yourself because the in-box default does not apply there [@ms-driver-block-rules]. If you ship through the Hardware Dev Center, schedule the H2 2026 WHCP SBOM gate now [@ms-qa-cra]. Subscribe to the Vulnerable &amp;amp; Malicious Driver Reporting Center cadence for new disclosures [@ms-vdrc-blog].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Driver authors.&lt;/strong&gt; Assume your IOCTL surface will be read by Check Point&apos;s import-table mass hunt [@cpr-byovd] and exercised by EURECOM&apos;s Kernelmon [@eurecom-paper] [@cpr-byovd] [@eurecom-paper]. Any handler that takes a user-supplied address and returns kernel data, or that dispatches a user-supplied function pointer, will end up on a block list on its current trajectory.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Researchers.&lt;/strong&gt; The field is wide open. The undecidability result is real, but the practical gap between what current analyses detect and what is, in principle, detectable for any specific vulnerability class is large. The NDSS 2026 paper found seven CVE-worthy drivers in a corpus of 773. The next paper will find more.&lt;/p&gt;
&lt;h3&gt;Every layer is somebody&apos;s incident report&lt;/h3&gt;
&lt;p&gt;Every layer in the 2026 stack exists because the previous one lost to a named adversary. Sony BMG XCP retired advisory signing. Stuxnet retired the assumption that a valid chain is a safe chain. Capcom.sys retired the assumption that a safe chain is a safe driver. RTCore64.sys, gdrv.sys, and KProcessHacker retired the assumption that the BYOVD class would burn itself out. Each entry on &lt;code&gt;DriverSiPolicy.p7b&lt;/code&gt; is somebody&apos;s incident report, recorded in the most permanent place Microsoft can put it -- the kernel loader&apos;s deny list.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Windows 11 22H2 ships with a list of drivers Microsoft will not load. The next list will be longer. The story has been adversarial since 1996 and the trajectory does not reverse: every layer was added because the previous one met an attacker. The structural gap is undecidable; the engineering gap, narrowable; the work, unfinished.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Frequently Asked Questions&lt;/h2&gt;


No. HVCI verifies the Authenticode signature at section-mapping time and enforces a write-xor-execute invariant on kernel memory; it does not analyse the driver&apos;s IOCTL surface. A signed driver with an unsafe IOCTL passes HVCI unchanged and proceeds to execute its handler in kernel mode with kernel privilege. That is what the Vulnerable Driver Block List is for: HVCI gates *who decides*; the block list gates *what gets decided*. See the Memory Integrity page [@ms-hvci-vbs] [@ms-hvci-vbs].

Yes. Microsoft publishes the source XML on the Microsoft-recommended driver block rules page [@ms-driver-block-rules] [@ms-driver-block-rules]. You can compile it into a binary App Control policy with the standard tooling and deploy it directly, picking up entries Microsoft holds back from the in-box list. Test in audit mode first because the published list is more inclusive than the shipped list and may flag drivers your environment depends on. Many defenders layer the LOLDrivers App Control policy [@gh-loldrivers] on top for community-curated coverage [@gh-loldrivers].

Windows Server 2016 does not enforce the block list by default, even when Memory Integrity is on. The block-rules page [@ms-driver-block-rules] calls this out explicitly [@ms-driver-block-rules]. If you administer Server 2016, deploy an explicit App Control policy to get the same coverage as the default-on 22H2 client.

App Control for Business (the engine formerly known as WDAC) is a policy *you* author. You define what signers, hashes, and paths are allowed; you ship and enforce the policy yourself. Smart App Control is a Microsoft-authored policy bundled with cloud reputation lookups via the Intelligent Security Graph. SAC is the consumer-friendly front end; App Control is the enterprise back end. SAC&apos;s default policy ships at `%windir%\schemas\CodeIntegrity\ExamplePolicies\SmartAppControl.xml`. SAC is consumer-only and turns itself off after 48 hours on enterprise-managed devices, where the expectation is that the operator deploys an App Control policy directly. See the Smart App Control FAQ [@ms-sac-faq] and the App Control for Business overview [@ms-appcontrol] [@ms-sac-faq] [@ms-appcontrol].

Increasingly yes. Major anti-cheat vendors have shipped HVCI-compatible kernel components since around 2023, but a meaningful tail of older configurations still requires HVCI off. On those configurations, the `g_CiOptions`-patching technique TrustedSec describes [@trustedsec-gcioptions] is back in play because the policy variable is no longer protected behind VTL1 [@trustedsec-gcioptions]. Audit your gaming-rig population if you care about coverage.

The in-box block list is Microsoft-curated with explicit compatibility holdbacks; the LOLDrivers catalogue [@loldrivers-io] is community-curated, considerably more inclusive (approximately 2,132 entries as of the source verification for this article), and ships with App Control deny policies, Sigma, YARA, ClamAV, and Sysmon detection content alongside the entries [@loldrivers-io] [@gh-loldrivers]. For threat hunting, use both. For enforcement, layer the LOLDrivers App Control policy on top of the in-box list if your environment can tolerate the compatibility risk. Check Point Research [@cpr-byovd] has documented the dual-use externality of any such public list -- attackers also read them -- but the defender net benefit of broader coverage outweighs the marginal attacker advantage on most environments [@cpr-byovd].

&lt;p&gt;&amp;lt;StudyGuide slug=&quot;vulnerable-driver-block-list-hvci-and-the-driver-signing-lifecycle&quot; keyTerms={[
  { term: &quot;Authenticode&quot;, definition: &quot;Microsoft&apos;s PKCS#7 code-signing format, used in Windows since 1996. Attests to publisher identity; does not analyse program behaviour.&quot; },
  { term: &quot;KMCS&quot;, definition: &quot;Kernel-Mode Code Signing. The mandatory load-time signature policy on 64-bit Windows since Vista x64 in 2006.&quot; },
  { term: &quot;BYOVD&quot;, definition: &quot;Bring Your Own Vulnerable Driver. An attack pattern in which an adversary installs a signed but design-vulnerable third-party driver to gain kernel capability.&quot; },
  { term: &quot;HVCI&quot;, definition: &quot;Hypervisor-protected Code Integrity, also called Memory Integrity. The Code Integrity engine running in VTL1 under a Hyper-V root, isolated from the VTL0 kernel.&quot; },
  { term: &quot;VTL&quot;, definition: &quot;Virtual Trust Level. VTL0 is the normal Windows kernel; VTL1 is the Secure Kernel and trustlets. VTL1 can read VTL0 memory but not vice versa.&quot; },
  { term: &quot;DriverSiPolicy.p7b&quot;, definition: &quot;The Microsoft-signed App Control deny policy that lists known-vulnerable signed kernel drivers and is default-on for all Windows 11 22H2 client devices.&quot; },
  { term: &quot;App Control for Business&quot;, definition: &quot;Microsoft&apos;s policy-driven application control engine, formerly WDAC. Used for both deny lists (the block list) and enterprise allowlists.&quot; },
  { term: &quot;Smart App Control&quot;, definition: &quot;Consumer-facing front end for App Control, backed by ISG cloud reputation. Available on clean Windows 11 22H2+ installs only.&quot; },
  { term: &quot;SBOM&quot;, definition: &quot;Software Bill of Materials. Machine-readable inventory of components and dependencies. Mandatory for WHCP submissions from H2 2026.&quot; },
  { term: &quot;DriverKit&quot;, definition: &quot;Apple&apos;s user-space driver framework. Third-party drivers ship as sandboxed dexts rather than kernel extensions; the BYOVD class is eliminated by construction.&quot; },
]} questions={[
  { q: &quot;Why did the Windows kernel-driver signing policy have to wait until Vista x64 to become mandatory?&quot;, a: &quot;The advisory SetupAPI-prompt model on 32-bit Windows could not be made mandatory without breaking compatibility with decades of unsigned drivers. The x64 architecture was a young platform with relatively few drivers in the field, which let Microsoft make the load-time signature requirement mandatory without disrupting an installed base.&quot; },
  { q: &quot;What single property of HVCI makes the g_CiOptions patching technique stop working?&quot;, a: &quot;HVCI runs the signature-verification and policy-consultation logic inside VTL1&apos;s Secure Kernel and uses Kernel Data Protection, exposed to VTL0 drivers as MmProtectDriverSection, to mark the VTL0 page containing g_CiOptions read-only at the second-level address translation level. The variable still resides in ci.dll&apos;s VTL0 data section, but a VTL0 ring-zero write to it faults because the SLAT mapping refuses the write -- and a live-kernel debugger attached to VTL0 cannot bypass that protection either.&quot; },
  { q: &quot;Why does Microsoft document that the published block list is more inclusive than the shipped one?&quot;, a: &quot;Some entries in the published list would block drivers that legitimate environments still depend on. Microsoft holds those entries back from the in-box DriverSiPolicy.p7b to avoid breaking existing functionality, while leaving them available in the source XML for defenders who can author their own App Control policies and accept the compatibility risk.&quot; },
  { q: &quot;Why is the BYOVD class undecidable to gate at the signing stage?&quot;, a: &quot;Whether an arbitrary signed driver exposes a kernel-write primitive through its IOCTL surface is a non-trivial semantic property of the driver&apos;s program text. Rice&apos;s theorem says no algorithm decides such properties for all programs. Static and dynamic analyses catch decidable subsets; the unrestricted class admits no complete solution.&quot; },
  { q: &quot;Why can Windows not simply move third-party drivers to user space the way macOS DriverKit did?&quot;, a: &quot;Apple owns its hardware vendors and could impose a multi-year migration on a comparatively centralised vendor community. Windows&apos; third-party IHV base is much larger and more independent; breaking compatibility with twenty years of shipped kernel drivers would impose unbounded migration cost on parties Microsoft does not direct.&quot; },
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>windows-kernel</category><category>code-signing</category><category>hvci</category><category>byovd</category><category>driver-block-list</category><category>secure-kernel</category><category>app-control</category><category>kmcs</category><author>noreply@paragmali.com (Parag Mali)</author></item><item><title>Authenticode and Catalog Files: The Crypto Foundation Under WDAC</title><link>https://paragmali.com/blog/authenticode-and-catalog-files-the-crypto-foundation-under-w/</link><guid isPermaLink="true">https://paragmali.com/blog/authenticode-and-catalog-files-the-crypto-foundation-under-w/</guid><description>Every Windows trust decision -- UAC, SmartScreen, WDAC, kernel-driver loading -- bottoms out on the same PKCS#7 SignedData envelope shipped in IE 3 in August 1996. Here is the byte-level reason.</description><pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate><content:encoded>
Every Windows trust decision -- UAC, SmartScreen, App Control for Business (WDAC), and kernel-mode driver loading -- bottoms out on the same PKCS#7 / CMS `SignedData` envelope that Microsoft shipped with Internet Explorer 3 in August 1996. This article dissects that envelope byte by byte: the `WIN_CERTIFICATE` record inside the PE certificate table, the `SpcIndirectDataContent` attribute that signs a hash rather than a file (which is what makes catalog signing and per-page hashing possible), the RFC 3161 timestamp tokens that keep 2010 signatures verifying in 2026, and the `Microsoft Code Verification Root` kernel chain. We follow the named incidents that drove every post-2010 retrenchment -- Stuxnet, Flame, CVE-2013-3900, ShadowHammer, the 2022 Vulnerable Driver Blocklist, the 2026 Bitwarden CLI npm hijack -- and finish at the WDAC rule levels (`Publisher`, `FilePublisher`, `WHQL`) that finally surface those primitives to administrators as policy.
&lt;h2&gt;1. The verified-publisher question&lt;/h2&gt;
&lt;p&gt;On 17 June 2010, Sergey Ulasen and his colleagues at VirusBlokAda in Minsk began circulating a sample of a worm that would, a month later, be named Stuxnet [@wiki-stuxnet][@stuxnet-dossier]. Two of its kernel-mode components, &lt;code&gt;mrxcls.sys&lt;/code&gt; and &lt;code&gt;mrxnet.sys&lt;/code&gt;, were signed -- properly, by Authenticode-conformant certificates issued to Realtek Semiconductor Corp. and shortly afterwards by JMicron Technology Corp. [@stuxnet-dossier][@archive-stuxnet-dossier-details]. The Windows kernel loaded them because the certificate chains validated. The chains validated because, cryptographically, nothing was wrong.&lt;/p&gt;
&lt;p&gt;That sentence is the lens for everything in this article. Microsoft&apos;s code-identity system did its job exactly as designed, and a piece of state-grade sabotage walked through it. The next forty minutes of reading reconstruct what the kernel checks before loading a driver, why those checks could not have caught Stuxnet, and what Microsoft layered on top during the next fourteen years so that the next stolen Realtek private key has less reach.&lt;/p&gt;
&lt;h3&gt;Where Authenticode shows up&lt;/h3&gt;
&lt;p&gt;Most Windows users meet Authenticode without realising it. The &lt;a href=&quot;https://paragmali.com/blog/adminless-how-windows-finally-made-elevation-a-security-boun/&quot; rel=&quot;noopener&quot;&gt;User Account Control dialog&lt;/a&gt; that says &quot;Verified publisher: Microsoft Windows&quot; instead of &quot;Publisher: Unknown&quot; is the user-visible end of a long cryptographic chain that bottoms out in a PKCS#7 / CMS &lt;code&gt;SignedData&lt;/code&gt; envelope wrapped inside a &lt;code&gt;WIN_CERTIFICATE&lt;/code&gt; record at the end of the PE file [@mslearn-authenticode-driver][@mslearn-pe-format]. The same plumbing is queried by SmartScreen, by App Control for Business (the 2024 rename of Windows Defender Application Control) [@mslearn-appcontrol-root], by &lt;code&gt;ci.dll&lt;/code&gt; at kernel-driver load [@mslearn-kmcs-policy], and by Windows Update during servicing. They all read the &lt;em&gt;same&lt;/em&gt; bytes in the certificate table; the verdicts differ only in which fields they consult and which policy they overlay.&lt;/p&gt;

flowchart TD
    SD[&quot;PKCS#7 / CMS SignedData&lt;br /&gt;(inside WIN_CERTIFICATE)&quot;]
    UAC[&quot;UAC&lt;br /&gt;&apos;Verified publisher&apos;&quot;]
    SS[&quot;SmartScreen&lt;br /&gt;reputation lookup&quot;]
    WDAC[&quot;App Control for Business (WDAC)&lt;br /&gt;rule evaluation&quot;]
    KMCS[&quot;ci.dll / KMCS&lt;br /&gt;kernel-driver load&quot;]
    SD --&amp;gt; UAC
    SD --&amp;gt; SS
    SD --&amp;gt; WDAC
    SD --&amp;gt; KMCS
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Every Windows trust statement -- UAC, SmartScreen, App Control for Business, kernel-mode driver loading -- is a query against the same small set of structures inside the PE certificate table. Once you can read those structures, you can predict every later trust decision the OS makes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;What you will be able to do by the end of this article&lt;/h3&gt;
&lt;p&gt;By the end of section 7 you should be able to decode every line of &lt;code&gt;signtool verify /v /pa /all&lt;/code&gt; output and explain, in one paragraph, why Stuxnet still loaded under a fully patched Windows 7 kernel. By the end of section 11 you should be able to run &lt;code&gt;certutil -CatDB&lt;/code&gt;, &lt;code&gt;New-CIPolicyRule -FilePath ... -Level FilePublisher&lt;/code&gt;, and &lt;code&gt;certutil -hashfile&lt;/code&gt; and explain what every byte of their output corresponds to in the on-disk structure.&lt;/p&gt;
&lt;p&gt;Stuxnet&apos;s kernel components loaded because the chain validated. The chain validated because, cryptographically, nothing was wrong. To understand why that sentence is true -- and what Microsoft has done in the fourteen years since to keep the next stolen Realtek certificate from getting as far -- we have to start in August 1996.&lt;/p&gt;
&lt;h2&gt;2. 1996: PKCS#7, ActiveX, and the original sin of downloadable code&lt;/h2&gt;
&lt;p&gt;Counterintuitively, Authenticode was not invented to sign Windows binaries. It was invented to sign downloadable web payloads.&lt;/p&gt;
&lt;p&gt;On 7 August 1996, Microsoft and VeriSign jointly announced what their press release called &quot;the first technology for secure downloading of software over the Internet&quot; [@press-pass-1996]. The release introduces Authenticode as a feature of Internet Explorer 3 beta 2, names Hank Vigil (&quot;general manager of the electronic commerce group at Microsoft&quot;) and Stratton Sclavos (&quot;president and CEO&quot; of VeriSign), and explicitly anchors the design in &lt;em&gt;open&lt;/em&gt; standards: &quot;Authenticode and VeriSign&apos;s Digital ID service support Internet standards, including the X.509 certificate format and PKCS #7 signature blocks&quot; [@press-pass-1996]. Six days later, on 13 August 1996, Internet Explorer 3 itself shipped as RTM for Microsoft Windows [@wiki-ie3].&lt;/p&gt;
&lt;p&gt;The original motivating problem was ActiveX. An ActiveX control was a downloadable COM binary that the browser would load in-process; without a signature, the browser had no idea who built it. The April 1996 W3C submission that preceded Authenticode is described in the press release as a &quot;code-signing proposal supported by more than 40 companies&quot; [@press-pass-1996]The 40+ company W3C signatory list is the institutional fact that made third-party CA participation possible from day one and seeded the modern multi-vendor code-signing economy. None of the architectural decisions that followed -- catalog signing, RFC 3161 timestamping, EV certificates -- would have been viable inside a single-vendor PKI.. Anchoring the design in X.509 and PKCS#7 instead of inventing a Microsoft-only signature format is the choice that made everything afterwards possible.&lt;/p&gt;
&lt;h3&gt;PKCS#7 was already there&lt;/h3&gt;
&lt;p&gt;By 1996, the &lt;em&gt;envelope&lt;/em&gt; part of the design was solved. RSA Laboratories had published PKCS #7 v1.5 in November 1993 as part of the Public-Key Cryptography Standards series [@rfc-2315]; in March 1998 the IETF republished it verbatim as RFC 2315, &quot;Cryptographic Message Syntax Version 1.5,&quot; authored by Burt Kaliski [@rfc-2315]. The same envelope evolved further: the IETF rebranded it as Cryptographic Message Syntax (CMS) and shipped progressively richer versions through RFCs 2630 (1999), 3369 (2002), 3852 (2004), and 5652 (2009) [@rfc-5652]. Modern Authenticode parsers consume the CMS dialect, but the on-disk envelope structure has barely moved in thirty years.&lt;/p&gt;

The ASN.1 envelope -- originally PKCS#7 v1.5 (Kaliski, 1993; republished as RFC 2315 in 1998), now generalised as CMS in RFC 5652 -- that carries the signature, signed and unsigned attributes, and the chain of X.509 certificates inside the Authenticode certificate-table entry [@rfc-5652].
&lt;p&gt;Authenticode is, in one sentence, &lt;em&gt;&quot;PKCS#7 SignedData carrying a Microsoft-defined content type that hashes the PE file in a specific repeatable way&quot;&lt;/em&gt; [@authenticode-pe-docx]. The asymmetric signature inside that envelope is RSA, the public-key system Rivest, Shamir, and Adleman published in 1978 [@rsa-1978], built on the Diffie-Hellman digital-signature concept introduced in 1976 [@diffie-hellman-1976]. None of that primitive cryptography has changed since. Everything that has changed sits &lt;em&gt;around&lt;/em&gt; the envelope: the algorithms it carries, the catalog store that lets Microsoft sign tens of thousands of files at once, the timestamp tokens that pin a signing moment in time.&lt;/p&gt;

flowchart LR
    DH[&quot;Diffie-Hellman (1976)&lt;br /&gt;digital-signature concept&quot;] --&amp;gt; RSA[&quot;RSA (1978)&quot;]
    RSA --&amp;gt; P7[&quot;PKCS#7 v1.5&lt;br /&gt;(RSA Labs, 1993)&quot;]
    P7 --&amp;gt; R2315[&quot;RFC 2315 (1998)&quot;]
    R2315 --&amp;gt; R2630[&quot;RFC 2630 (1999)&quot;]
    R2630 --&amp;gt; R3369[&quot;RFC 3369 (2002)&quot;]
    R3369 --&amp;gt; R3852[&quot;RFC 3852 (2004)&quot;]
    R3852 --&amp;gt; R5652[&quot;RFC 5652 / CMS&lt;br /&gt;(2009)&quot;]
    P7 --&amp;gt; AC[&quot;Authenticode&lt;br /&gt;(IE3, August 1996)&quot;]
    AC --&amp;gt; WC[&quot;WIN_CERTIFICATE&lt;br /&gt;in modern PE&quot;]
&lt;h3&gt;From one click to four trust decisions&lt;/h3&gt;
&lt;p&gt;The original UX of Authenticode in IE 3 was a &lt;em&gt;modal trust prompt&lt;/em&gt;. The user saw a dialog (&quot;Do you want to install and run [name] signed and distributed by [publisher]?&quot;) and clicked Yes or No. The signature was checked once, and that was the entire trust decision. By 2026, the same &lt;code&gt;SignedData&lt;/code&gt; envelope feeds at least four entirely different trust subsystems -- UAC, SmartScreen, App Control for Business, kernel-mode code integrity -- and most of the time the user clicks nothing at all.&lt;/p&gt;
&lt;p&gt;That layering is what the rest of this article is about. Thirty years on, the on-disk bytes have barely changed. The certificate table at the end of every signed Windows binary still carries a PKCS#7 SignedData envelope, and at the head of that envelope is the same content type -- &lt;code&gt;SpcIndirectDataContent&lt;/code&gt; -- Microsoft defined in 1996. What &lt;em&gt;has&lt;/em&gt; changed is everything around it: the algorithms inside the envelope, the catalog store, the timestamp tokens, the WDAC policy layer on top. Let&apos;s open the envelope and look.&lt;/p&gt;
&lt;h2&gt;3. Anatomy on disk: WIN_CERTIFICATE, PKCS#7 SignedData, SpcIndirectDataContent&lt;/h2&gt;
&lt;p&gt;Where does the signature actually live in a signed &lt;code&gt;.exe&lt;/code&gt;? Most engineers can guess &quot;the end of the file.&quot; Fewer can name the data directory entry, fewer still the wrapper structure, and almost nobody volunteers the exact ASN.1 content type. Four nesting levels matter. Walk them in order and the whole rest of the architecture starts making sense.&lt;/p&gt;
&lt;h3&gt;Level 1: the PE certificate table&lt;/h3&gt;
&lt;p&gt;The PE optional header carries a &lt;code&gt;DataDirectory[16]&lt;/code&gt; array. Entry index 4, &lt;code&gt;IMAGE_DIRECTORY_ENTRY_SECURITY&lt;/code&gt;, points at the &lt;em&gt;certificate table&lt;/em&gt; -- an offset and size into the file [@mslearn-pe-format]. Unlike every other data directory entry, the certificate table is the only one whose offset is a &lt;em&gt;file&lt;/em&gt; offset, not a relative virtual address; the certificate table is never mapped into memory at load time.&lt;/p&gt;
&lt;p&gt;Inside that offset+size region is a sequence of &lt;code&gt;WIN_CERTIFICATE&lt;/code&gt; records, each laid out as:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;typedef struct _WIN_CERTIFICATE {
    DWORD       dwLength;           // total length of this record, including header
    WORD        wRevision;           // WIN_CERT_REVISION_2_0
    WORD        wCertificateType;    // WIN_CERT_TYPE_PKCS_SIGNED_DATA
    BYTE        bCertificate[ANYSIZE_ARRAY];  // the DER-encoded blob
} WIN_CERTIFICATE;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For Authenticode-signed Windows binaries, &lt;code&gt;wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA&lt;/code&gt; (constant value &lt;code&gt;0x0002&lt;/code&gt;), and &lt;code&gt;bCertificate[]&lt;/code&gt; is a DER-encoded CMS / PKCS#7 SignedData blob [@authenticode-pe-docx]. Multiple &lt;code&gt;WIN_CERTIFICATE&lt;/code&gt; records are legal; this is how a single binary can carry both a SHA-1 (legacy) and a SHA-256 (modern) signature, or a dual-signed binary carrying both a primary and a nested secondary embedded signature (via the unsignedAttrs nested-signature mechanism).&lt;/p&gt;

The PE certificate-table record (`dwLength`, `wRevision`, `wCertificateType`, `bCertificate[]`) that wraps a single attribute certificate inside a PE. For Authenticode signatures, `wCertificateType` is `WIN_CERT_TYPE_PKCS_SIGNED_DATA` and `bCertificate` holds a DER-encoded CMS / PKCS#7 SignedData blob [@authenticode-pe-docx][@mslearn-pe-format].
&lt;h3&gt;Level 2: the CMS SignedData envelope&lt;/h3&gt;
&lt;p&gt;Decoding &lt;code&gt;bCertificate&lt;/code&gt; produces an ASN.1 SEQUENCE describing a CMS &lt;code&gt;ContentInfo&lt;/code&gt; whose content type is &lt;code&gt;signedData&lt;/code&gt; (OID &lt;code&gt;1.2.840.113549.1.7.2&lt;/code&gt;). Inside that is the &lt;code&gt;SignedData&lt;/code&gt; structure proper [@rfc-5652]:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;version&lt;/code&gt; -- an integer, typically 1 or 3.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;digestAlgorithms&lt;/code&gt; -- the set of hash algorithms used by any signer (commonly &lt;code&gt;sha256&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;encapContentInfo&lt;/code&gt; -- the content the signers are signing over. &lt;em&gt;This is the field that matters.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;certificates&lt;/code&gt; -- the X.509 chain certificates needed to validate the signers.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;crls&lt;/code&gt; -- optional, almost never populated inline.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;signerInfos&lt;/code&gt; -- one or more &lt;code&gt;SignerInfo&lt;/code&gt; structures, each with the actual signature bytes plus signed and unsigned attributes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each &lt;code&gt;SignerInfo&lt;/code&gt; carries the signing certificate identifier, a set of &lt;code&gt;signedAttrs&lt;/code&gt; (whose digest is what gets signed), an &lt;code&gt;encryptedDigest&lt;/code&gt; (the actual signature bytes), and a set of &lt;code&gt;unsignedAttrs&lt;/code&gt;. The single most important unsigned attribute, in practice, is the RFC 3161 &lt;code&gt;TimeStampToken&lt;/code&gt; -- the counter-signature that pegs the signing event to a moment in time. We will come back to that in section 5.&lt;/p&gt;
&lt;h3&gt;Level 3: SpcIndirectDataContent&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;encapContentInfo.eContentType&lt;/code&gt; for Authenticode is &lt;code&gt;1.3.6.1.4.1.311.2.1.4&lt;/code&gt; -- the OID Microsoft registered for &lt;code&gt;SpcIndirectDataContent&lt;/code&gt;. Inside, the &lt;code&gt;eContent&lt;/code&gt; is a Microsoft-specific ASN.1 structure [@authenticode-pe-docx]:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-asn1&quot;&gt;SpcIndirectDataContent ::= SEQUENCE {
    data        SpcAttributeTypeAndOptionalValue,
    messageDigest DigestInfo
}

SpcAttributeTypeAndOptionalValue ::= SEQUENCE {
    type   OBJECT IDENTIFIER,   -- 1.3.6.1.4.1.311.2.1.15 for PE images
    value  [0] EXPLICIT ANY DEFINED BY type OPTIONAL
}

DigestInfo ::= SEQUENCE {
    digestAlgorithm AlgorithmIdentifier,
    digest          OCTET STRING
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For a PE binary, &lt;code&gt;data.type&lt;/code&gt; is &lt;code&gt;1.3.6.1.4.1.311.2.1.15&lt;/code&gt; (&lt;code&gt;SPC_PE_IMAGE_DATAOBJ&lt;/code&gt;) and &lt;code&gt;data.value&lt;/code&gt; carries a &lt;code&gt;SpcPeImageData&lt;/code&gt; structure describing what kind of image this is (32-bit, 64-bit, importable, executable). The &lt;code&gt;messageDigest.digest&lt;/code&gt; is the &lt;strong&gt;Authenticode hash&lt;/strong&gt; of the PE file [@authenticode-pe-docx]. That hash is &lt;em&gt;not&lt;/em&gt; SHA-256 over the file bytes.&lt;/p&gt;

Microsoft&apos;s `eContentType` registered under OID `1.3.6.1.4.1.311.2.1.4`. Its `messageDigest` field holds the Authenticode hash of the signed artefact, and its `data` field describes what kind of artefact it is (PE image, MSI, script). The fact that this structure signs *a hash* rather than a file is what makes catalog signing possible [@authenticode-pe-docx].
&lt;h3&gt;Level 4: the Authenticode hash and its four exclusions&lt;/h3&gt;
&lt;p&gt;The Authenticode hash is computed over the PE file with four specific byte ranges excluded [@authenticode-pe-docx]:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Excluded region&lt;/th&gt;
&lt;th&gt;Why excluded&lt;/th&gt;
&lt;th&gt;Spec reference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;OptionalHeader.CheckSum&lt;/code&gt; (4 bytes)&lt;/td&gt;
&lt;td&gt;The OS recomputes the optional-header checksum when servicing; signing over it would make every signature invalidate at first patch.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Authenticode_PE.docx&lt;/code&gt; §3.1 [@authenticode-pe-docx]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]&lt;/code&gt; (8 bytes)&lt;/td&gt;
&lt;td&gt;The pointer to the certificate table itself moves when a signature is added; signing over the pointer is a chicken-and-egg loop.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Authenticode_PE.docx&lt;/code&gt; §3.1 [@authenticode-pe-docx]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The certificate-table bytes themselves&lt;/td&gt;
&lt;td&gt;Same chicken-and-egg loop -- the signature cannot sign itself.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Authenticode_PE.docx&lt;/code&gt; §3.1 [@authenticode-pe-docx]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File-alignment padding after each section&lt;/td&gt;
&lt;td&gt;Padding can be different on different builds for harmless reasons (alignment, build-tool quirks); signing over it would punish those harmless differences.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Authenticode_PE.docx&lt;/code&gt; §3.1 [@authenticode-pe-docx]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

The PE digest computed over the file with four regions excluded: the optional-header `CheckSum` field, the `IMAGE_DIRECTORY_ENTRY_SECURITY` data-directory entry, the certificate-table bytes themselves, and the file-alignment padding after each section. Because the excluded regions include the certificate-table area, the same hash remains valid after the signature is appended [@authenticode-pe-docx].
&lt;p&gt;The exclusion of the certificate-table bytes is the design move that makes the whole architecture work. The Authenticode hash is computed &lt;em&gt;first&lt;/em&gt;, signed, and then the signature is appended into the very region the hash excluded. After appending, the hash is still valid; verifying simply recomputes the hash with the same four regions excluded and compares.ASN.1 DER&apos;s tag-length-value shape means that, given enough patience, you can decode every level of the certificate table with nothing but a hex dump. This accessibility is also why parser bugs are particularly damaging: a verifier that re-encodes or normalises before hashing can be tricked into hashing different bytes than the bytes that get loaded -- the structural failure mode at the bottom of CVE-2013-3900 [@nvd-cve-2013-3900].&lt;/p&gt;
&lt;h3&gt;A separate, smaller hash per 4 KiB page&lt;/h3&gt;
&lt;p&gt;Authenticode supports an optional signed attribute, &lt;code&gt;SpcPeImagePageHashes2&lt;/code&gt;, with OID &lt;code&gt;1.3.6.1.4.1.311.2.3.2&lt;/code&gt; (SHA-256). It carries a sequence of &lt;code&gt;(RVA, hash)&lt;/code&gt; pairs, one hash per 4 KiB page of the PE image [@authenticode-pe-docx]. The older &lt;code&gt;1.3.6.1.4.1.311.2.3.1&lt;/code&gt; SHA-1 variant is effectively deprecated. Under &lt;a href=&quot;https://paragmali.com/blog/wdac--hvci-code-integrity-at-every-layer-in-windows/&quot; rel=&quot;noopener&quot;&gt;Hypervisor-Protected Code Integrity (HVCI)&lt;/a&gt;, the page hashes are validated at demand-fault time: when the OS faults in a page from disk, HVCI hashes the page and compares it to the signed page-hash entry before mapping the page as executable. Whole-file integrity checking at load is &lt;em&gt;not&lt;/em&gt; the same as runtime integrity checking at fault; page hashes are what closes that gap.ARM64 Windows configurations have used 4 KiB native pages on the systems that ship Authenticode page-hash enforcement to date. The page-hash attribute encodes RVAs into the on-disk image, so any future move to 16 KiB or 64 KiB page granularity would require a corresponding spec revision.&lt;/p&gt;

An optional signed attribute (OID `1.3.6.1.4.1.311.2.3.2` for SHA-256) carrying a sequence of `(RVA, SHA-256)` pairs, one per 4 KiB page of the PE image. The hashes are checked at demand-fault time by HVCI / Code Integrity, not just at load time [@authenticode-pe-docx].
&lt;h3&gt;The whole nest, in one picture&lt;/h3&gt;

flowchart TD
    PE[&quot;PE file on disk&quot;]
    OH[&quot;Optional header&quot;]
    DD[&quot;DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY] (entry 4)&quot;]
    WC[&quot;WIN_CERTIFICATE record&lt;br /&gt;(dwLength, wRevision, wCertificateType, bCertificate[])&quot;]
    SD[&quot;PKCS#7 / CMS SignedData&quot;]
    Certs[&quot;certificates: X.509 chain&quot;]
    SI[&quot;SignerInfo&quot;]
    Sa[&quot;signedAttrs&quot;]
    SIDC[&quot;encapContentInfo: SpcIndirectDataContent&quot;]
    SPI[&quot;data: SpcPeImageData (SPC_PE_IMAGE_DATAOBJ)&quot;]
    MD[&quot;messageDigest: Authenticode hash&quot;]
    PH[&quot;SpcPeImagePageHashes2 (optional)&quot;]
    ED[&quot;encryptedDigest: signature bytes&quot;]
    Ua[&quot;unsignedAttrs&quot;]
    TST[&quot;RFC 3161 TimeStampToken&lt;br /&gt;(OID 1.2.840.113549.1.9.16.2.14)&quot;]
    PE --&amp;gt; OH
    OH --&amp;gt; DD
    DD --&amp;gt; WC
    WC --&amp;gt; SD
    SD --&amp;gt; Certs
    SD --&amp;gt; SI
    SI --&amp;gt; Sa
    Sa --&amp;gt; SIDC
    SIDC --&amp;gt; SPI
    SIDC --&amp;gt; MD
    SIDC --&amp;gt; PH
    SI --&amp;gt; ED
    SI --&amp;gt; Ua
    Ua --&amp;gt; TST
&lt;h3&gt;Try it yourself&lt;/h3&gt;
&lt;p&gt;{`&lt;/p&gt;
Decode the four nesting levels of an Authenticode signature.
Requires: pip install pefile asn1crypto
&lt;p&gt;const catalogSignedInboxFile = {
  Path: &quot;C:\\Windows\\System32\\ntoskrnl.exe&quot;,
  // PowerShell: (Get-AuthenticodeSignature ntoskrnl.exe).SignatureType -&amp;gt; Catalog
  SignatureType: &quot;Catalog&quot;,
  Status: &quot;Valid&quot;,
  CatalogFile: &quot;C:\\Windows\\System32\\CatRoot\\{F750E6C3-...}\\Microsoft-Windows-Client-Drivers-Package&lt;del&gt;31bf3856ad364e35&lt;/del&gt;amd64~~10.0.x.y.cat&quot;,
  SignerCertificate: { Subject: &quot;CN=Microsoft Windows Production PCA 2011, ...&quot; }
};&lt;/p&gt;
&lt;p&gt;console.log(&quot;Embedded signature:&quot;, embeddedSignedBinary.SignatureType, embeddedSignedBinary.CatalogFile);
console.log(&quot;Catalog signature: &quot;, catalogSignedInboxFile.SignatureType, catalogSignedInboxFile.CatalogFile);
`}&lt;/p&gt;
&lt;p&gt;Once you can sign a hash instead of a file, and once you can pin a signing event to a moment in time that outlives the certificate, the rest of the architecture stops being a sequence of crypto choices and starts being a sequence of &lt;em&gt;policy&lt;/em&gt; choices: which roots do we trust for ring 0, which file-publisher tuples does this enterprise authorise, which drivers does Microsoft deny by hash? To see those policy choices in operation, watch a single &lt;code&gt;WinVerifyTrust&lt;/code&gt; call end to end.&lt;/p&gt;
&lt;h2&gt;6. A modern WinVerifyTrust call, end to end&lt;/h2&gt;
&lt;p&gt;A user double-clicks a Microsoft-signed &lt;code&gt;.exe&lt;/code&gt; on Windows 11 24H2. HVCI is on, Smart App Control is on, an enterprise App Control policy is loaded. The shell calls &lt;code&gt;ShellExecute&lt;/code&gt;. Before the OS hands control to the new process, the kernel&apos;s code-integrity stack (&lt;code&gt;ci.dll&lt;/code&gt;) and user-mode &lt;code&gt;WinVerifyTrust&lt;/code&gt; between them answer the question &lt;em&gt;&quot;is this binary trusted?&quot;&lt;/em&gt; in roughly the following seven stages.&lt;/p&gt;
&lt;h3&gt;Stage 1: read the certificate table&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ci.dll&lt;/code&gt; reads the optional header, finds &lt;code&gt;DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]&lt;/code&gt;, walks the certificate-table region, and enumerates the &lt;code&gt;WIN_CERTIFICATE&lt;/code&gt; records. Multiple records may be present (e.g. a SHA-1 record for compatibility with Windows 7 verifiers, and a SHA-256 record for modern ones); the verifier picks the strongest record whose algorithm is allowed by current policy [@authenticode-pe-docx][@mslearn-pe-format].&lt;/p&gt;
&lt;h3&gt;Stage 2: decode the SignedData&lt;/h3&gt;
&lt;p&gt;For each candidate record with &lt;code&gt;wCertificateType == WIN_CERT_TYPE_PKCS_SIGNED_DATA&lt;/code&gt;, the verifier DER-decodes &lt;code&gt;bCertificate&lt;/code&gt; into a CMS &lt;code&gt;ContentInfo&lt;/code&gt;, then into a &lt;code&gt;SignedData&lt;/code&gt; structure [@rfc-5652]. The verifier reads &lt;code&gt;signerInfos&lt;/code&gt;, picks the signer (usually one), and extracts the signed and unsigned attributes.&lt;/p&gt;
&lt;h3&gt;Stage 3: verify the content type&lt;/h3&gt;
&lt;p&gt;The verifier confirms &lt;code&gt;encapContentInfo.eContentType == 1.3.6.1.4.1.311.2.1.4&lt;/code&gt; (&lt;code&gt;SpcIndirectDataContent&lt;/code&gt;), then decodes the inner structure and confirms &lt;code&gt;data.type == 1.3.6.1.4.1.311.2.1.15&lt;/code&gt; (&lt;code&gt;SPC_PE_IMAGE_DATAOBJ&lt;/code&gt;) [@authenticode-pe-docx]. The inner &lt;code&gt;messageDigest&lt;/code&gt; is the Authenticode hash this signature claims to cover; the &lt;code&gt;digestAlgorithm&lt;/code&gt; says how it was computed.&lt;/p&gt;
&lt;h3&gt;Stage 4: recompute the Authenticode hash&lt;/h3&gt;
&lt;p&gt;The verifier re-reads the PE file bytes, applies the four exclusions (&lt;code&gt;CheckSum&lt;/code&gt;, the SECURITY data-directory entry, the certificate-table bytes, and section-padding), hashes the remaining bytes with the claimed algorithm, and compares to &lt;code&gt;SpcIndirectDataContent.messageDigest&lt;/code&gt; [@authenticode-pe-docx]. If they differ, the signature is rejected.&lt;/p&gt;
&lt;h3&gt;Stage 5: validate page hashes under HVCI&lt;/h3&gt;
&lt;p&gt;If &lt;code&gt;SpcPeImagePageHashes2&lt;/code&gt; is attached and the running policy includes HVCI, the page-hash table is preserved across the verification call and consulted later by the secure kernel at demand-fault time [@authenticode-pe-docx]. The full-file Authenticode hash check is &lt;em&gt;necessary&lt;/em&gt; but not &lt;em&gt;sufficient&lt;/em&gt; for runtime integrity; pages on disk can be tampered after load by a kernel-level attacker who bypasses file-system protections. Page hashes are what closes that gap by re-checking each page at the moment it is mapped executable.&lt;/p&gt;
&lt;h3&gt;Stage 6: build the chain&lt;/h3&gt;
&lt;p&gt;The verifier collects the &lt;code&gt;certificates&lt;/code&gt; SET from the &lt;code&gt;SignedData&lt;/code&gt;, plus any AIA-fetched certificates needed to complete the chain, and tries to terminate the path at a trusted root. For kernel-mode loads, the legacy anchor is the &lt;code&gt;Microsoft Code Verification Root&lt;/code&gt;; for portal-signed drivers, the chain may instead terminate at one of the Microsoft Root Authority anchors. The KMCS policy page describes the Windows 10 1607+ kernel-mode anchors verbatim: &lt;em&gt;&quot;Microsoft Root Authority 2010, Microsoft Root Certificate Authority, Microsoft Root Authority&quot;&lt;/em&gt; with Secure Boot on [@mslearn-kmcs-policy]. For user-mode loads, the chain may terminate at any root in the system Trusted Root store; the enterprise&apos;s App Control policy narrows the trust further by referencing specific anchors at the RootCertificate / PcaCertificate rule level [@mslearn-select-types-of-rules].&lt;/p&gt;

The CryptoAPI function (`wintrust.dll!WinVerifyTrust`) that orchestrates the Authenticode verification pipeline: certificate-table read, SignedData decode, content-type check, Authenticode-hash recomputation, optional page-hash association, chain build, catalog fallback for unsigned PEs, and timestamp validation. It returns a success or specific error code; the caller (UAC, SmartScreen, `ci.dll`, WDAC) interprets the result against its own policy.

The Windows kernel-mode component that enforces the Kernel-Mode Code Signing policy on driver loads (Vista x64 and later [@wiki-kernel-patch-protection][@mslearn-kmcs-policy]) and, under HVCI, evaluates page hashes at fault time. `ci.dll` is the kernel-side caller of `WinVerifyTrust` semantics for driver loads.

The historical kernel-mode trust anchor whose name appears in Microsoft&apos;s KMCS documentation and whose intermediate cross-signed third-party code-signing CAs for pre-July-2015 drivers [@mslearn-kmcs-policy]. Microsoft Learn does not publish a single canonical page with the root&apos;s SHA-1 / SHA-256 thumbprint, validity dates, or issuance year; in practice the thumbprint is read by running `certutil -store` on a recent Windows system.
&lt;p&gt;The Microsoft Code Verification Root metadata absence is real: although the root is named in the KMCS policy document [@mslearn-kmcs-policy], no Microsoft Learn URL publishes its thumbprint or validity dates on a stable page. Practitioners should reference the root by name in policy and treat the actual thumbprint as something to be enumerated via &lt;code&gt;certutil -store&lt;/code&gt; on the running system rather than copy-pasted from a published document.&lt;/p&gt;
&lt;h3&gt;Stage 7: catalog fallback for unsigned PEs&lt;/h3&gt;
&lt;p&gt;If the PE has no embedded signature, the verifier computes the Authenticode hash and queries &lt;code&gt;CryptSvc&lt;/code&gt;: is this hash a member of any installed catalog under &lt;code&gt;%SystemRoot%\System32\CatRoot\&lt;/code&gt;? If yes, the verifier uses the catalog&apos;s signer as the effective signer for the PE [@mslearn-catalog-files][@mslearn-authenticode-driver]. Cross-system files installed by Windows Update (most drivers, most inbox executables) take this path.&lt;/p&gt;
&lt;h3&gt;Stage 8: validate the RFC 3161 timestamp&lt;/h3&gt;
&lt;p&gt;If the unsigned attributes carry a &lt;code&gt;TimeStampToken&lt;/code&gt; (OID &lt;code&gt;1.2.840.113549.1.9.16.2.14&lt;/code&gt;), the verifier decodes it, validates the TSA&apos;s chain, extracts &lt;code&gt;genTime&lt;/code&gt;, and confirms the signing certificate was valid at &lt;code&gt;genTime&lt;/code&gt; [@rfc-3161]. This is how a 2010 signature still verifies in 2026: not because the 2010 certificate is still valid, but because a TSA attested at signing time that the signature existed when the certificate was valid.&lt;/p&gt;
&lt;h3&gt;Stage 9: WDAC policy evaluation&lt;/h3&gt;
&lt;p&gt;With cryptographic verdicts in hand, the App Control policy engine evaluates the file against the active policy: does any allow rule match, does any deny rule match, including the default-on Vulnerable Driver Blocklist supplemental deny [@mslearn-recommended-driver-block-rules]? The matching rule -- by Hash, FileName, Publisher, FilePublisher, WHQL, WHQLPublisher, WHQLFilePublisher, LeafCertificate, PcaCertificate, or RootCertificate level [@mslearn-select-types-of-rules] -- decides the final outcome. Audit-mode hits produce event ID 3076; enforcement-mode blocks produce event ID 3077 [@mslearn-event-id-explanations].&lt;/p&gt;
&lt;h3&gt;Stage 10: legacy parser hardening, if opted in&lt;/h3&gt;
&lt;p&gt;A hardened environment will also have &lt;code&gt;EnableCertPaddingCheck=1&lt;/code&gt; set [@nvd-cve-2013-3900], enabling the strict parser that rejects the CVE-2013-3900 appended-data form. CISA added the CVE to its Known Exploited Vulnerabilities catalogue on 10 January 2022 with a federal due date of 10 July 2022 [@nvd-cve-2013-3900]; environments subject to federal compliance regimes treat this as mandatory.For practitioners: the registry key needs to be set in both &lt;code&gt;HKLM\Software\Microsoft\Cryptography\Wintrust\Config&lt;/code&gt; and the matching &lt;code&gt;Wow6432Node&lt;/code&gt; path, because the 32-bit and 64-bit &lt;code&gt;WinVerifyTrust&lt;/code&gt; code paths read separate copies. Setting only one and rebooting is one of the more common configuration mistakes in hardened-baseline rollouts.&lt;/p&gt;

flowchart TD
    Start[&quot;ShellExecute / driver load&quot;]
    CT[&quot;Read PE certificate table&quot;]
    Decode[&quot;Decode WIN_CERTIFICATE -&amp;gt; CMS SignedData&quot;]
    OID{&quot;eContentType =&lt;br /&gt;SpcIndirectDataContent?&quot;}
    Hash[&quot;Recompute Authenticode hash&quot;]
    HashOK{&quot;Hash matches&lt;br /&gt;messageDigest?&quot;}
    Chain[&quot;Build certificate chain&quot;]
    Cat[&quot;Catalog fallback?&lt;br /&gt;(if PE unsigned)&quot;]
    TS[&quot;Validate RFC 3161 token&quot;]
    PHash[&quot;Associate SpcPeImagePageHashes2&lt;br /&gt;(HVCI fault-time check)&quot;]
    Pol[&quot;WDAC policy evaluation&quot;]
    EPC[&quot;EnableCertPaddingCheck&lt;br /&gt;strict parser (if opt-in)&quot;]
    Done[&quot;LOAD or DENY&quot;]
    Start --&amp;gt; CT --&amp;gt; Decode --&amp;gt; OID
    OID --&amp;gt;|yes| Hash
    OID --&amp;gt;|no| Done
    Hash --&amp;gt; HashOK
    HashOK --&amp;gt;|yes| Chain
    HashOK --&amp;gt;|no| Done
    Chain --&amp;gt; Cat --&amp;gt; TS --&amp;gt; PHash --&amp;gt; EPC --&amp;gt; Pol --&amp;gt; Done
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; There is no separate certificate table per trust subsystem. UAC, SmartScreen, &lt;code&gt;ci.dll&lt;/code&gt;, WDAC, and the catalog-fallback path all read the &lt;em&gt;same&lt;/em&gt; bytes inside the same &lt;code&gt;WIN_CERTIFICATE&lt;/code&gt; record. What differs is which fields each consumer cares about and what policy each consumer overlays on top. Once you read the on-disk structures, every later trust decision is predictable.&lt;/p&gt;
&lt;/blockquote&gt;

`WinVerifyTrust` does not execute the binary. It does not appraise behaviour or reputation -- that is SmartScreen&apos;s job, downstream. It does not verify runtime page integrity -- HVCI does, in the secure kernel, at demand-fault time. It does not enforce the App Control policy -- the policy engine does, downstream. It does not check OCSP unless the caller opts in; chain-revocation behaviour is governed by `WinVerifyTrust` flags supplied by the caller. The function answers only the narrow cryptographic question: does the SignedData blob parse, does the recomputed hash match, does the chain build, and (if a token is attached) did the signing event happen inside the signing certificate&apos;s validity window?
&lt;p&gt;By the seventh stage of this pipeline, the answer to &quot;is this binary trusted?&quot; is no longer a yes-or-no statement about cryptography. It is a &lt;em&gt;composite&lt;/em&gt; of cryptographic verdicts (signature integrity, hash match, chain build, timestamp validity, page hashes) and &lt;em&gt;policy&lt;/em&gt; verdicts (allowed by WDAC, not on the blocklist). Authenticode supplies the inputs to a policy; WDAC writes the policy. Let us look at the policy language.&lt;/p&gt;
&lt;h2&gt;7. WDAC rule levels: Authenticode as policy input, not policy itself&lt;/h2&gt;
&lt;p&gt;App Control for Business (WDAC) is where the Authenticode primitives finally surface to administrators as policy. The &lt;code&gt;SignerInfo&lt;/code&gt;, the subject CN of the leaf certificate, the file&apos;s &lt;code&gt;OriginalFileName&lt;/code&gt; and &lt;code&gt;ProductVersion&lt;/code&gt; from the version resource, the page-hash table, even the choice of catalog signer -- all of them become inputs to a small rule language.&lt;/p&gt;
&lt;h3&gt;Rule levels: what Authenticode field each level consults&lt;/h3&gt;
&lt;p&gt;The verbatim rule-level catalogue from Microsoft Learn is [@mslearn-select-types-of-rules]:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Rule level&lt;/th&gt;
&lt;th&gt;Authenticode field(s) consulted&lt;/th&gt;
&lt;th&gt;Example use case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Hash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Authenticode hash of the file&lt;/td&gt;
&lt;td&gt;Pinning a single binary by exact bytes; brittle across patches.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FileName&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;OriginalFileName&lt;/code&gt; from the PE version resource&lt;/td&gt;
&lt;td&gt;Convenience for inbox files; not cryptographic.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FilePath&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Filesystem path&lt;/td&gt;
&lt;td&gt;UNC or absolute path; not cryptographic. Use sparingly.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SignedVersion&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Publisher + &lt;code&gt;OriginalFileName&lt;/code&gt; + version range&lt;/td&gt;
&lt;td&gt;Allow a publisher&apos;s binary at a given version or higher.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Publisher&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Issuing CA + leaf-cert subject CN&lt;/td&gt;
&lt;td&gt;Allow anything signed by a given vendor under a given CA.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;FilePublisher&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Publisher + &lt;code&gt;OriginalFileName&lt;/code&gt; + minimum &lt;code&gt;FileVersion&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allow a specific binary from a specific vendor at min version.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WHQL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The Windows Hardware Quality Labs EKU&lt;/td&gt;
&lt;td&gt;Allow any WHQL-signed driver.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WHQLPublisher&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;WHQL EKU + leaf-cert subject CN&lt;/td&gt;
&lt;td&gt;Allow WHQL drivers from a specific OEM.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WHQLFilePublisher&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;WHQL EKU + &lt;code&gt;OriginalFileName&lt;/code&gt; + min &lt;code&gt;FileVersion&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The strictest driver rule.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;LeafCertificate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Leaf cert subject + issuer&lt;/td&gt;
&lt;td&gt;Pin to a specific signing cert.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PcaCertificate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The PCA (intermediate) cert&lt;/td&gt;
&lt;td&gt;Useful for &quot;anything Microsoft-signed&quot; without enumerating leaves.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RootCertificate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The root anchor&lt;/td&gt;
&lt;td&gt;Broadest; usually too coarse.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;Policy options&lt;/h3&gt;
&lt;p&gt;App Control policies are XML documents with a &lt;code&gt;&amp;lt;Rules&amp;gt;&lt;/code&gt; section that toggles broad behavioural options [@mslearn-select-types-of-rules]:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;0 Enabled:UMCI&lt;/code&gt;&lt;/strong&gt; -- &lt;em&gt;&quot;App Control policies restrict both kernel-mode and user-mode binaries. By default, only kernel-mode binaries are restricted. Enabling this rule option validates user mode executables and scripts&quot;&lt;/em&gt; [@mslearn-select-types-of-rules].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;2 Required:WHQL&lt;/code&gt;&lt;/strong&gt; -- &lt;em&gt;&quot;By default, kernel drivers that aren&apos;t Windows Hardware Quality Labs (WHQL) signed are allowed to run. Enabling this rule requires that every driver is WHQL signed and removes legacy driver support&quot;&lt;/em&gt; [@mslearn-select-types-of-rules].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;8 Required:EV Signers&lt;/code&gt;&lt;/strong&gt; -- documented but, per the same Microsoft Learn page, &lt;em&gt;&quot;This option isn&apos;t currently supported.&quot;&lt;/em&gt;The Required:EV Signers option is in every published rule-options table but never makes it past parsing today. The EV requirement is enforced contractually via the Hardware Developer Center submission gate, not via the rule option. Treat it as documentation of intent rather than runtime enforcement.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Vulnerable Driver Blocklist is shipped as a &lt;em&gt;supplemental&lt;/em&gt; deny policy that overlays the user&apos;s primary policy. From Windows 11 22H2 onward it is default-on and automatically enforced under HVCI, Smart App Control, or S Mode [@mslearn-recommended-driver-block-rules]. Updates arrive quarterly. The blocklist is deliberately conservative: Microsoft&apos;s own documentation acknowledges &lt;em&gt;&quot;It&apos;s often necessary for us to hold back some blocks to avoid breaking existing functionality while we work with our partners who are engaging their users to update to patched versions&quot;&lt;/em&gt; [@mslearn-recommended-driver-block-rules].&lt;/p&gt;

The post-2024 rename of Windows Defender Application Control [@mslearn-appcontrol-root]; a code-integrity policy language that consumes Authenticode primitives (chain, leaf-cert subject, `OriginalFileName`, version, WHQL EKU, page-hash table, embedded-vs-catalog provenance) as inputs to administrator-authored allow and deny rules.

A WDAC rule level that allows or denies a binary if it is signed by a given Publisher (issuing CA + leaf-cert subject CN) **and** the PE&apos;s `OriginalFileName` matches **and** the PE&apos;s `FileVersion` is at or above a minimum. The tightest commonly used rule level; brittle across self-updating applications whose binaries change without warning [@mslearn-select-types-of-rules][@mslearn-use-code-signing].
&lt;h3&gt;A worked example&lt;/h3&gt;
&lt;p&gt;Generating a FilePublisher rule for a Microsoft-signed binary on PowerShell:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;New-CIPolicyRule -FilePath &quot;C:\Path\To\App.exe&quot; -Level FilePublisher
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;produces a &lt;code&gt;&amp;lt;FileRule&amp;gt;&lt;/code&gt; whose XML carries the issuing CA, the leaf-cert subject CN, the &lt;code&gt;OriginalFileName&lt;/code&gt; from the version resource, and a &lt;code&gt;MinimumFileVersion&lt;/code&gt; attribute. Every one of those fields is a direct read of the Authenticode &lt;code&gt;SignerInfo&lt;/code&gt; and the PE version resource; nothing in the rule generation step talks to Microsoft. The administrator owns the rule.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Microsoft&apos;s own guidance is verbatim: &lt;em&gt;&quot;Be aware of self-updating apps, as their app binaries may change without your knowledge&quot;&lt;/em&gt; [@mslearn-use-code-signing]. FilePublisher rules pin a minimum version; if a self-updating app rolls out a build with a different &lt;code&gt;OriginalFileName&lt;/code&gt; casing, or with &lt;code&gt;ProductVersion&lt;/code&gt; changes that some packagers reuse as &lt;code&gt;FileVersion&lt;/code&gt;, the rule silently stops matching. For self-updating apps, prefer &lt;code&gt;Publisher&lt;/code&gt; (CA + subject CN only) and accept the looser blast radius.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Operational tip: audit-mode hits write event ID 3076 to the &lt;em&gt;Microsoft-Windows-CodeIntegrity/Operational&lt;/em&gt; channel, enforcement-mode blocks write event ID 3077 [@mslearn-event-id-explanations]. Stage every policy in audit mode for at least one full patch cycle before flipping to enforcement; the 3076 stream is your inventory of what the rules would have denied.&lt;/p&gt;
&lt;p&gt;WDAC&apos;s vocabulary makes one structural choice explicit that the article has been implicit about until now: trust is &lt;em&gt;administrator-authored&lt;/em&gt;, not &lt;em&gt;vendor-authored&lt;/em&gt;. The cryptographic identity is supplied by the same Authenticode primitives we just dissected; the policy is whatever the organisation writes. Before we look at the limits of what this stack can prove, one quick detour into how other operating systems have approached the same problem.&lt;/p&gt;
&lt;h2&gt;8. Catalog-vs-embedded across operating systems&lt;/h2&gt;
&lt;p&gt;Windows is unusual in two specific ways: it stores the catalog on the endpoint, and it refreshes the catalog through the OS update channel. No other mainstream OS does both.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;System&lt;/th&gt;
&lt;th&gt;Signature carrier&lt;/th&gt;
&lt;th&gt;Catalog model?&lt;/th&gt;
&lt;th&gt;Transparency log?&lt;/th&gt;
&lt;th&gt;Counter-signing for longevity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Windows (Authenticode)&lt;/td&gt;
&lt;td&gt;PKCS#7 / CMS SignedData inside &lt;code&gt;WIN_CERTIFICATE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes -- &lt;code&gt;.cat&lt;/code&gt; files in &lt;code&gt;CatRoot&lt;/code&gt;, refreshed by Windows Update [@mslearn-catalog-files]&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes -- RFC 3161 token as unsigned attribute [@rfc-3161]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;td&gt;Apple-issued code signature + Notarization ticket; ticket stapled to artefact or fetched online [@apple-notarization]&lt;/td&gt;
&lt;td&gt;No -- Notarization ticket attests, but there is no on-disk &quot;list of hashes&quot; structure&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Stapled ticket effectively gives a signing-time guarantee; no third-party TSA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux IMA / EVM&lt;/td&gt;
&lt;td&gt;Extended-attribute signatures on individual files [@linux-ima-wiki]&lt;/td&gt;
&lt;td&gt;No -- per-file &lt;code&gt;security.ima&lt;/code&gt; xattr&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Out of scope; appraised against locally trusted keyring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Android&lt;/td&gt;
&lt;td&gt;APK Signature Scheme v3 (block inside the APK) [@android-apk-v3]&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No (signatures live inside the APK)&lt;/td&gt;
&lt;td&gt;Proof-of-rotation chain inside the v3 block lets a publisher rotate keys without re-signing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sigstore (OCI artefacts)&lt;/td&gt;
&lt;td&gt;Detached signature in OCI registry; short-lived Fulcio cert [@sigstore-overview]&lt;/td&gt;
&lt;td&gt;Closest analogue -- detached signature can cover blobs [@cosign-blobs]&lt;/td&gt;
&lt;td&gt;Yes -- Rekor [@rekor-github]&lt;/td&gt;
&lt;td&gt;TSA-style entries possible via Rekor&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The closest design analogue to the Windows catalog model is sigstore. Both decouple the signature from the artefact, and both let a single signing event cover many files. The difference is &lt;em&gt;where the detached signature lives&lt;/em&gt;. Windows puts the &lt;code&gt;.cat&lt;/code&gt; on the endpoint and refreshes the catalog through the OS update channel; sigstore stores the detached signature in an OCI registry and writes an attestation to a Rekor transparency log. That difference is also what gives Windows the offline-stale-catalog problem (a disconnected endpoint cannot freshness-check &lt;code&gt;CatRoot&lt;/code&gt;) and gives sigstore the offline-no-Rekor problem (a disconnected verifier cannot consult the log).Readers who want the broader cross-platform identity comparison should consult the earlier &lt;a href=&quot;https://paragmali.com/blog/windows-app-identity-33-year-reinvention/&quot; rel=&quot;noopener&quot;&gt;&lt;em&gt;App Identity in Windows&lt;/em&gt;&lt;/a&gt; article in this series, which compares Apple&apos;s package identity, Android&apos;s app IDs, and Linux&apos;s lack of a unified equivalent in more depth. The present article only summarises the &lt;em&gt;signature-carrier&lt;/em&gt; side of the comparison.&lt;/p&gt;
&lt;p&gt;Whether Microsoft puts the catalog on the endpoint or in an OCI registry is a deployment choice. The &lt;em&gt;limit&lt;/em&gt; of what any signature -- catalog, embedded, sigstore-anchored, Apple-notarised -- can prove is a deeper, more uncomfortable claim. We turn to that next.&lt;/p&gt;
&lt;h2&gt;9. What signatures cannot prove&lt;/h2&gt;
&lt;p&gt;Stuxnet did not break Authenticode. It walked through it. The same is true of Flame, of ShadowHammer, and of the Bitwarden CLI npm hijack. Every named incident on the modern Windows code-signing timeline is an instance of the same structural lower bound: signatures prove &lt;em&gt;who&lt;/em&gt;, not &lt;em&gt;what&lt;/em&gt;. The Windows code-identity stack has spent fourteen years adding layers that narrow the consequences of that bound. None of them eliminate it.&lt;/p&gt;
&lt;p&gt;Four limits are worth naming explicitly.&lt;/p&gt;
&lt;h3&gt;L1. Provenance is not safety&lt;/h3&gt;
&lt;p&gt;By Rice&apos;s theorem corollary, no decision procedure can determine arbitrary non-trivial semantic properties of a program. A signing system can therefore certify only &quot;this binary came from a key-holder,&quot; never &quot;this binary is benign.&quot; Stuxnet 2010 [@stuxnet-dossier], Flame 2012 [@stevens-counter-cryptanalysis][@ms-advisory-2718704], Operation ShadowHammer 2019 [@securelist-shadowhammer], and the Bitwarden CLI npm hijack of 22 April 2026 [@bitwarden-statement][@stepsecurity-bitwarden][@hackernews-bitwarden] are four independent instances of the same gap, across four entirely different attack surfaces (stolen kernel-driver key; forged sub-CA via MD5 collision; compromised ASUS Live Update certificate; compromised npm OIDC trusted-publishing). The empirical scale is large: Kim, Kwon, and Dumitraș measured millions of certificates and hundreds of thousands of signed-but-malicious PE samples in the Windows code-signing PKI in their CCS 2017 paper [@dumitras-ccs-2017].&lt;/p&gt;
&lt;p&gt;The mathematics of Rice&apos;s theorem is succinct. Let $P$ be any non-trivial semantic property of programs (e.g. &lt;em&gt;is malicious&lt;/em&gt;). For any algorithm $A$ that on input program $p$ outputs $A(p) \in {\text{yes}, \text{no}}$ claiming whether $p$ has property $P$, there exists a program $q$ where $A(q)$ is wrong. A signature scheme is not such an algorithm $A$ in the first place: it computes $\text{Sig}_{\text{sk}}(\text{hash}(p))$. The signature output has no semantic content about $p$&apos;s behaviour; it asserts only that the holder of $\text{sk}$ touched $\text{hash}(p)$.&lt;/p&gt;
&lt;h3&gt;L2. CA cardinality and the weakest-link property&lt;/h3&gt;
&lt;p&gt;The trust graph for kernel-mode loads is narrow: a small number of Microsoft roots [@mslearn-kmcs-policy]. The trust graph for user-mode loads is the union of every root in the system Trusted Root store -- a much larger set. &lt;em&gt;Any one&lt;/em&gt; root, if compromised, degrades the entire user-mode code-identity trust graph; &lt;em&gt;any one&lt;/em&gt; sub-CA, if forged, opens the kernel-mode path for the lifetime of the certificate. The Sotirov / Stevens / Appelbaum / Lenstra / Molnar / Osvik / de Weger rogue-CA work from December 2008 [@hashclash-rogue-ca] demonstrated this dynamic for the web PKI; the same family of attack was then mounted in Flame in 2012 against the Microsoft Enforced Licensing Intermediate PCA [@ms-advisory-2718704]. The CSBR&apos;s EV-on-hardware requirements [@cabf-cs-documents] reduce stolen-key risk at the leaf level, but a forged sub-CA bypasses the leaf entirely.&lt;/p&gt;
&lt;h3&gt;L3. Catalog-store freshness on disconnected endpoints&lt;/h3&gt;
&lt;p&gt;A disconnected endpoint cannot freshness-check its &lt;code&gt;CatRoot&lt;/code&gt;. The catalog database is whatever Windows Update last delivered -- which means freshly issued catalogs covering newly shipped inbox files cannot be trusted on machines that have been offline. The Vulnerable Driver Blocklist faces the same problem in reverse: a freshly blocked driver does not become &lt;em&gt;un&lt;/em&gt;-trusted on a disconnected endpoint until the supplemental policy lands. Microsoft acknowledges this in the VDB documentation: &lt;em&gt;&quot;It&apos;s often necessary for us to hold back some blocks to avoid breaking existing functionality&quot;&lt;/em&gt; [@mslearn-recommended-driver-block-rules]. The publication lag is deliberate, not accidental, and there is no in-band way for an endpoint to ask &quot;is my VDB current?&quot;&lt;/p&gt;
&lt;h3&gt;L4. TSA centralisation and antedating&lt;/h3&gt;
&lt;p&gt;RFC 3161 has no transparency log. A compromised TSA can issue countersignatures with arbitrary &lt;code&gt;genTime&lt;/code&gt; undetectably, until and unless the TSA&apos;s root is revoked. Sigstore Rekor [@rekor-github] is the canonical answer to this problem in the OSS world; nothing equivalent ships in the Authenticode stack. The consequence is asymmetric: a compromised TSA can antedate a signature backwards, making a freshly signed but recently malicious binary appear to have been signed before the malicious campaign began -- which on most verifiers means it will &lt;em&gt;still&lt;/em&gt; verify even after the actual signing certificate is revoked.&lt;/p&gt;

flowchart TD
    L1[&quot;L1: Provenance != safety&lt;br /&gt;(Rice&apos;s theorem corollary)&quot;]
    L2[&quot;L2: CA cardinality&lt;br /&gt;(weakest-link property)&quot;]
    L3[&quot;L3: CatRoot freshness&lt;br /&gt;(offline endpoints stale)&quot;]
    L4[&quot;L4: TSA centralisation&lt;br /&gt;(no transparency log)&quot;]
    Floor[&quot;What is actually being proved:&lt;br /&gt;a key-holder touched hash(p) at genTime&quot;]
    L1 --&amp;gt; Floor
    L2 --&amp;gt; Floor
    L3 --&amp;gt; Floor
    L4 --&amp;gt; Floor

A valid signature proves only who signed the binary, never what the binary does.
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Authenticode is the floor of Windows trust, not the ceiling. Every later layer -- Kernel-Mode Code Signing, App Control for Business, the Vulnerable Driver Blocklist, HVCI page-hash enforcement -- exists because the floor cannot, by construction, do more.&lt;/p&gt;
&lt;/blockquote&gt;

Stuxnet 2010, Flame 2012, ShadowHammer 2019, and Bitwarden CLI 2026 are four instances of the same lower bound, fourteen years apart, across four entirely different surfaces: a stolen private key for a kernel driver; a forged Microsoft sub-CA via cryptographic collision; a compromised ASUSTeK signing certificate used to sign a malicious updater; a compromised npm OIDC trusted-publishing pipeline used to publish a malicious CLI release. In each case the signature was valid. In each case the binary was malicious. The layers we add -- cross-signing deprecation, EV-on-hardware, the VDB, WDAC -- do not close the gap. They reduce the blast radius of the inevitable next incident.
&lt;p&gt;Once you see provenance and safety as separate questions, every open problem in the code-signing stack lines up in one direction: how do you reduce the blast radius of the inevitable next valid-but-malicious signature?&lt;/p&gt;
&lt;h2&gt;10. Open problems&lt;/h2&gt;
&lt;p&gt;Five problems are concrete enough to call out as ongoing work.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;O1. Post-quantum Authenticode.&lt;/strong&gt; Microsoft has not yet published a &lt;code&gt;SpcIndirectDataContent&lt;/code&gt; variant that references the &lt;a href=&quot;https://paragmali.com/blog/post-quantum-cryptography-on-windows-the-thirty-year-migrati/&quot; rel=&quot;noopener&quot;&gt;ML-DSA&lt;/a&gt; (FIPS 204 [@fips-204]) or SLH-DSA (FIPS 205 [@fips-205]) OIDs. The CA/B Forum CSBR has not named a post-quantum algorithm for code-signing certificates; the current CSBR v3.8 [@cabf-cs-documents] still rests on RSA and ECDSA. NIST&apos;s PQC programme has set a 2035 deadline to deprecate quantum-vulnerable algorithms [@nist-pqc]. The CMS extensibility precedents are there: RFC 8554 profiles stateful LMS [@rfc-8554], RFC 8419 profiles EdDSA in CMS [@rfc-8419], and there is no architectural reason the same approach cannot profile ML-DSA. A hybrid-signed binary that carries both an RSA and an ML-DSA &lt;code&gt;SignerInfo&lt;/code&gt; inside the same &lt;code&gt;SignedData&lt;/code&gt; is technically possible today, and Microsoft will likely have to ship it before catastrophic loss of confidence in RSA can happen.FIPS 204 (ML-DSA) and FIPS 205 (SLH-DSA) were both finalised on 13 August 2024 [@fips-204][@fips-205]. The standards are stable; what is missing is the Authenticode-side OID registration and the Hardware Developer Center portal-signing pipeline that would emit a PQ counter-signature. The CSBR side and the Microsoft side both have to move; neither has publicly committed to a date.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;O2. Per-page integrity for non-PE artefacts.&lt;/strong&gt; Page hashes inside &lt;code&gt;SpcPeImagePageHashes2&lt;/code&gt; [@authenticode-pe-docx] are PE-specific. PowerShell scripts, MSIX packages, Appx packages, and the &lt;code&gt;.cat&lt;/code&gt; files themselves rely on whole-file Authenticode hashing; if an attacker can corrupt a single byte after load, the OS does not currently re-hash. HVCI gives PE binaries a runtime check; the script and package side does not yet have an equivalent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;O3. Transparency logs for Authenticode countersignatures.&lt;/strong&gt; RFC 3161 TSAs do not publish their issued tokens. A backdated countersignature from a compromised TSA is currently undetectable beyond CA revocation. Sigstore Rekor [@rekor-github] demonstrates that a transparency log integrates with a signing pipeline at low overhead; there is no equivalent for the Microsoft-signed-driver world or for third-party Authenticode signers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;O4. Revocation propagation latency.&lt;/strong&gt; The gap between &quot;the CA revokes&quot; and &quot;every endpoint refuses to verify&quot; is empirically days to weeks. CRLs are downloaded on a cadence (with &lt;code&gt;EnableCertPaddingCheck&lt;/code&gt; aside, OCSP is not even applied to Authenticode by default). The VDB&apos;s quarterly cadence [@mslearn-recommended-driver-block-rules] is faster than CRL-only and slower than the rate at which attackers can stand up an attack with a freshly stolen certificate. Some of this is unavoidable -- you cannot push a revocation faster than an offline endpoint can reach Windows Update -- but a structurally better answer is one of the open questions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;O5. Post-CrowdStrike (July 2024) kernel-driver-loading discipline.&lt;/strong&gt; Microsoft&apos;s Windows Resiliency Initiative was announced in the wake of the 19 July 2024 CrowdStrike Falcon Sensor outage; a fully-specified replacement for today&apos;s third-party kernel-driver model has not yet shipped. A successful answer would push parts of today&apos;s Authenticode + KMCS + WDAC story toward sandboxed user-mode driver frameworks, with the kernel restricted to a much narrower interface. The Authenticode primitives this article has dissected will still be the substrate; what gets layered on top is the open architectural question.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This article is about the &lt;em&gt;crypto foundation&lt;/em&gt; under WDAC: the bytes on disk, the envelope structures, the chain of trust. It does not cover the runtime enforcement layer -- how Code Integrity, HVCI, and the secure kernel use these primitives at process- and driver-load time, how page hashes are checked at fault time, how the Vulnerable Driver Blocklist is loaded as a supplemental policy. That story is the subject of the next post in this series.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The next decade of Windows code-signing is going to be dominated by post-quantum migration and by whatever the Windows Resiliency Initiative converges to. Both will be evolution, not revolution: they will sit on top of the certificate-table, catalog-store, and timestamp-token primitives that have been load-bearing since 1996. To finish, the day-to-day commands that interrogate every byte we have discussed.&lt;/p&gt;
&lt;h2&gt;11. Practical guide: signtool, certutil, New-CIPolicyRule&lt;/h2&gt;
&lt;p&gt;If you have read this far, you should be able to run the following commands on a Windows host and explain every field of their output. Microsoft&apos;s &lt;code&gt;signtool&lt;/code&gt;, &lt;code&gt;certutil&lt;/code&gt;, and the &lt;code&gt;ConfigCI&lt;/code&gt; PowerShell module are the canonical tools [@mslearn-crypto-tools].&lt;/p&gt;
&lt;h3&gt;Verify a signed binary end to end&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;signtool verify /v /pa /all &quot;C:\Path\To\binary.exe&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output prints, in order: the SHA-256 of the file&apos;s Authenticode hash, the leaf certificate&apos;s subject and issuer, every intermediate up to the trusted root, the RFC 3161 timestamp&apos;s &lt;code&gt;genTime&lt;/code&gt;, and the policy used to validate. &lt;code&gt;/pa&lt;/code&gt; opts into the &quot;default authenticode&quot; policy (instead of the deprecated &lt;code&gt;MicrosoftRoot&lt;/code&gt; policy); &lt;code&gt;/all&lt;/code&gt; walks every signature on the file rather than just the strongest.&lt;/p&gt;
&lt;h3&gt;Compute and look up an Authenticode hash&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;certutil -hashfile &quot;C:\Path\To\driver.sys&quot; SHA256
certutil -CatDB &quot;C:\Windows\System32\CatRoot\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}&quot; /v /search &amp;lt;hash&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;-hashfile&lt;/code&gt; command emits the &lt;em&gt;file&lt;/em&gt; SHA-256, which is &lt;em&gt;not&lt;/em&gt; the Authenticode hash (the file SHA-256 includes the certificate-table bytes; the Authenticode hash excludes them). The Authenticode hash is what is stored inside each catalog&apos;s &lt;code&gt;CatalogList&lt;/code&gt;. &lt;code&gt;Get-AuthenticodeSignature&lt;/code&gt; is the easier PowerShell route to the Authenticode hash directly.&lt;/p&gt;
&lt;h3&gt;Walk the catalog store&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Get-ChildItem &quot;C:\Windows\System32\CatRoot&quot; -Recurse | Select-Object FullName
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The GUID-named subfolder is the CryptSvc policy database identifier; the &lt;code&gt;.cat&lt;/code&gt; files inside are individually-signed &lt;code&gt;SignedData&lt;/code&gt; blobs whose &lt;code&gt;encapContentInfo&lt;/code&gt; is a &lt;code&gt;CatalogList&lt;/code&gt; [@mslearn-catalog-files]. &lt;code&gt;CatRoot2&lt;/code&gt; holds staging copies and the catalog database index.&lt;/p&gt;
&lt;h3&gt;Generate a WDAC rule&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;New-CIPolicyRule -FilePath &quot;C:\Path\To\App.exe&quot; -Level FilePublisher
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This produces an XML &lt;code&gt;&amp;lt;FileRule&amp;gt;&lt;/code&gt; element with the issuer, subject CN, original file name, and minimum file version. Pipe the result into &lt;code&gt;New-CIPolicy&lt;/code&gt; to build a policy XML; convert to binary with &lt;code&gt;ConvertFrom-CIPolicy&lt;/code&gt; and deploy via Group Policy or Intune.&lt;/p&gt;
&lt;h3&gt;Decide between embedded and catalog signing&lt;/h3&gt;
&lt;p&gt;For an internal line-of-business app shipped as a single MSI, embedded signing is the default and the cleanest choice. For a multi-binary package where some files are third-party and unsignable, the Package Inspector workflow [@mslearn-deploy-catalog-files] builds a &lt;code&gt;.cat&lt;/code&gt; covering the post-installation file set without modifying any binary:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;PackageInspector.exe Start C:\
... install your app ...
PackageInspector.exe Stop C:\ -Name MyApp.cat -ResultsFile C:\Temp\MyApp_inspection.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Confirm a kernel-mode chain&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;signtool verify /v /pa /kp &quot;C:\Windows\System32\drivers\example.sys&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;/kp&lt;/code&gt; policy uses the kernel-mode driver policy: the chain must terminate at a kernel-mode-trusted root (the &lt;code&gt;Microsoft Code Verification Root&lt;/code&gt; family of anchors, or a portal-signed-driver Microsoft Root Authority anchor). &lt;code&gt;certutil -store -enterprise root&lt;/code&gt; enumerates the local kernel-mode roots; the legacy &lt;code&gt;Microsoft Code Verification Root&lt;/code&gt; is named on the KMCS policy page [@mslearn-kmcs-policy] but its thumbprint is not published on a stable Microsoft Learn URL -- you read it via &lt;code&gt;certutil -store&lt;/code&gt; on the running system.&lt;/p&gt;
&lt;h3&gt;Make an informed &lt;code&gt;EnableCertPaddingCheck&lt;/code&gt; decision&lt;/h3&gt;
&lt;p&gt;The strict-parser registry value lives in two places. Set both:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;reg add &quot;HKLM\Software\Microsoft\Cryptography\Wintrust\Config&quot; /v EnableCertPaddingCheck /t REG_DWORD /d 1 /f
reg add &quot;HKLM\Software\Wow6432Node\Microsoft\Cryptography\Wintrust\Config&quot; /v EnableCertPaddingCheck /t REG_DWORD /d 1 /f
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;CISA added CVE-2013-3900 to the Known Exploited Vulnerabilities catalogue on 10 January 2022 [@nvd-cve-2013-3900]; treat this as effectively mandatory in any hardened-baseline build.&lt;/p&gt;
&lt;h3&gt;Annotated &lt;code&gt;signtool verify&lt;/code&gt; output&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Verifying: notepad.exe
Hash of file (sha256): 6B9B7E...   &amp;lt;-- Authenticode hash, the same one
                                       inside SpcIndirectDataContent.messageDigest
Signing Certificate Chain:
  Issued to: Microsoft Root Certificate Authority 2010   &amp;lt;-- root anchor
    Issued by: Microsoft Root Certificate Authority 2010
  Issued to: Microsoft Windows Production PCA 2011        &amp;lt;-- intermediate / PCA
    Issued by: Microsoft Root Certificate Authority 2010
  Issued to: Microsoft Windows                             &amp;lt;-- leaf / signer
    Issued by: Microsoft Windows Production PCA 2011
The signature is timestamped: Thu Jul ...                 &amp;lt;-- RFC 3161 genTime
Timestamp Verified by:
  Issued to: Microsoft Time-Stamp PCA 2010                &amp;lt;-- TSA chain
  Issued to: Microsoft Time-Stamp Service
File is signed and the signature was verified.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;{`
// Cross-platform pedagogy: this snippet shows the flow of a catalog lookup.
// On Windows, &quot;certutil -CatDB  /v /search &quot; returns the
// covering catalog file. Off Windows, we mock the output so the flow is visible.&lt;/p&gt;
&lt;p&gt;interface CatalogLookupResult {
  hash: string;
  catalogFile: string | null;
  signerSubject: string | null;
}&lt;/p&gt;
&lt;p&gt;function lookupCatalog(authenticodeHash: string): CatalogLookupResult {
  // Real implementation would shell out to:
  //   certutil -CatDB  /v /search 
  // Parse the output for &quot;Hash:   Catalog: &quot;.
  const known: Record&amp;lt;string, CatalogLookupResult&amp;gt; = {
    &quot;6B9B7E...&quot;: {
      hash: &quot;6B9B7E...&quot;,
      catalogFile: &quot;C:\\Windows\\System32\\CatRoot\\{F750E6C3-...}\\Package_for_KB12345.cat&quot;,
      signerSubject: &quot;CN=Microsoft Windows Production PCA 2011&quot;
    }
  };
  return known[authenticodeHash] || { hash: authenticodeHash, catalogFile: null, signerSubject: null };
}&lt;/p&gt;
&lt;p&gt;const r = lookupCatalog(&quot;6B9B7E...&quot;);
console.log(r.catalogFile ? &quot;Catalog-signed by &quot; + r.signerSubject : &quot;Not catalog-covered&quot;);
`}&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The most common practitioner mistake is &lt;code&gt;signtool sign /n &amp;lt;name&amp;gt;&lt;/code&gt; without &lt;code&gt;/tr &amp;lt;tsa-url&amp;gt; /td sha256&lt;/code&gt;. A signature produced this way silently loses validity the moment the end-entity certificate expires -- which can be years later, when the signer has long since lost access to whatever signing key produced it. The fix is to always include &lt;code&gt;/tr&lt;/code&gt; and a strong &lt;code&gt;/td&lt;/code&gt;. RFC 3161 [@rfc-3161] is the entire reason long-lived signatures still verify; opting out of it is opting out of the longevity guarantee.&lt;/p&gt;
&lt;/blockquote&gt;

SmartScreen Application Reputation is not gated on Authenticode validity. It is gated on certificate *class* (EV vs. OV) and on aggregate *download volume* and reporting. An internally signed enterprise LOB app has neither: it is signed with an OV certificate, and its download volume is at most a few hundred enterprise users. The fix has two paths. The cheap one is to ride your enterprise WDAC policy rather than fight SmartScreen -- App Control rules allow the binary unconditionally inside your organisation. The expensive one is to buy an EV certificate, push the binary through a small early-access user pool, and let SmartScreen accumulate the reputation signal. Both work. Fighting SmartScreen with a louder OV signature does not.
&lt;p&gt;These seven commands cover the full surface of what Authenticode, catalog signing, and WDAC let a Windows engineer actually inspect. Everything else in this article is context for what those command outputs &lt;em&gt;mean&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;12. Frequently asked questions&lt;/h2&gt;

Authenticode is a specific PKCS#7 / CMS profile for signing Windows portable executables, catalog files, and a small set of related artefacts. It is defined by Microsoft&apos;s `Authenticode_PE.docx` specification [@authenticode-pe-docx] and is characterised by a PE-specific Authenticode hash (with four exclusions), the `SpcIndirectDataContent` content type at OID `1.3.6.1.4.1.311.2.1.4`, and the `WIN_CERTIFICATE` certificate-table wrapper. Other code-signing schemes -- JAR signing for Java, APK Signature Scheme v3 for Android [@android-apk-v3], sigstore/cosign for OCI artefacts [@sigstore-overview], Apple Notarization for macOS [@apple-notarization] -- are not Authenticode-compatible. They solve similar problems with different envelopes.

Not if the signature was RFC 3161 timestamped at signing time. The `TimeStampToken` in the unsigned attributes pegs the signing event to a `genTime` from a Trusted Time-Stamping Authority [@rfc-3161]; later verifiers compare `genTime` to the signing certificate&apos;s validity window and honour the signature so long as `genTime` was inside that window. The signature *will* stop working on hash-only WDAC rules (which do not consult certificate expiry at all) and on the rare verifiers that enforce chain time at validation. Signing without `/tr` is the way to produce a signature that silently loses validity at end-entity-cert expiry; that is the single most common Authenticode mistake at signing time.

Only by enabling Test Signing mode (which puts a watermark on the desktop and refuses to coexist with Secure Boot), or by booting with Driver Signature Enforcement disabled (which is a one-boot bypass), or by using a vulnerable signed driver to load your unsigned code (the entire point of the Vulnerable Driver Blocklist [@mslearn-recommended-driver-block-rules]). Production loading of an unsigned driver on a normally configured Windows 11 system is not supported. Cross-signing for new end-entity certs has been closed since the 29 July 2015 issuance cutoff [@mslearn-kmcs-policy]; cross-certificates expired by July 2021 [@mslearn-deprecation-spc-crc].

See the §11 Spoiler *&quot;Why your internally-signed LOB app trips SmartScreen&quot;* for the detailed explanation of why SmartScreen Application Reputation weights certificate class (EV vs. OV) and download volume rather than Authenticode validity, and for the two production fixes (ride your enterprise App Control policy, or buy an EV certificate and let reputation accumulate). The one-line summary: Authenticode and SmartScreen are different decision systems that happen to read the same `SignerInfo` -- making your signature *louder* in Authenticode does not buy you reputation in SmartScreen.

The `Microsoft Code Verification Root` is the historical kernel-mode trust anchor whose intermediate cross-signed third-party kernel code-signing CAs for pre-July-2015 drivers [@mslearn-kmcs-policy]. It is named in the KMCS policy document; its thumbprint is not published on a stable Microsoft Learn URL, so practitioners read it via `certutil -store` on the running system. The `Microsoft Code Signing PCA` family of intermediates (and its newer cousins like `Microsoft Windows Production PCA 2011`) are user-mode signing chains used for Microsoft-internal binaries and most WHQL catalogs. Both feed into `WinVerifyTrust`; they differ in which downstream consumer treats them as authoritative -- the kernel for the former, user-mode trust decisions for the latter.

No. The Authenticode hash excludes four PE regions: the optional-header `CheckSum` (4 bytes), the `IMAGE_DIRECTORY_ENTRY_SECURITY` data-directory entry (8 bytes), the certificate-table bytes themselves, and the file-alignment padding after each section [@authenticode-pe-docx]. So `(Get-AuthenticodeSignature notepad.exe).Hash` returns a different value than `certutil -hashfile notepad.exe SHA256`. The Authenticode hash is what is stored inside `SpcIndirectDataContent.messageDigest` and what is matched against catalog `memberHash` entries; the file SHA-256 is useful for forensic identification but does not appear anywhere in the signature flow.

They differ in precision and in which Authenticode fields they consult [@mslearn-select-types-of-rules]. `Publisher` allows anything signed by a given issuing CA + leaf-cert subject CN; broadest but loosest. `FilePublisher` adds `OriginalFileName` + `MinimumFileVersion` constraints; tightens to a specific binary at a min version. `WHQLFilePublisher` further requires the WHQL EKU; the strictest commonly used rule level. Self-updating apps invalidate `FilePublisher` rules silently when their `OriginalFileName` or `FileVersion` change without warning [@mslearn-use-code-signing]; most enterprises start at `Publisher` and tighten only for high-risk binaries.

No. NVD&apos;s verbatim Microsoft language: *&quot;Microsoft does not plan to enforce the stricter verification behavior as a default functionality on supported releases of Microsoft Windows. This behavior remains available as an opt-in feature via reg key setting, and is available on supported editions of Windows released since December 10, 2013&quot;* [@nvd-cve-2013-3900]. CISA added the CVE to the Known Exploited Vulnerabilities catalogue on 10 January 2022 with a federal due date of 10 July 2022. Hardened environments should set `EnableCertPaddingCheck=1` in both the native and `Wow6432Node` registry paths.
&lt;h2&gt;13. Closing reflection&lt;/h2&gt;
&lt;p&gt;In August 1996 the Authenticode trust decision was a single yes/no answer to a single question: did this PKCS#7 SignedData blob, attached to this downloadable ActiveX control, validate against a CA in the user&apos;s browser? Thirty years later, the trust decision is a chained question composing every primitive in this article: a &lt;code&gt;WIN_CERTIFICATE&lt;/code&gt; record points to a &lt;code&gt;SignedData&lt;/code&gt; envelope; the envelope&apos;s &lt;code&gt;SpcIndirectDataContent&lt;/code&gt; carries an Authenticode hash and optional page hashes; an unsigned attribute carries an RFC 3161 timestamp; the catalog store may carry a parallel signature for the same hash; the certificate chain terminates at one of a small set of Microsoft anchors for kernel-mode loads; an administrator&apos;s App Control policy decides whether the verdict survives the rule evaluation; the Vulnerable Driver Blocklist denies a small curated list outright.&lt;/p&gt;
&lt;p&gt;The cryptography has not moved. The certificate table is still where the bytes live. PKCS#7 SignedData is still the envelope. RSA is still the signature algorithm. What has changed -- and what is going to keep changing through the post-quantum migration and whatever the Windows Resiliency Initiative converges to -- is the layering of policy on top.&lt;/p&gt;
&lt;p&gt;Authenticode is not the ceiling. It is the floor. Everything else is built on top, and the next time a Realtek certificate is stolen, those layers are what decides whether the next Stuxnet still loads.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;authenticode-and-catalog-files-the-crypto-foundation-under-wdac&quot; keyTerms={[
  { term: &quot;Authenticode&quot;, definition: &quot;Microsoft&apos;s PKCS#7 / CMS profile for signing Windows PE binaries, defined by Authenticode_PE.docx.&quot; },
  { term: &quot;WIN_CERTIFICATE&quot;, definition: &quot;The PE certificate-table record (dwLength, wRevision, wCertificateType, bCertificate[]) wrapping the PKCS#7 SignedData blob.&quot; },
  { term: &quot;SpcIndirectDataContent&quot;, definition: &quot;Microsoft eContentType (OID 1.3.6.1.4.1.311.2.1.4) whose messageDigest is the Authenticode hash; signs a hash, not a file.&quot; },
  { term: &quot;Authenticode hash&quot;, definition: &quot;The PE digest computed with four regions excluded (CheckSum, SECURITY data-directory entry, certificate-table bytes, section-padding).&quot; },
  { term: &quot;Page hash (SpcPeImagePageHashes2)&quot;, definition: &quot;Signed attribute carrying per-4 KiB-page hashes for HVCI demand-fault-time verification.&quot; },
  { term: &quot;Catalog file (.cat)&quot;, definition: &quot;A degenerate SignedData whose encapsulated content is a CatalogList of (memberHash, attributes) tuples; detached signature.&quot; },
  { term: &quot;CatRoot / CryptSvc&quot;, definition: &quot;On-endpoint catalog store at %SystemRoot%\System32\CatRoot\{GUID}\ and the service that indexes member hashes.&quot; },
  { term: &quot;Trusted Time-Stamping Authority (TSA)&quot;, definition: &quot;RFC 3161 service that counter-signs a signature&apos;s hash with a trusted genTime, attached as an unsigned attribute.&quot; },
  { term: &quot;WinVerifyTrust&quot;, definition: &quot;CryptoAPI function orchestrating the Authenticode verification pipeline.&quot; },
  { term: &quot;Code Integrity / ci.dll&quot;, definition: &quot;Windows kernel-mode component enforcing KMCS on driver loads and feeding page hashes to HVCI.&quot; },
  { term: &quot;Microsoft Code Verification Root&quot;, definition: &quot;Historical kernel-mode trust anchor for cross-signed third-party drivers; thumbprint read via certutil -store.&quot; },
  { term: &quot;App Control for Business (WDAC)&quot;, definition: &quot;Post-2024 rename of Windows Defender Application Control; consumes Authenticode primitives as policy inputs.&quot; },
  { term: &quot;FilePublisher rule&quot;, definition: &quot;WDAC rule level allowing Publisher + OriginalFileName + MinimumFileVersion combinations.&quot; },
  { term: &quot;Vulnerable Driver Blocklist (VDB)&quot;, definition: &quot;Microsoft-curated supplemental deny policy enabled by default since Windows 11 22H2; quarterly cadence.&quot; },
  { term: &quot;RFC 3161 TimeStampToken&quot;, definition: &quot;CMS SignedData over hash(signature) || genTime, attached at OID 1.2.840.113549.1.9.16.2.14 as an unsigned attribute.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>authenticode</category><category>wdac</category><category>code-signing</category><category>pkcs7</category><category>windows-security</category><category>catalog-files</category><category>kmcs</category><category>rfc-3161</category><author>noreply@paragmali.com (Parag Mali)</author></item><item><title>&quot;Who Is This Code?&quot; -- The Quiet 33-Year Reinvention of App Identity in Windows</title><link>https://paragmali.com/blog/windows-app-identity-33-year-reinvention/</link><guid isPermaLink="true">https://paragmali.com/blog/windows-app-identity-33-year-reinvention/</guid><description>NT 3.1 could prove which user typed at the keyboard but had no answer to which code was running. Eight successive primitives later, Windows is still answering the same question.</description><pubDate>Fri, 08 May 2026 00:00:00 GMT</pubDate><content:encoded>
Windows NT 3.1 (1993) could prove which **user** typed at the keyboard but had no answer to **which code was running**. Over the next thirty-three years, eight successive primitives -- Authenticode, Kernel-Mode Code Signing, Protected Process Light, AppContainer with the Package SID, App Control for Business, Mark of the Web with SmartScreen, the Vulnerable Driver Block List, and Pluton-rooted attestation -- accreted into a single layered code-identity stack. Each was forced into existence by a specific, named failure of the one before it. This is that story, told as one system.
&lt;h2&gt;Two identities, one operating system&lt;/h2&gt;
&lt;p&gt;On July 27, 1993 -- the day Windows NT 3.1 shipped -- the new operating system could prove with cryptographic precision who Alice was, which group she belonged to, which file she was allowed to open, and at what level of privilege she was running. It could prove exactly nothing about the program she had just double-clicked.&lt;/p&gt;
&lt;p&gt;Thirty-three years later, &quot;Alice&quot; has barely changed. The code she runs has acquired a publisher signature stamped onto its Portable Executable file, a kernel-loader gate that refuses to load unsigned drivers, a signer level in a runtime lattice that decides whether one process can read another&apos;s memory, a Package SID derived from a Crockford-Base32 hash of the manifest publisher [@ms-package-identity], a publisher-rule entry in a centrally managed App Control policy [@ms-appcontrol], a Mark-of-the-Web alternate data stream from the browser that downloaded it [@ms-fscc-motw], a SmartScreen reputation score [@learn-smartscreen], a possible entry on a Microsoft-curated denylist that overrides its own valid signature [@msft-driver-blocklist], and -- on a Pluton-equipped 2026 laptop -- a hardware-attested measurement of the boot chain that loaded it [@learn-pluton]. Every one of those identities was forced into existence by a specific failure of the one before. This is that story.&lt;/p&gt;
&lt;p&gt;A modern symptom makes the asymmetry concrete. In April 2026, attackers seized the publishing pipeline for the &lt;code&gt;@bitwarden/cli&lt;/code&gt; npm package -- a credential they had no business holding -- and shipped a backdoored release for ninety-three minutes before maintainers caught it [@bitwarden-statement]. Code identity, as it existed at every layer of every operating system that consumed that package, said the artifact was authentic. The signature was valid. The publisher&apos;s account was real. The package metadata was correct. Every check passed. &lt;em&gt;And the binary was hostile.&lt;/em&gt; That gap, between &quot;who shipped it&quot; and &quot;is it safe to run,&quot; is the same gap NT 3.1 first stepped over in 1993 and that Windows has been trying to close ever since.&lt;/p&gt;
&lt;p&gt;The Bitwarden case sits in a long company. Stuxnet&apos;s stolen Realtek and JMicron driver-signing keys (2010) [@symantec-stuxnet], Flame&apos;s MD5 collision against Microsoft&apos;s own intermediate CAs (2012) [@ms-2718704], the ASUS ShadowHammer pipeline compromise (operation 2018, disclosed 2019) [@securelist-shadowhammer], every &quot;Bring Your Own Vulnerable Driver&quot; rootkit since 2018 -- they all have the same shape. A valid Windows-anchored signature, on code the publisher did not intend to ship, on a machine that loaded it without complaint.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Every Windows code-identity primitive introduced since 1996 was forced into existence by a specific failure of the layer before it. The article&apos;s spine is that cascade.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The pieces in 2026 are not a feature checklist. They are a layered system, each layer answering a question its predecessor structurally could not. If you read the Microsoft Learn pages one at a time you see eight unrelated products. If you read them in the order their failures forced them into existence, you see one operating system slowly learning to name the code it runs.&lt;/p&gt;

timeline
    title Windows code identity, 1993 to 2026
    1993 : NT 3.1 ships : user-only principal
    1996 : Authenticode : publisher signature on PE
    2002 : Trustworthy Computing memo : SDL forcing function
    2006 : Vista x64 KMCS : refusal of unsigned kernel code
    2010 : Stuxnet : stolen Realtek + JMicron keys
    2012 : AppContainer : per-app SID
    2012 : Flame : MD5 collision against MS CA
    2013 : Windows 8.1 PPL : signer level as runtime ACL
    2015 : Device Guard / WDAC : publisher policy
    2019 : ASUS ShadowHammer disclosed : compromised pipeline (2018 operation)
    2020 : Pluton announced : in-die security processor
    2022 : Driver Block List default-on : signed != trusted
    2024 : CrowdStrike outage : placement is identity
    2025 : MVI 3.0 user-mode preview : kernel/user split
&lt;p&gt;&lt;em&gt;Timeline sources, in row order (Mermaid syntax does not permit inline tokens inside the timeline block; each event is independently cited in the surrounding prose as well):&lt;/em&gt; 1993 NT 3.1 [@custer-inside-nt]; 1996 Authenticode [@ms-news-1996-authenticode]; 2002 Trustworthy Computing memo [@cnet-gates-memo] [@theregister-tcm]; 2006 Vista x64 KMCS [@ms-kmcs]; 2010 Stuxnet [@symantec-stuxnet]; 2012 AppContainer [@ms-package-identity]; 2012 Flame MD5 collision [@ms-2718704] [@msrc-2718704]; 2013 Windows 8.1 PPL [@ionescu-ppl] [@ms-protected-processes]; 2015 Device Guard / WDAC [@ms-appcontrol]; 2019 ASUS ShadowHammer disclosed (operation 2018) [@securelist-shadowhammer]; 2020 Pluton announced [@learn-pluton]; 2022 Driver Block List default-on [@msft-driver-blocklist]; 2024 CrowdStrike outage [@ms-crowdstrike-blog] [@msft-crowdstrike-best-practices]; 2025 MVI 3.0 user-mode preview [@weston-2024] [@weston-2025].&lt;/p&gt;
&lt;p&gt;If user identity was easy, why did code identity take thirty-three years -- and where exactly did each generation break?&lt;/p&gt;
&lt;h2&gt;Why code had no name&lt;/h2&gt;
&lt;p&gt;Helen Custer&apos;s 1992 &lt;em&gt;Inside Windows NT&lt;/em&gt; opens its security chapter on a single principle: the user is the principal [@custer-inside-nt]. Every action the kernel arbitrates is attributable to a user account. The token that the kernel manufactures at logon carries a Security Identifier (SID) for the user, SIDs for each group the user belongs to, a privilege bitmap, and a set of impersonation flags. Every Discretionary Access Control List on every securable object is evaluated against that token [@ms-sids]. The kernel never asks what binary is running. It asks who is running it.&lt;/p&gt;

A variable-length value that uniquely identifies a security principal in Windows. Users, groups, computer accounts, and (later) packages and capabilities all receive SIDs. Until Windows 8, every SID encoded a *user* or *group*; AppContainer and Package SIDs (the `S-1-15-2-...` form) extended SIDs to name code instead.
&lt;p&gt;For 1993&apos;s threat model, the user-as-principal model was defensible. NT 3.1 lived on multi-user workstations in a trusted local-area network. The attacker the designers worried about was a malicious insider, a contractor with the wrong group membership, an admin who exceeded his authority. Code arrived on floppies and CDs from coworkers and shrink-wrapped vendors; nobody downloaded executables off the public internet, because for most of the world there was no public internet to download them from.Integrity levels (Low, Medium, High, System) were added later, in Vista (2006), and they are still attributes of the &lt;em&gt;token&lt;/em&gt;, not of the binary on disk. A Low-integrity Internet Explorer process and a Low-integrity Notepad receive the same write restrictions because their tokens carry the same Mandatory Integrity Control label, regardless of which binary loaded.&lt;/p&gt;
&lt;p&gt;Then came Internet Explorer 3.0 in August 1996 and ActiveX. Microsoft repositioned OLE/COM as a cross-internet component model and committed to letting any compliant ActiveX control execute inside the browser [@ms-news-1996-authenticode]. The decision was not casually made; it was the strategic foundation of Microsoft&apos;s bet on the web. But its consequence at the security layer was immediate and devastating.&lt;/p&gt;
&lt;p&gt;If Alice double-clicks a control on a web page, the operating system&apos;s question is &quot;who is running this?&quot; The answer is &quot;Alice.&quot; She is allowed to run anything she wants. The control does whatever it likes -- with her token, her files, her privileges, her network access. The user-as-principal model has no second axis to invoke.&lt;/p&gt;
&lt;p&gt;There was no theoretical fix at this layer. Alice did genuinely request the download. She did genuinely double-click. NT had no other principal to consult. The model was complete, internally consistent, and exactly wrong for the new threat surface.&lt;/p&gt;
&lt;p&gt;What was missing was a cryptographic, network-portable identity for the code itself, attached to the binary in a way nobody downstream could forge. If the kernel cannot see the code, who can put a name on it -- and how do we attach that name to a running PE?&lt;/p&gt;
&lt;h2&gt;The first naive attempt: Authenticode (1996)&lt;/h2&gt;
&lt;p&gt;On August 7, 1996, Microsoft and VeriSign jointly announced the first cryptographic answer Windows had ever offered to &quot;who is this code?&quot; The press release ran twenty-two paragraphs and named every design choice that the next thirty years of Windows code identity would inherit: an X.509 certificate issued by an external commercial Certificate Authority, a PKCS#7 SignedData blob attached directly to the binary, and verification at download or install time by Internet Explorer 3.0 [@ms-news-1996-authenticode].&lt;/p&gt;

A cryptographic format for binding a publisher&apos;s identity and a tamper-evident hash to a Portable Executable. The signature is stored in the PE Attribute Certificate Table as a PKCS#7 SignedData structure containing an X.509 certificate chain and a hash that excludes the checksum field, the certificate-table directory entry, and the certificate table itself. Authenticode names the *publisher*, not the code; this is the founding constraint the rest of the article is forced to work around.

The new Microsoft Authenticode technology uniquely identifies the publisher of a piece of software and provides assurance to end users that it has not been tampered with or modified. -- Microsoft press release, August 7, 1996 [@ms-news-1996-authenticode]
&lt;p&gt;That sentence is the founding promise of Windows code identity. Read it once and the rest of the article becomes inevitable. Authenticode promises two things. It identifies the publisher. It detects tampering. It does not promise that the publisher is trustworthy, that the publisher&apos;s key is uncompromised, or that the bytes it covers are safe to execute. Three decades of failure modes follow from exactly that scoping.&lt;/p&gt;
&lt;p&gt;The mechanism is precise enough to demand a diagram. SignTool computes a hash that deliberately skips three regions of the PE: the checksum field (which the loader recomputes), the certificate-table directory entry, and the certificate table itself. The signature does not have to sign the bytes of its own embedding [@ms-pe-format].&lt;/p&gt;
&lt;p&gt;It then forms a PKCS#7 SignedData structure [@rfc-2315] containing the hash, an algorithm identifier, the X.509 chain, and an optional RFC 3161 timestamp. That blob is appended to the certificate table. At verify time, &lt;code&gt;WinVerifyTrust&lt;/code&gt; recomputes the hash, walks the chain to a trusted root, and (if a timestamp is present) honours signatures that were valid as of the timestamped time even if the issuer has since revoked the certificate [@ms-cryptotools].&lt;/p&gt;

sequenceDiagram
    participant Dev as Developer
    participant Sign as SignTool
    participant PE as PE binary
    participant Win as WinVerifyTrust
    participant CA as CA / chain store
    Dev-&amp;gt;&amp;gt;Sign: signtool sign /a app.exe
    Sign-&amp;gt;&amp;gt;PE: hash bytes (skip checksum + cert table)
    Sign-&amp;gt;&amp;gt;PE: build PKCS#7 SignedData
    Sign-&amp;gt;&amp;gt;PE: append RFC 3161 timestamp
    Sign-&amp;gt;&amp;gt;PE: write into Attribute Cert Table
    Note over Win: at install / download time
    Win-&amp;gt;&amp;gt;PE: re-hash same byte ranges
    Win-&amp;gt;&amp;gt;PE: extract PKCS#7 SignedData
    Win-&amp;gt;&amp;gt;CA: verify X.509 chain to trusted root
    CA--&amp;gt;&amp;gt;Win: chain ok
    Win--&amp;gt;&amp;gt;Win: trust verdict (advisory pre-Vista)
&lt;p&gt;Three structural failure modes shipped on day one and still ship in 2026.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Userland was advisory.&lt;/strong&gt; A signed &lt;code&gt;.exe&lt;/code&gt; ran. An unsigned &lt;code&gt;.exe&lt;/code&gt; also ran. Internet Explorer would prompt the user with a publisher name, but the prompt was a UI feature, not a kernel gate. The signature was a credential offered for inspection, never a wall the loader refused to cross. Closing this gap took ten years for kernel code (Authenticode 1996 [@ms-news-1996-authenticode] -&amp;gt; KMCS, Vista 2006 [@ms-kmcs]) and nineteen years for managed user-mode policy (Authenticode 1996 [@ms-news-1996-authenticode] -&amp;gt; Device Guard, 2015 [@ms-appcontrol]). Unmanaged consumer Windows in 2026 still permits arbitrary unsigned &lt;code&gt;.exe&lt;/code&gt; to run if the user clicks through SmartScreen.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The signed hash did not cover the whole file.&lt;/strong&gt; This is CVE-2013-3900, disclosed by Microsoft on December 10, 2013 in security bulletin MS13-098 [@ms13-098]. The Authenticode hash skips the certificate-table region by design, and the verifier in &lt;code&gt;WinVerifyTrust&lt;/code&gt; did not constrain the size of the unsigned PKCS#7 blob. An attacker could append arbitrary unauthenticated bytes inside the &lt;code&gt;WIN_CERTIFICATE&lt;/code&gt; structure of an already-signed PE without invalidating the signature.&lt;/p&gt;
&lt;p&gt;The fix was a registry value, &lt;code&gt;EnableCertPaddingCheck=1&lt;/code&gt;, that turned on strict verification. Microsoft chose not to enable it by default. Twelve years later, the National Vulnerability Database still records the same scoping note: &quot;Microsoft does not plan to enforce the stricter verification behavior as a default functionality on supported releases of Microsoft Windows&quot; [@nvd-cve-2013-3900]. CISA added CVE-2013-3900 to its Known Exploited Vulnerabilities catalog on January 10, 2022 -- eight years after disclosure, because attackers were still abusing the unfixed default [@nvd-cve-2013-3900].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; CVE-2013-3900 is still default-off in 2026. On any Windows endpoint where strict signature verification matters, set &lt;code&gt;HKLM\Software\Microsoft\Cryptography\Wintrust\Config\EnableCertPaddingCheck=1&lt;/code&gt; (and the WOW6432Node mirror on 64-bit). Microsoft documents the change as opt-in by design [@msrc-cve-2013-3900].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Timestamped signatures survive revocation.&lt;/strong&gt; The trust evaluator in &lt;code&gt;WinVerifyTrust&lt;/code&gt; is told to trust signatures as of the timestamped instant, not as of now. Removing this property would invalidate large catalogs of legitimate, archived signed software whose signing certificates have since expired [@ms-cryptotools]. The same property is what let the Stuxnet drivers load on every Windows machine that received them, because Microsoft revoked the Realtek and JMicron certificates &lt;em&gt;after&lt;/em&gt; Stuxnet had already shipped.The architectural choice here is genuinely hard. Synchronous global revocation would break offline software install. Asynchronous revocation, the alternative Microsoft chose, lets pre-revocation signatures continue to verify forever. There is no third option inside the Authenticode design.&lt;/p&gt;
&lt;p&gt;Pull these three threads together and the first aha falls out. Authenticode names the &lt;em&gt;publisher&lt;/em&gt;, not the code. A signed binary is a credential, not a verdict. The signature proves the bytes came from a holder of the publisher&apos;s private key. It does not prove the publisher is trustworthy, that the publisher&apos;s key has not been stolen, or that the bytes are safe to execute. Every failure mode of the next twenty-five years lives in that gap.&lt;/p&gt;
&lt;p&gt;Six years of failure modes had to accumulate before Microsoft executive priorities caught up. On January 15, 2002, Bill Gates sent the &quot;Trustworthy Computing&quot; memo company-wide, declaring security a higher priority than features and freezing engineering work for security review across its Windows product line (with SDL processes later extended company-wide) [@cnet-gates-memo] [@theregister-tcm]. The memo did not specify a code-identity mechanism. It is in this story because every later code-identity primitive -- the Security Development Lifecycle&apos;s mandatory SignTool integration, the XP SP2 hardening pass that produced MOTW, and the Vista work that produced KMCS -- shipped under the executive cover the memo provided [@windows-internals-7e].&lt;/p&gt;
&lt;p&gt;If unsigned code still runs in userland, what makes us think the same primitive will work for a kernel driver -- where the wrong binary owns the operating system?&lt;/p&gt;
&lt;h2&gt;The first refusal: KMCS, EV, and the WHQL pipeline (Vista, 2006)&lt;/h2&gt;
&lt;p&gt;Vista x64 shipped in November 2006 as the first Windows release that &lt;em&gt;refuses to load unsigned kernel code&lt;/em&gt; [@ms-kmcs]. The refusal was uncompromising. The kernel loader and the Plug-and-Play manager call into &lt;code&gt;WinVerifyTrust&lt;/code&gt; for every driver image; if the chain does not terminate at one of a small set of Microsoft-trusted roots, &lt;code&gt;MmLoadSystemImage&lt;/code&gt; returns &lt;code&gt;STATUS_INVALID_IMAGE_HASH&lt;/code&gt; and the driver does not load.&lt;/p&gt;

The Vista-era policy that requires every kernel-mode driver to carry an Authenticode signature chained to a Microsoft-trusted root. From Windows 10 1607 onward (the August 2016 Anniversary Update), only drivers signed by Microsoft via the Hardware Developer Center are accepted on Secure-Boot systems; end-entity cross-signed certificates issued before July 29, 2015 are grandfathered for legacy devices [@ms-kmcs].
&lt;p&gt;The mechanism is a load-time gate. In 2026, Microsoft offers three signing tiers that all terminate at a Microsoft cross-signed cert: HLK-tested (the full Windows Hardware Lab Kit run, eligible for retail Windows Update distribution), attestation-signed (lighter-weight, EV cert plus Microsoft attestation key, no hardware testing), and preproduction (developer signing for pre-release Windows builds) [@learn-driver-signing-offerings] [@ms-attestation-signing]. Driver &lt;code&gt;.cat&lt;/code&gt; catalog files extend Authenticode coverage from a single PE to an entire driver package, including INF files and supporting executables [@learn-embedded-sig].&lt;/p&gt;
&lt;p&gt;EV certificates -- Extended Validation, with mandatory hardware-security-module key storage and audited issuance -- became the practical floor for kernel signing. The reason was not pedagogical. A Domain Validated Authenticode cert from a commodity CA in that era could be obtained cheaply, often with little more than a working email address. EV raised the cost and binding strength of the publisher claim by an order of magnitude.&lt;/p&gt;
&lt;p&gt;Then, on June 17, 2010, Sergey Ulasen of the Belarusian anti-virus vendor VirusBlokAda flagged a strange piece of malware on a customer machine in Iran. It had been signed [@wikipedia-stuxnet].&lt;/p&gt;
&lt;p&gt;The Stuxnet dropper carried two kernel drivers, &lt;code&gt;mrxnet.sys&lt;/code&gt; and &lt;code&gt;mrxcls.sys&lt;/code&gt;, signed with legitimate Authenticode certificates issued to Realtek Semiconductor and JMicron Technology -- two Taiwanese hardware vendors. Investigators concluded the private keys had been physically exfiltrated from the publishers&apos; Taiwanese offices. VeriSign revoked the Realtek certificate on July 16, 2010 (and the JMicron certificate shortly afterward); Microsoft issued advisories and pushed Windows CTL updates to propagate the revocation [@symantec-stuxnet]. While the certs were valid, Vista x64 KMCS happily loaded both drivers on every system it touched.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; KMCS verifies &lt;em&gt;who signed&lt;/em&gt;, never &lt;em&gt;whether the signed code is safe&lt;/em&gt;. Every kernel-mode-identity failure between 2010 and 2026 reduces to that single sentence.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The Stuxnet certificates were not anomalies. The same failure shape -- valid Microsoft-rooted signature, on code the publisher did not intend to ship, on a healthy KMCS-enforcing kernel -- replays at predictable intervals.&lt;/p&gt;

The Flame espionage toolkit produced a *forged* Microsoft-rooted certificate by exploiting an MD5 chosen-prefix collision against Microsoft&apos;s Terminal Services Licensing Service, which still issued MD5-hash code-signing certificates years after MD5&apos;s brokenness was known. Microsoft Security Advisory 2718704 revoked three of its own intermediate CAs and emergency-deployed a new Untrusted Certificate Store mechanism through Windows Update [@ms-2718704] [@msrc-2718704]. The episode forced Microsoft to deprecate MD5 in code signing and led directly to the curation infrastructure the Driver Block List uses today.
&lt;p&gt;ASUS ShadowHammer in 2018, disclosed by Kaspersky in 2019, added a third variant. The attackers did not steal an HSM-bound key. They compromised ASUS&apos;s signing pipeline and got their backdoor signed by ASUS&apos;s &lt;em&gt;production&lt;/em&gt; signing key in the normal course of a normal release, distributed through ASUS Live Update [@securelist-shadowhammer]. Kaspersky&apos;s analysis recorded &quot;trojanized updaters were signed with legitimate certificates (eg: &apos;ASUSTeK Computer Inc.&apos;)&quot; and that &quot;over 57,000 Kaspersky users have downloaded and installed the backdoored version of ASUS Live Update.&quot; The trust root, the chain, the cert -- all valid. The bytes -- attacker-controlled.&lt;/p&gt;
&lt;p&gt;KMCS verified that a driver was signed, not that it was safe. Signing alone was not enough. But what was?&lt;/p&gt;
&lt;h2&gt;The second refusal: identity as a runtime attribute (PPL, 2013)&lt;/h2&gt;
&lt;p&gt;Until October 17, 2013, code identity gated &lt;em&gt;whether&lt;/em&gt; code could load. Windows 8.1 quietly shipped a structural shift: code identity now also gated &lt;em&gt;what one running process could do to another&lt;/em&gt; [@ionescu-ppl]. Alex Ionescu, then CrowdStrike&apos;s founding Chief Architect and previously a co-author of &lt;em&gt;Windows Internals&lt;/em&gt;, was the first person to publish a detailed external map of the new mechanism. The lineage runs back to Vista&apos;s 2006 Protected Process model, originally introduced as a DRM container for protected media playback; PPL is the security-grade descendant of that primitive, repurposed seven years later as a general-purpose process-protection mechanism [@windows-internals-7e].&lt;/p&gt;

A protection attribute attached to running processes that mediates inter-process access checks above and beyond the user-token DACL. PPL processes carry a *signer level* (in increasing order, roughly: `Authenticode`, `CodeGen`, `Antimalware`, `Lsa`, `Windows`, `WinTcb`, `WinSystem`). A process can open `PROCESS_VM_READ`, `PROCESS_VM_WRITE`, or `CREATE_THREAD` rights against another protected process only if its own signer level is greater than or equal to the target&apos;s [@ionescu-ppl] [@ms-protected-processes].
&lt;p&gt;The mechanism lives in the kernel&apos;s &lt;code&gt;EPROCESS&lt;/code&gt; object. When process A opens process B, the kernel calls into &lt;code&gt;RtlTestProtectedAccess&lt;/code&gt; (and downstream &lt;code&gt;PsTestProtectedProcessIncompatibility&lt;/code&gt;) before any DACL evaluation [@scrt-ppl-bypass]. If A&apos;s signer level is below B&apos;s, sensitive access masks are silently stripped from the returned handle. The classic effect: an attacker running with a SYSTEM token, holding &lt;code&gt;SeDebugPrivilege&lt;/code&gt;, calling &lt;code&gt;OpenProcess&lt;/code&gt; on LSASS, gets back a handle without &lt;code&gt;PROCESS_VM_READ&lt;/code&gt;. Mimikatz can no longer dump the LSASS process memory.&lt;/p&gt;
&lt;p&gt;The signer level itself is set by an Enhanced Key Usage extension on the Authenticode certificate Microsoft issues to the binary&apos;s publisher. Antimalware vendors receive a certificate carrying the &lt;code&gt;Antimalware&lt;/code&gt; EKU; only Microsoft-internal binaries carry &lt;code&gt;WinTcb&lt;/code&gt; [@itm4n-runasppl]. Identity, in this model, is an EKU OID baked into a Microsoft-issued Authenticode cert, attached to the binary, evaluated by the kernel at every cross-process access check.&lt;/p&gt;

flowchart TD
    A[WinSystem]
    B[WinTcb]
    C[Windows]
    D[Lsa]
    E[Antimalware]
    F[CodeGen]
    G[Authenticode]
    A --&amp;gt; B --&amp;gt; C --&amp;gt; D --&amp;gt; E --&amp;gt; F --&amp;gt; G
    H[&quot;Caller (signer level X)&quot;] -- &quot;OpenProcess(target T, signer Y)&quot; --&amp;gt; I{&quot;X &amp;gt;= Y ?&quot;}
    I -- yes --&amp;gt; J[&quot;full access mask&quot;]
    I -- no  --&amp;gt; K[&quot;VM_READ / VM_WRITE / CREATE_THREAD stripped&quot;]
&lt;p&gt;LSASS-as-PPL is the canonical demonstration of the mechanism in practice. Setting &lt;code&gt;HKLM\SYSTEM\CurrentControlSet\Control\Lsa\RunAsPPL=1&lt;/code&gt; causes the next boot&apos;s LSASS to start with &lt;code&gt;PsProtectedSignerLsa&lt;/code&gt;. From that moment, no process below the &lt;code&gt;Lsa&lt;/code&gt; signer level can read LSASS memory, regardless of the user account. Mimikatz still runs as code; its &lt;code&gt;OpenProcess(LSASS, PROCESS_VM_READ)&lt;/code&gt; call returns a handle with the read right stripped, and its memory dump fails with &lt;code&gt;STATUS_ACCESS_DENIED&lt;/code&gt; before it ever sees a credential blob [@itm4n-runasppl].The &lt;code&gt;RunAsPPL=1&lt;/code&gt; setting is mirrored into a UEFI variable on Secure Boot systems precisely so that an attacker with &lt;code&gt;HKLM\SYSTEM&lt;/code&gt; registry write but no firmware-level access cannot disable LSA Protection by editing the registry and rebooting. The UEFI mirror is checked before the registry value is read [@itm4n-runasppl].&lt;/p&gt;
&lt;p&gt;ELAM -- Early Launch Antimalware -- is the same idea applied to boot. An ELAM driver, signed with a Microsoft-issued antimalware certificate, runs before any third-party boot driver and gets to vote on which subsequent drivers are allowed to load [@learn-elam]. Signer level enters the boot chain at the earliest moment third-party code can enter the boot chain.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; PPL&apos;s invention is conceptual, not just mechanical. Code identity becomes a runtime ACL between two running processes, not merely a load-time gate. App Control, HVCI, and the Driver Block List all operate on this same conceptual frame: identity continuously evaluated, in context, while code is executing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;PPL was, and is, the right idea. It is also incomplete in two ways that drove every subsequent layer.&lt;/p&gt;
&lt;p&gt;The first gap is BYOVD -- Bring Your Own Vulnerable Driver. A signed-but-vulnerable driver such as &lt;code&gt;RTCore64.sys&lt;/code&gt; (shipped with MSI Afterburner), &lt;code&gt;Capcom.sys&lt;/code&gt; (shipped with the &lt;em&gt;Street Fighter V&lt;/em&gt; anti-cheat), or &lt;code&gt;gdrv.sys&lt;/code&gt; (shipped with Gigabyte motherboard utilities) gives any local administrator arbitrary kernel read/write through an IOCTL. Because these drivers are validly KMCS-signed, they load. From kernel mode, the attacker simply zeroes the &lt;code&gt;Protection&lt;/code&gt; byte in the target process&apos;s &lt;code&gt;EPROCESS&lt;/code&gt; structure, and PPL evaporates. The signing chain is sound. The signer level is correctly evaluated. The mechanism that decides which kernel code is allowed to &lt;em&gt;exist&lt;/em&gt; -- not just to be signed -- is what fails.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; PPL is bypassed not by attacking PPL itself but by editing &lt;code&gt;EPROCESS.Protection&lt;/code&gt; from kernel mode. That is exactly why the Driver Block List had to exist as a separate layer above KMCS [@msft-driver-blocklist].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The second gap is the user-mode side. PPLdump and PPLfault demonstrated that confused-deputy DLL loads inside higher-PPL services could be turned into an arbitrary memory read of LSASS. Microsoft eventually patched PPLdump in Windows 10 21H2 build 19044.1826, but the failure &lt;em&gt;class&lt;/em&gt; remains structural: trusting a higher-signer process to safely load DLLs from publisher-controlled paths is a foot-gun every time a new such service ships [@ppldump-github] [@scrt-ppl-bypass].&lt;/p&gt;
&lt;p&gt;If signer level is the principal for OS-internal processes, what is the principal for the next layer up -- the application?&lt;/p&gt;
&lt;h2&gt;The application becomes a principal: AppContainer and the Package SID&lt;/h2&gt;
&lt;p&gt;Two processes, same user, same machine. One can read the user&apos;s SSH private keys. The other cannot. Same token. Same DACLs on the file. Different verdict. That is the AppContainer promise [@ms-appcontainer-isolation], and to keep it the operating system needs a &lt;em&gt;cryptographic identity for the application itself&lt;/em&gt; -- something derived from the application, not from the user, that ACLs can name.&lt;/p&gt;
&lt;p&gt;Windows 8 shipped AppContainer in 2012. Internally it was called LowBox, the name surviving in the legacy documentation [@ms-appcontainer-legacy]. Windows 10 generalised the model into MSIX, the modern app-package format [@ms-msix].&lt;/p&gt;

AppContainer is a per-process sandbox that augments the user-token security check with an *AppContainer SID* (`S-1-15-2-...`) derived from the package identity of the running application. ACLs and capability claims (such as `internetClient` or `picturesLibrary`) are evaluated against this SID, not against the user. Two processes running as the same user can therefore receive different access verdicts because their AppContainer SIDs differ.
&lt;p&gt;The cryptographic move is in how the SID is built.&lt;/p&gt;

Every MSIX/APPX package is identified by a five-element tuple: `(Name, Version, Architecture, ResourceId, Publisher)` [@ms-package-identity]. The `Publisher` field is the X.509 subject Distinguished Name of the certificate that signed the package. A 13-character `PublisherId` is derived deterministically from the Publisher DN by Crockford-Base32 encoding the first 64 bits of a SHA-256 hash (per community reverse-engineering; Microsoft&apos;s public documentation does not confirm the specific algorithm). The *Package Family Name* is then `_`; the *AppContainer SID* is computed deterministically from the full identity tuple and slotted into the `S-1-15-2-...` namespace.
&lt;p&gt;The derivation is dense enough to deserve a worked example. &lt;code&gt;Microsoft Corporation&lt;/code&gt; plus the &lt;code&gt;Microsoft.WindowsCalculator&lt;/code&gt; package name yields &lt;code&gt;Microsoft.WindowsCalculator_8wekyb3d8bbwe&lt;/code&gt; -- the suffix is the Crockford-Base32 PublisherId of &lt;code&gt;Microsoft Corporation&lt;/code&gt;&apos;s subject DN [@ms-package-identity]. Every MSIX package whose Publisher DN matches will share that suffix; every package whose Publisher DN differs will have a different suffix; an attacker who does not hold the publisher&apos;s signing key cannot make a package masquerade as belonging to that publisher&apos;s family.&lt;/p&gt;
&lt;p&gt;{&lt;code&gt;async function publisherIdOf(publisherDN) {   const data = new TextEncoder().encode(publisherDN);   const digest = await crypto.subtle.digest(&apos;SHA-256&apos;, data);   const first8 = new Uint8Array(digest.slice(0, 8));   // Crockford Base32 alphabet (no I, L, O, U)   const alpha = &apos;0123456789abcdefghjkmnpqrstvwxyz&apos;;   let bits = 0n;   for (const b of first8) bits = (bits &amp;lt;&amp;lt; 8n) | BigInt(b);   let out = &apos;&apos;;   for (let i = 0; i &amp;lt; 13; i++) {     out = alpha[Number(bits &amp;amp; 31n)] + out;     bits &amp;gt;&amp;gt;= 5n;   }   return out; } const dn = &apos;CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US&apos;; publisherIdOf(dn).then(pid =&amp;gt; console.log(&apos;PFN suffix candidate:&apos;, pid)); console.log(&apos;Real PFN: Microsoft.WindowsCalculator_8wekyb3d8bbwe&apos;); console.log(&apos;Note: the real algorithm is documented in package-identity-overview; this snippet demonstrates the structure, not the exact hash.&apos;);&lt;/code&gt;}&lt;/p&gt;
&lt;p&gt;Capabilities sit at the same layer. When an MSIX manifest declares &lt;code&gt;&amp;lt;Capability Name=&quot;internetClient&quot; /&amp;gt;&lt;/code&gt;, the package is tagged at install time with a &lt;em&gt;capability SID&lt;/em&gt; of the form &lt;code&gt;S-1-15-3-1&lt;/code&gt;, and the Windows Filtering Platform evaluates outbound TCP connections against that SID, not against the user&apos;s [@p0-appcontainer]. Mandatory Integrity Control labels (Low/Medium/High) compose with the AppContainer SID rather than replacing it [@learn-mic]. A broker process running outside the AppContainer is the only path back to user-scoped resources, and the broker keys its trust decisions on the calling Package SID.&lt;/p&gt;

Windows Hello&apos;s biometric authentication broker is itself an MSIX-style protected service whose AppContainer-flavoured identity is the Package SID derived from its Microsoft-signed manifest. Other processes that want to ask Hello to verify a face or a fingerprint must talk to the broker, and the broker decides whether to honour the request based partly on the caller&apos;s package identity. The reason this matters is the same as the LSASS reason: the secret material the broker holds (the user&apos;s TPM-bound private key) needs a principal that an attacker holding a SYSTEM token cannot impersonate. User-SID equality is not enough. Package-SID equality is.
&lt;p&gt;The &lt;code&gt;8wekyb3d8bbwe&lt;/code&gt; suffix you see on Calculator, Edge, the Microsoft Store, and most other in-box apps is &lt;code&gt;Microsoft Corporation&lt;/code&gt;&apos;s PublisherId. Once you know what it is, you start seeing it everywhere -- it is the cryptographic fingerprint of &quot;Microsoft signed this package&quot; [@ms-package-identity].&lt;/p&gt;
&lt;p&gt;The aha is the same shape as the PPL aha but at the layer above. Two binaries running as the same user can be authorised differently because the Package SID is derived from the manifest publisher and the package cannot forge it. AppContainer is not a sandbox you opt into. It is a SID you have. Capability ACLs name that SID. The firewall keys on it. The MIC label composes with it. The broker checks it.&lt;/p&gt;
&lt;p&gt;The limits are also visible. AppContainer is opt-in for Win32 desktop apps that have not been packaged. Forshaw&apos;s 2021 Project Zero analysis of the AppContainer firewall identified loopback-exemption and namespace-isolation holes that Microsoft classified as WontFix [@p0-appcontainer]. Per-app sandbox identity solves the Modern-app problem; it does not solve the legacy Win32 problem. For that, the operating system needs a policy plane that names code in publisher vocabulary instead of path vocabulary.&lt;/p&gt;
&lt;p&gt;What does an enterprise admin do when the application refuses to be packaged at all?&lt;/p&gt;
&lt;h2&gt;The policy plane: AppLocker, App Control, and the publisher rule&lt;/h2&gt;
&lt;p&gt;Path-based whitelisting failed for the same reason path-based ACLs failed. Anything writeable can be planted. AppLocker, shipped in Windows 7 in 2009, still stays in the box for compatibility, but Microsoft&apos;s own documentation recommends App Control for Business -- the rebranded Windows Defender Application Control -- for new deployments [@ms-applocker] [@ms-appcontrol]. The change is not cosmetic. It is the difference between filename-as-identity and Authenticode-publisher-as-identity.&lt;/p&gt;

A Code Integrity policy mechanism that expresses allow and deny rules in Authenticode-publisher vocabulary. Policies are authored in XML, compiled to a binary `siPolicy.p7b`, and enforced by the Code Integrity engine at every PE load. With HVCI active, enforcement happens inside the Hyper-V-protected secure kernel, immune to a compromised NT kernel [@ms-appcontrol].
&lt;p&gt;The certificate-and-publisher rule levels run from strictest to broadest as &lt;code&gt;Hash &amp;gt; FileName &amp;gt; FilePath &amp;gt; FilePublisher &amp;gt; SignedVersion &amp;gt; LeafCertificate &amp;gt; Publisher &amp;gt; PcaCertificate&lt;/code&gt;, with a parallel WHQL-only family for kernel drivers ordered &lt;code&gt;WHQLFilePublisher &amp;gt; WHQLPublisher &amp;gt; WHQL&lt;/code&gt; [@ms-appcontrol]. &lt;code&gt;Hash&lt;/code&gt; is the strictest (this exact byte string); &lt;code&gt;PcaCertificate&lt;/code&gt; is the broadest signer-based level (anything signed under that intermediate CA). Microsoft documents &lt;code&gt;RootCertificate&lt;/code&gt; as not supported, and &lt;code&gt;FilePath&lt;/code&gt; -- available for user-mode binaries from Windows 10 1903 onward -- is path-based and so inherits the failure modes the publisher-rule model was designed to escape.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;LeafCertificate &amp;gt; Publisher&lt;/code&gt; adjacency is the subtle one. &lt;code&gt;LeafCertificate&lt;/code&gt; pins to one specific signing certificate, so a renewal under a new leaf cert no longer matches. &lt;code&gt;Publisher&lt;/code&gt; matches any certificate with the same PCA + leaf-CN combination, including future renewals. &lt;code&gt;LeafCertificate&lt;/code&gt; is the stricter of the two [@ms-appcontrol].&lt;/p&gt;
&lt;p&gt;The practical sweet spot is &lt;code&gt;FilePublisher&lt;/code&gt;. It binds an allow rule to the tuple &lt;code&gt;(certificate authority + leaf publisher CN + original filename + minimum version)&lt;/code&gt;. That tuple survives recompiles: a benign update from the same publisher under the same name, signed by the same key, with a higher version still passes. It does not survive tampering. Change the original filename in the resource section, change the publisher, change the leaf certificate, and the rule no longer matches.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Policy primitive&lt;/th&gt;
&lt;th&gt;Era&lt;/th&gt;
&lt;th&gt;Rule basis&lt;/th&gt;
&lt;th&gt;Kernel coverage&lt;/th&gt;
&lt;th&gt;Default state&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Software Restriction Policies (SRP)&lt;/td&gt;
&lt;td&gt;XP, 2001&lt;/td&gt;
&lt;td&gt;path / hash / certificate&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;unmanaged&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AppLocker&lt;/td&gt;
&lt;td&gt;Windows 7 Enterprise, 2009&lt;/td&gt;
&lt;td&gt;path / publisher / hash&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;off&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WDAC (Device Guard)&lt;/td&gt;
&lt;td&gt;Windows 10, 2015&lt;/td&gt;
&lt;td&gt;publisher / file attributes / hash&lt;/td&gt;
&lt;td&gt;full (with &lt;a href=&quot;https://paragmali.com/blog/the-windows-secure-kernel/&quot; rel=&quot;noopener&quot;&gt;HVCI&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;off&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App Control for Business&lt;/td&gt;
&lt;td&gt;renamed 2023&lt;/td&gt;
&lt;td&gt;publisher / file attributes / hash&lt;/td&gt;
&lt;td&gt;full (with HVCI)&lt;/td&gt;
&lt;td&gt;off; on by default in S Mode and on Windows 11 SE&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The Code Integrity engine evaluates an App Control policy on every PE load -- user mode and kernel mode alike. With HVCI active, the policy lives behind the Hyper-V security boundary; even an NT-kernel-level attacker with arbitrary memory write cannot edit it without breaking out of the virtualization layer [@ms-appcontrol]. Deny rules always win; an explicit deny can never be undone by any number of allows on the same binary.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Author every App Control policy in audit mode for at least one full reference-image cycle before promoting to enforce. Audit mode logs every load that &lt;em&gt;would have been&lt;/em&gt; blocked, into the &lt;code&gt;Microsoft-Windows-CodeIntegrity/Operational&lt;/code&gt; event channel, without breaking anything. The pre-deployment failure rate of strict policies on real fleets is high enough that audit mode is not optional [@ms-appcontrol].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;App Control inherits the same structural ceiling Authenticode put in place. &lt;code&gt;Allow Signer = Microsoft Windows&lt;/code&gt; admits the entire LOLBins inventory -- &lt;code&gt;regsvr32&lt;/code&gt;, &lt;code&gt;mshta&lt;/code&gt;, &lt;code&gt;installutil&lt;/code&gt;, &lt;code&gt;rundll32&lt;/code&gt;, every signed-by-Microsoft binary an attacker can call to execute arbitrary content. &lt;code&gt;Allow Signer = ASUSTeK&lt;/code&gt; would have admitted ShadowHammer (operation 2018, disclosed 2019), every byte of which carried a valid ASUS production signature [@securelist-shadowhammer]. The publisher-rule model is the right primitive for managed endpoints, and the LOLBins / supply-chain-attack failure modes are the structural ceiling on what the primitive can prove.&lt;/p&gt;
&lt;p&gt;PKI-rooted publisher policy still trusts the publisher&apos;s key custody. When the key is stolen or the binary is signed but malicious, what does the operating system fall back on?&lt;/p&gt;
&lt;h2&gt;Reputation as identity: Mark of the Web and SmartScreen&lt;/h2&gt;
&lt;p&gt;A novel binary, signed by a freshly issued EV cert, has zero history. PKI says yes. Reputation says: I have never seen this before -- run it past the user.&lt;/p&gt;

An NTFS alternate data stream named `Zone.Identifier` written by browsers, mail clients, and other downloaders to record the trust zone of a downloaded file. The stream contains an INI-style `[ZoneTransfer]` block with `ZoneId=3` for files from the public internet, plus optional `ReferrerUrl=` and `HostUrl=` fields. The protocol is documented in the MS-FSCC reference [@ms-fscc-motw]. SmartScreen, Office Protected View, and the Attachment Execution Service all read MOTW to gate behaviour on origin.
&lt;p&gt;MOTW is not an Authenticode replacement. It is a parallel, &lt;em&gt;origin-based&lt;/em&gt; identity: the binary&apos;s provenance, encoded as data the file system carries with it, separate from any signature. Origin is the input to SmartScreen. SmartScreen submits a hash of the binary together with publisher metadata to a Microsoft-hosted reputation service; if the service has not seen the binary before, or has not seen enough downloads to be confident, the user gets the familiar &quot;Windows protected your PC&quot; prompt that requires an explicit More info / Run anyway click [@learn-smartscreen].&lt;/p&gt;
&lt;p&gt;The pipeline is parallel to Authenticode and App Control, not a successor. PKI says &quot;this signature chains to a real publisher.&quot; Reputation says &quot;this hash has been observed N times in the last 30 days, with prevalence trending up; the publisher account is six years old; M of the downloads were from machines later flagged for malware.&quot; None of those signals are derivable from a signature.The Defender machine-learning pipeline that powers SmartScreen reputation is the deeper version of the same idea -- already covered in &lt;em&gt;The Defender&apos;s Dilemma&lt;/em&gt; sibling article, which traces the twenty-year arc from Defender&apos;s 0.5/6 AV-TEST score to its 100% MITRE detection rate. The reputation primitive sits on top of that ML pipeline.&lt;/p&gt;
&lt;p&gt;The bypass surface is now well-known. Container formats (ISO, IMG, VHD, 7z) historically did not propagate MOTW to files extracted from them, because their on-disk representation does not preserve alternate data streams. Phishing campaigns adapted: send the attacker&apos;s &lt;code&gt;.exe&lt;/code&gt; inside an &lt;code&gt;.iso&lt;/code&gt;, the user mounts the &lt;code&gt;.iso&lt;/code&gt;, double-clicks the &lt;code&gt;.exe&lt;/code&gt;, and SmartScreen sees a binary with no MOTW and offers no warning.&lt;/p&gt;
&lt;p&gt;Microsoft&apos;s response combined fixes -- VHD and ISO MOTW propagation shipped in the December 2022 cumulative update for Windows 11 22H2, MOTW-aware extraction in OneDrive and the new Windows Archive APIs -- with two attack-surface-reduction rules that gate execution on prevalence and trust independently of MOTW [@learn-asr-reference]. The most useful is rule &lt;code&gt;01443614-cd74-433a-b99e-2ecdc07bfc25&lt;/code&gt;, &quot;Block executable files from running unless they meet a prevalence, age, or trusted list criterion.&quot;&lt;/p&gt;
&lt;p&gt;Office is the most consequential consumer of MOTW. A Word, Excel, or PowerPoint file carrying a &lt;code&gt;ZoneId=3&lt;/code&gt; Mark of the Web opens in Protected View: read-only, in a sandboxed renderer, with macros and active content disabled, until the user clicks &quot;Enable Editing&quot; on the message bar [@learn-protected-view].&lt;/p&gt;
&lt;p&gt;The 2022 wave of HTML-smuggling and ISO-borne malware that bypassed SmartScreen still tripped over Protected View at the document layer, and the post-2022 macro-blocked-by-default change extended the same MOTW-gated logic from container files to embedded VBA. Origin is now an input to two parallel pipelines: SmartScreen&apos;s reputation check on the executable, and Office&apos;s read-only-until-confirmed gate on the document.&lt;/p&gt;
&lt;p&gt;The full ASR rule GUIDs are in the Defender for Endpoint reference. Memorise none of them; pin the page.&lt;/p&gt;
&lt;p&gt;A useful way to read the layered system at this point: Authenticode answered &quot;who shipped it?&quot; KMCS answered &quot;is the kernel allowed to load it?&quot; PPL answered &quot;is this running process allowed to touch that one?&quot; AppContainer answered &quot;what application is this?&quot; App Control answered &quot;does the enterprise honour this publisher?&quot; MOTW and SmartScreen answer the question PKI cannot: &quot;have we seen this before, and from where?&quot; When PKI identity is necessary but not sufficient, reputation closes the gap -- statistically, never absolutely.&lt;/p&gt;
&lt;p&gt;PKI says yes; reputation says unknown. What does the operating system do when Microsoft itself says &lt;em&gt;no&lt;/em&gt; to a signature it just minted?&lt;/p&gt;
&lt;h2&gt;The breakthrough: signed is not trusted (Driver Block List, 2022)&lt;/h2&gt;
&lt;p&gt;December 8, 2021. Microsoft launches the Vulnerable and Malicious Driver Reporting Center [@msft-driver-reporting]. The blog post enumerates the failure shape that drove it: drivers that &quot;map arbitrary kernel, physical, or device memory to user mode,&quot; drivers that &quot;provide access to storage that bypass Windows access control,&quot; drivers whose IOCTLs let a local admin become an arbitrary kernel writer. Every one of those drivers was signed. Every one of those signatures was valid. Every one of those binaries was loadable on a default Windows install.&lt;/p&gt;
&lt;p&gt;By the Windows 11 22H2 update in September 2022, the Vulnerable Driver Block List was enabled by default [@msft-driver-blocklist]. The mechanism is a Microsoft-curated &lt;code&gt;SiPolicy.p7b&lt;/code&gt; (the same WDAC binary policy format), distributed through Windows Update and Defender intelligence updates, enforced by the Code Integrity engine -- with HVCI when present -- at every driver load. The published rules deny drivers by publisher, original filename, and hash. Critically, &lt;em&gt;the publisher&apos;s signature is still valid&lt;/em&gt;. The Block List is an explicit Microsoft veto layered on top of a working PKI verdict.&lt;/p&gt;

The blocklist included in this article ... usually contains a more complete set of known vulnerable drivers than the version in the OS and delivered by Windows Update. -- Microsoft Learn, *Microsoft recommended driver block rules* [@msft-driver-blocklist]
&lt;p&gt;That sentence, in Microsoft&apos;s own documentation, is the breakthrough. Microsoft is openly admitting that the version of the list shipped with the operating system trails the curated reference list. Curation is now a continuous, asynchronous activity, distinct from signing. The list ships on a quarterly cadence. New BYOVD drivers ship faster than that. The LOLDrivers community catalogue tracks hundreds of vulnerable drivers, many of which are not (yet) on Microsoft&apos;s list [@loldrivers].&lt;/p&gt;
&lt;p&gt;The Block List has a write-time companion. ASR rule &lt;code&gt;56a863a9-875e-4185-98a7-b882c64b5ce5&lt;/code&gt;, &quot;Block abuse of exploited vulnerable signed drivers,&quot; prevents &lt;em&gt;writing&lt;/em&gt; a known-vulnerable driver to disk in the first place [@learn-asr-reference]. The defence is layered: the Block List denies load; the ASR rule denies install; together they form a curtain across the BYOVD attack class. Together they do not close the BYOVD class -- the catalogue is a list, the threat is a set, and the gap is structural.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; A signature attests &lt;em&gt;who&lt;/em&gt;. A reputation score attests &lt;em&gt;unfamiliar versus seen-good&lt;/em&gt;. A block list attests &lt;em&gt;Microsoft has revoked trust at runtime, even though the signature still verifies&lt;/em&gt;. These are three distinct identity layers, and 2022 is the year all three were finally co-deployed by default on the same operating system.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &quot;Curated identity at runtime&quot; is the conceptual breakthrough. &quot;Quarterly cadence&quot; is its operational ceiling. The Driver Block List is a list, the BYOVD threat is a set, and the gap between them is the open problem the next layer (Pluton + attestation + faster curation pipelines) is being asked to close.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The Driver Block List is the operational expression of a 25-year admission. After 1996&apos;s &quot;the new Microsoft Authenticode technology uniquely identifies the publisher,&quot; after Vista&apos;s &quot;we will refuse unsigned kernel drivers,&quot; after Windows 8.1&apos;s &quot;signer level mediates inter-process access,&quot; after Windows 10&apos;s &quot;App Control names policy in publisher vocabulary,&quot; Microsoft&apos;s December 2021 blog post said something different. It said: a signature is a publisher claim; trust is a different claim; we, Microsoft, will curate the second claim continuously, even when we ourselves issued the first one. Identity has become curated, not just verified.&lt;/p&gt;
&lt;p&gt;If even Microsoft can no longer trust a valid signature, where does trust ultimately have to live?&lt;/p&gt;
&lt;h2&gt;The 2026 stack and the hardware future&lt;/h2&gt;
&lt;p&gt;The eight primitives from the previous sections do not run in isolation. They compose. A modern Windows boot -- on a Pluton-equipped 2026 laptop running Windows 11 24H2 with HVCI on, App Control in enforce mode, Smart App Control on, and Microsoft Defender as the active anti-malware -- evaluates code identity continuously, top to bottom, from firmware through user mode.&lt;/p&gt;

flowchart LR
    A[&quot;UEFI Secure Boot&lt;br /&gt;firmware-rooted PKI&quot;] --&amp;gt; B[&quot;Pluton / TPM&lt;br /&gt;measured boot, PCRs&quot;]
    B --&amp;gt; C[&quot;KMCS&lt;br /&gt;chain-to-Microsoft&quot;]
    C --&amp;gt; D[&quot;Driver Block List&lt;br /&gt;Microsoft curated veto&quot;]
    D --&amp;gt; E[&quot;ELAM&lt;br /&gt;signer-level boot gate&quot;]
    E --&amp;gt; F[&quot;User-mode Authenticode&lt;br /&gt;publisher attribution&quot;]
    F --&amp;gt; G[&quot;PPL signer-level&lt;br /&gt;runtime ACL&quot;]
    G --&amp;gt; H[&quot;AppContainer + Package SID&lt;br /&gt;per-app principal&quot;]
    H --&amp;gt; I[&quot;App Control for Business&lt;br /&gt;publisher policy&quot;]
    I --&amp;gt; J[&quot;MOTW + SmartScreen&lt;br /&gt;origin + reputation&quot;]
    J --&amp;gt; K[&quot;Pluton attestation&lt;br /&gt;device-identity claim&quot;]
&lt;p&gt;The hardware root has shifted in five years. Pluton, announced on November 17, 2020 by Microsoft together with AMD, Intel, and Qualcomm, is a security processor integrated into the CPU die rather than a discrete TPM chip on the motherboard bus [@ms-pluton-blog]. AMD Ryzen 6000-series and later (including Ryzen AI), Intel Core Series 3, Core Ultra Series 3, and Core Ultra 200V, and Qualcomm Snapdragon 8cx Gen 3 and Snapdragon X Series ship Pluton as the on-die TPM. Pluton&apos;s firmware is updated through Windows Update -- not through OEM-controlled SPI flash patches -- and Microsoft started delivering Rust-based Pluton firmware on 2024 AMD and Intel systems, with broader rollout ongoing [@learn-pluton].&lt;/p&gt;
&lt;p&gt;The architectural significance is twofold. The trust root is no longer a chip with its bus exposed to a trace-and-sniff attacker. The firmware update path is now a Microsoft-controlled channel rather than thirty different OEM-controlled channels. The same hardware root is what &lt;a href=&quot;https://paragmali.com/blog/bitlocker-on-windows-architecture-attacks-and-the-limits-of-/&quot; rel=&quot;noopener&quot;&gt;BitLocker&lt;/a&gt; depends on when it seals the Volume Master Key to a &lt;a href=&quot;https://paragmali.com/blog/the-tpm-in-windows-one-primitive-twenty-five-years-and-the-c/&quot; rel=&quot;noopener&quot;&gt;measured boot&lt;/a&gt; chain via TPM PCRs [@ms-bitlocker]. On Pluton, those PCR measurements live in-die rather than on a bus-exposed chip, and the sibling article &lt;em&gt;BitLocker on Windows&lt;/em&gt; traces what that buys and what it does not.&lt;/p&gt;

Apple Gatekeeper plus Notarization is a single-CA model. All Mac binaries that pass Gatekeeper are notarized by Apple, scanning happens server-side, and Apple&apos;s own notary signature is the trust root [@apple-gatekeeper]. Linux IMA-Appraisal expresses code identity as a per-host keyring of cryptographic measurements; the kernel evaluates a load against a policy stored in the same keyring [@linux-ima]. Android APK Signature Scheme v3 binds the APK to a per-app signing key with an explicit proof-of-rotation chain that lets a publisher rotate keys without breaking the app&apos;s identity [@apksigning-v3]. Windows is the only one of the four that accepts third-party CAs in user mode while reserving Microsoft roots for the kernel. The cost of pluralism is exactly the long tail of failure modes this article enumerates; the benefit is the freedom every Windows ISV has used since 1996 to ship without asking Microsoft&apos;s permission.
&lt;p&gt;Then came July 19, 2024.&lt;/p&gt;
&lt;p&gt;CrowdStrike&apos;s Falcon kernel driver loaded a malformed Channel File 291 update that triggered an out-of-bounds memory read inside &lt;code&gt;csagent.sys&lt;/code&gt; and raised an invalid page fault [@msft-crowdstrike-best-practices], bug-checking roughly 8.5 million Windows endpoints simultaneously [@ms-crowdstrike-blog]. The driver was correctly Microsoft-signed through the Hardware Developer Center attestation pipeline. Every code-identity layer in the stack -- KMCS, the cross-cert, the EV cert, the attestation key, even the Block List -- said yes. The thing that went wrong was not identity. It was that an identity-blessed driver, running in kernel mode, can fail in ways that take entire continents offline.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The CrowdStrike outage proves that a correctly-signed, attested kernel driver is still a planet-scale liability if its placement is wrong. Identity is not the only dimension. Where in the privilege hierarchy a binary runs is itself a dimension that signing cannot capture.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Microsoft&apos;s reaction was structural. On September 12, 2024, David Weston published the recap of the September 10 WESES summit Microsoft had hosted with its endpoint-security partners, committing to provide &quot;additional security capabilities outside of kernel mode&quot; so that EDR vendors could run their detection logic in user mode [@weston-2024].&lt;/p&gt;
&lt;p&gt;On June 26, 2025, the Windows Resiliency Initiative announced a private preview of the new endpoint security platform, scheduled for July 2025 delivery to selected MVI partners: Bitdefender, CrowdStrike, ESET, and SentinelOne [@weston-2025]. CrowdStrike&apos;s representative was Alex Ionescu, now its Chief Technology Innovation Officer -- the same Alex Ionescu whose 2013 Breakpoint talk publicly mapped PPL signer levels. The arc had closed in twelve years.&lt;/p&gt;
&lt;p&gt;MVI 3.0 -- the Microsoft Virus Initiative, version three -- adds Safe Deployment Practices as a contractual condition: staged rollouts, deployment rings, monitoring. The same playbook Microsoft itself follows for Windows updates after the 2024 outage [@msft-crowdstrike-best-practices].&lt;/p&gt;
&lt;p&gt;The conceptual move is the same one PPL made in 2013, projected one layer higher. Then: identity becomes a runtime ACL between processes. Now: identity-bound &lt;em&gt;placement&lt;/em&gt; (kernel mode versus user mode) becomes a trust dimension co-equal with identity-bound &lt;em&gt;signing&lt;/em&gt;. The question is no longer &quot;is this driver signed and on the allow list?&quot; The question is &quot;should code with this identity be running in this context at all?&quot;&lt;/p&gt;
&lt;p&gt;If even attested, signed, blessed kernel code can fail catastrophically, what could code identity in principle ever prove -- and what is provably out of reach?&lt;/p&gt;
&lt;h2&gt;Theoretical bounds and open problems&lt;/h2&gt;
&lt;p&gt;Two papers from the 1980s bound everything that followed.&lt;/p&gt;
&lt;p&gt;Fred Cohen&apos;s 1984 paper at IFIP-Sec, republished in &lt;em&gt;Computers &amp;amp; Security&lt;/em&gt; in 1987, proved that perfect virus detection is undecidable: there is no algorithm that, given an arbitrary program, can decide whether it is a virus [@cohen-1986]. Reputation systems are necessarily heuristic. The &quot;first 1,000 downloads&quot; gap -- the window where SmartScreen has not yet seen enough of a new binary to be confident -- is structural, not a tuning problem. You cannot close it by waiting harder.&lt;/p&gt;
&lt;p&gt;Ken Thompson&apos;s 1984 ACM Turing Award lecture, &quot;Reflections on Trusting Trust,&quot; made a different point about a different layer [@thompson-trusting-trust]. Thompson exhibited a compiler that, when used to build itself, inserted a backdoor into a target program; when used to build the compiler, propagated the backdoor invisibly to the next-generation binary. Signing what the compiler emitted never proved the compiler was unmodified. SLSA Level 3+ provenance, reproducible builds, hermetic build environments [@slsa-spec] push the bound back one level. They do not eliminate it.&lt;/p&gt;
&lt;p&gt;A third bound is Authenticode-specific. Asynchronous revocation, the property that lets pre-revocation timestamped signatures continue to verify forever, is the reason Stuxnet&apos;s drivers loaded after Realtek&apos;s certificate was revoked, and the reason every other stolen-key compromise has a window of cryptographic legitimacy [@symantec-stuxnet]. Synchronous global revocation would invalidate large catalogs of legitimate, archived, signed software whose signing certs have since expired. There is no fix inside the design.&lt;/p&gt;
&lt;p&gt;Pulled together, these bounds explain the persistent gap. Stolen-but-not-yet-revoked publisher keys are the same failure mode replayed three times in sixteen years: Stuxnet (2010, Realtek and JMicron), ASUS ShadowHammer (operation 2018, disclosed 2019, ASUSTeK production key), &lt;a href=&quot;https://paragmali.com/blog/when-your-password-manager-attacks-you-inside-the-bitwarden-/&quot; rel=&quot;noopener&quot;&gt;Bitwarden CLI&lt;/a&gt; (2026, npm publishing credential). The Pluton firmware-update pipeline is the most credible architectural response yet -- a Microsoft-controlled key-rotation channel that does not depend on OEM-side custody -- but it does not eliminate the class. It compresses the response window.&lt;/p&gt;
&lt;p&gt;The other open problem is identity for non-PE artifacts. The Authenticode hash and the WDAC publisher rule were designed for Portable Executable files; everything else gets uneven coverage. PowerShell &lt;code&gt;.ps1&lt;/code&gt; scripts can be signed and gated through Constrained Language Mode, which the runtime enters automatically when an AppLocker or App Control policy is in force [@learn-clm]. .NET assemblies have strong-name signatures, separate from Authenticode and explicitly not a security boundary; Microsoft&apos;s own documentation warns &quot;do not rely on strong names for security&quot; [@learn-strong-name].&lt;/p&gt;
&lt;p&gt;JIT-compiled code -- the most common shape of &quot;code&quot; in 2026 -- is signed only insofar as the JIT host is signed. The JIT itself produces unsigned bytes. Container images, WSL guests, AI model files, and (now) agent prompts all live outside the Authenticode universe entirely. Each is its own substrate, with its own emerging signing scheme, and the unification has not happened.&lt;/p&gt;
&lt;p&gt;$$\text{trust}_{2026}(\text{binary}) = \text{publisher}(\text{binary}) \land \text{provenance}(\text{build}) \land \text{placement}(\text{runtime}) \land \text{reputation}(\text{telemetry}) \land \neg \text{revoked}(\text{Microsoft})$$&lt;/p&gt;
&lt;p&gt;That conjunction is the 2026 verdict. None of its terms are sufficient on their own. Each was forced into existence by a failure of the term before. The arc from &quot;who launched this thread?&quot; in 1993 to that conjunction in 2026 is what thirty-three years of forced moves produced.&lt;/p&gt;
&lt;p&gt;What does the layered system look like in practice on a 2026 endpoint -- and what should an admin actually do?&lt;/p&gt;
&lt;h2&gt;Practical guide&lt;/h2&gt;
&lt;p&gt;Six concrete recommendations for a 2026 Windows fleet, each tied to a primary Microsoft Learn or MSRC source.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; On Windows 11 22H2 and later it is enabled by default. On Windows 10, Server, and downlevel Windows 11 builds, enable it explicitly through Settings &amp;gt; Privacy &amp;amp; security &amp;gt; Windows Security &amp;gt; Device security &amp;gt; Core isolation &amp;gt; Microsoft Vulnerable Driver Blocklist. HVCI must be on for full enforcement [@msft-driver-blocklist].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The published Microsoft baseline policies (&lt;code&gt;Default Windows&lt;/code&gt;, &lt;code&gt;Allow Microsoft&lt;/code&gt;, the Windows S Mode policy) are the right starting points. Run any custom policy in audit mode for a full reference-image cycle, mine the &lt;code&gt;Microsoft-Windows-CodeIntegrity/Operational&lt;/code&gt; event log for blocked loads, then promote to enforce. Pair with HVCI so the policy lives behind the secure-kernel boundary [@ms-appcontrol]. Deploy through Microsoft Intune (or your MDM of choice), Configuration Manager, or Group Policy -- App Control policy distribution is a first-class managed-endpoint scenario rather than a per-machine hand edit [@learn-appcontrol-deployment].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; On Secure Boot systems the value is mirrored into a UEFI variable, so registry-only attackers cannot turn it off. Verify with &lt;code&gt;Get-ItemProperty -Path &apos;HKLM:\SYSTEM\CurrentControlSet\Control\Lsa&apos; -Name RunAsPPL&lt;/code&gt; and the corresponding &lt;code&gt;RunAsPPLBoot&lt;/code&gt; UEFI variable [@itm4n-runasppl].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; SmartScreen alone is bypassed by container-format MOTW stripping. Pair it with ASR rule &lt;code&gt;01443614-cd74-433a-b99e-2ecdc07bfc25&lt;/code&gt;, which gates execution on prevalence, age, or a trusted list, independently of MOTW [@learn-smartscreen] [@learn-asr-reference].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The Package SID is a free identity for any internal app you ship as MSIX. ACL sensitive resources to it, declare capabilities explicitly in the manifest, and let the AppContainer SID enforce the ACL at the kernel boundary [@ms-package-identity] [@ms-msix].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Treat your code-signing key like a credential, not a build artifact. Rotate the EV cert, revoke the old one, notify customers, and -- if the binary already shipped -- request the offending hash on the Driver Block List or the ASR rule [@msft-driver-reporting]. The Bitwarden CLI 2026 incident took 93 minutes from release to containment, with rollback continuing for several hours afterward [@bitwarden-statement]; have the playbook ready before you need it.&lt;/p&gt;
&lt;/blockquote&gt;

```js
function loadDecision({ signed, signerLevel, motwed, onBlockList, allowedByAppControl, smartScreenVerdict }) {
  if (onBlockList) return &apos;BLOCK -- Microsoft veto, signature ignored&apos;;
  if (signed === false &amp;amp;&amp;amp; allowedByAppControl === false) return &apos;BLOCK -- unsigned, App Control denies&apos;;
  if (signerLevel === &apos;WinTcb&apos; || signerLevel === &apos;WinSystem&apos;) return &apos;LOAD -- protected process&apos;;
  if (allowedByAppControl === false) return &apos;BLOCK -- App Control deny&apos;;
  if (motwed &amp;amp;&amp;amp; smartScreenVerdict === &apos;unknown&apos;) return &apos;WARN -- SmartScreen, user gate&apos;;
  if (motwed &amp;amp;&amp;amp; smartScreenVerdict === &apos;malicious&apos;) return &apos;BLOCK -- SmartScreen&apos;;
  return &apos;LOAD&apos;;
}
console.log(loadDecision({
  signed: true, signerLevel: &apos;Authenticode&apos;,
  motwed: true, onBlockList: false,
  allowedByAppControl: true, smartScreenVerdict: &apos;good&apos;,
}));
```
The decision tree is the practical mental model. Every branch of it is the consequence of one of the failures this article tracks.

No. A signature attests *publisher identity* and *binary integrity*. It does not attest safety. Microsoft trust is a separate, runtime claim expressed through the Driver Block List, App Control policies, and Defender reputation -- evaluated continuously, even on signatures Microsoft itself once minted [@msft-driver-blocklist].

Extended Validation Authenticode signing vets organisational identity through an audited issuance process and mandates that the private key live in a hardware security module; the publisher&apos;s signature is the trust root. Attestation signing is Microsoft&apos;s lighter-weight pipeline for kernel drivers: the publisher submits an EV-signed binary to the Hardware Developer Center, Microsoft re-signs with its own attestation key, and the result is delivered back. Attestation-signed drivers are not WHQL tested and are not distributed via retail Windows Update [@learn-driver-signing-offerings] [@ms-attestation-signing].

MOTW plus low prevalence. SmartScreen sees a binary it has not observed enough times in the global telemetry to be confident, on a file marked as having been downloaded from the internet. Sign the binary with an EV certificate, accumulate downloads on a stable hash, and the warning fades. Internal binaries can have MOTW stripped at deployment time if your distribution channel is itself trusted [@learn-smartscreen].

No. AppLocker is the Windows 7-era policy mechanism with rules in path/publisher/hash form, no kernel coverage, and no virtualization-based protection of the policy itself. App Control for Business -- formerly Windows Defender Application Control -- is the publisher-rule Code Integrity policy mechanism with HVCI enforcement at the kernel boundary. Microsoft recommends App Control for new deployments and keeps AppLocker for compatibility [@ms-applocker] [@ms-appcontrol].

LSASS is running as a Protected Process Light at the `Lsa` signer level. Signer-level gating sits *above* the token DACL check. Even a SYSTEM-token caller with `SeDebugPrivilege` gets a process handle with `PROCESS_VM_READ` and `PROCESS_VM_WRITE` stripped, because PPL strips access masks before the DACL evaluation. Disable LSA Protection (`RunAsPPL=0`) on a test machine and the same call succeeds [@itm4n-runasppl] [@scrt-ppl-bypass].

Only if the publisher&apos;s signing-key custody and build pipeline are themselves uncompromised. Stuxnet (stolen Realtek and JMicron keys, 2010), ASUS ShadowHammer (compromised production signing pipeline, operation 2018 / disclosed 2019), and the Bitwarden CLI npm incident (2026) all produced cryptographically valid signatures on attacker-controlled bytes [@symantec-stuxnet] [@securelist-shadowhammer] [@bitwarden-statement]. SLSA-level build provenance and Pluton-rooted attestation are the architectural responses; neither is yet universally deployed [@slsa-spec] [@learn-pluton].
&lt;h2&gt;Where this is going&lt;/h2&gt;
&lt;p&gt;Pluton-rooted device attestation, MVI 3.0&apos;s user-mode security platform, SLSA build provenance, and the post-CrowdStrike push to make placement a first-class identity attribute are all in motion in 2026 [@weston-2025] [@slsa-spec]. The follow-on articles -- Driver Block List in production, App Control with HVCI on real fleets, Secure Boot internals, the Pluton firmware-update channel -- are the operational complement to the conceptual story this article has told.&lt;/p&gt;
&lt;p&gt;The arc that began with Windows NT 3.1 having no answer to &quot;who is this code?&quot; now has eight overlapping answers, each insufficient on its own. Identity in 2026 is a multi-layered claim about a binary&apos;s publisher, its build provenance, its runtime placement, and its reputation, evaluated continuously while the code is running. The arc from 1993&apos;s &quot;who launched this thread?&quot; to 2026&apos;s &quot;is this signed binary, in this placement, with this build provenance, on Microsoft&apos;s curated honour list, today, on this hardware-attested device?&quot; is the answer thirty-three years of forced moves produced -- and the question the next thirty-three years will keep asking, because none of the bounds Cohen and Thompson proved have moved.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;app-identity-in-windows&quot; keyTerms={[
  { term: &quot;Authenticode&quot;, definition: &quot;PE-attached PKCS#7 SignedData that names the publisher and detects tampering. Names the publisher, not the code.&quot; },
  { term: &quot;Kernel-Mode Code Signing (KMCS)&quot;, definition: &quot;Vista x64 policy that refuses to load unsigned kernel drivers; chain-to-Microsoft requirement post-2015.&quot; },
  { term: &quot;Protected Process Light (PPL)&quot;, definition: &quot;Windows 8.1 attribute that mediates inter-process access by signer level; LSASS-as-PPL defeats user-mode credential dumpers.&quot; },
  { term: &quot;Package SID&quot;, definition: &quot;Cryptographic application identity (S-1-15-2-...) derived from the MSIX manifest publisher; first-class principal in ACLs and capability checks.&quot; },
  { term: &quot;App Control for Business&quot;, definition: &quot;Publisher-rule Code Integrity policy formerly called WDAC; enforced by HVCI; ships in S Mode and Windows 11 SE by default.&quot; },
  { term: &quot;Mark of the Web (MOTW)&quot;, definition: &quot;Zone.Identifier alternate data stream that records a file&apos;s origin; input to SmartScreen reputation.&quot; },
  { term: &quot;Vulnerable Driver Block List&quot;, definition: &quot;Microsoft-curated WDAC-format deny list shipped quarterly; default-on since Windows 11 22H2; the operational expression of &apos;signed != trusted&apos;.&quot; },
  { term: &quot;Pluton&quot;, definition: &quot;On-die Microsoft security processor in AMD Ryzen 6000+, Intel Core Ultra 200V, and Qualcomm 8cx Gen 3; firmware updated through Windows Update.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>windows-security</category><category>authenticode</category><category>code-signing</category><category>protected-process-light</category><category>appcontainer</category><category>app-control</category><category>driver-blocklist</category><author>noreply@paragmali.com (Parag Mali)</author></item></channel></rss>