# VBS Trustlets: What Actually Runs in the Secure Kernel

> A field guide to Virtualization-Based Security trustlets on Windows 11: the five gates a binary passes to become one, the inbox roster, and where the model ends.

*Published: 2026-05-10*
*Canonical: https://paragmali.com/blog/vbs-trustlets-what-actually-runs-in-the-secure-kernel*
*License: CC BY 4.0 - https://creativecommons.org/licenses/by/4.0/*

---
<TLDR>
**Trustlets are the user-mode processes Microsoft places in Virtual Trust Level 1** to hold the secrets a SYSTEM-privilege attacker on the Windows kernel must never reach: NTLM hashes, Kerberos tickets, biometric templates, virtual TPM keys, and (in 2025-2026) just-in-time admin tokens. A binary becomes a trustlet by passing five gates at load time: a process attribute, two specific signing EKUs at Signature Level 12, a `.tpolicy` PE section containing `s_IumPolicyMetadata`, a Trustlet Instance GUID, and a stripped-down loader path. Once loaded, the trustlet talks to the rest of Windows over ALPC, services an agent process in VTL0, and uses only 48 of NT's roughly 480 syscalls. The Hyper-V hypervisor refuses to map its pages into VTL0. That is what "isolated" means.
</TLDR>

## 1. Four Locked Rooms

It is 3:14 a.m. and a red-team operator on a fully patched Windows 11 25H2 box has, after eight hours of careful work, achieved the prize: a SYSTEM-privilege write primitive in the NT kernel. For two decades that has been the moment when the engagement ends and the report writes itself. SYSTEM in the kernel meant every process, every page, every secret. Game over.

It is not game over.

The operator's target list has four items on it. The [NTLM hashes](https://paragmali.com/blog/ntlmless-the-death-of-ntlm-in-windows) and Kerberos Ticket-Granting Tickets sitting in `lsass.exe`. The user's fingerprint template, in whatever process the [Windows Hello](https://paragmali.com/blog/your-face-is-not-your-password-inside-windows-hellos-hardwar) biometric pipeline puts it. The just-in-time admin token that Administrator Protection issued thirty seconds ago. The keys of the four Hyper-V virtual machines running on the box, including the one hosting the user's corporate VPN. Four secrets. Four user-mode processes. And on this 2026 machine, four locked rooms whose pages the operator's kernel write primitive cannot touch and whose contents the operator's kernel does not have permission to ask.

Those four processes are *trustlets*. They run in a different kernel from the one the operator just compromised, on a different virtual trust level enforced by a hypervisor running underneath both. The operator owns the NT kernel; the NT kernel does not own them. That sentence is what changed in 2015, and the rest of this piece is what it actually means.

This is not "Microsoft hid the memory better." It is not obfuscation, not a clever access-control rule, not a kernel mitigation that the next CVE will erase. It is an architectural relocation: the user-mode processes that hold the secrets no longer live in the operating system the attacker compromised. The hypervisor refuses to map their pages into Virtual Trust Level 0 ("VTL0"), and the operator's kernel is in VTL0.

> **Key idea:** Four user-mode processes survive a SYSTEM kernel write primitive on a 2026 Windows 11 box. That is what changed in 2015, and trustlets are the reason.

The promise of this piece is to explain trustlets at the level of "what does `LsaIso.exe` actually do, how is it built, how does it talk to the rest of the system, and where does the model end." Not at the level of "VBS isolates them." By the end, four locked rooms will have become something you can name, list, audit, and reason about. Where the public record runs out (some trustlet binary names and IDs are not on Microsoft's published list as of mid-2026), the piece will say so, and it will tell you what the actual records look like instead of inventing replacements.

So how does a user-mode process become unreachable from SYSTEM-in-the-NT-kernel? The answer is not new. It begins, like much of operating-system security, at MIT in the early 1970s.

## 2. The User-Mode-In-A-Higher-Privilege Problem

In March 1972 Michael Schroeder and Jerome Saltzer published a paper in the *Communications of the ACM* describing an unusual machine. The Multics team at MIT had been wrestling with a question that does not, at first glance, sound like a security question. What should happen when a user program calls a password-checking routine that needs to read the system password file? The user program must not be allowed to read that file directly. The routine must be allowed to read it. The two pieces of code run in the same process. How does the machine know which one is asking?

Schroeder and Saltzer's answer was eight hardware-enforced rings of privilege, with each segment in memory carrying a *ring bracket* in its descriptor word, and with cross-ring calls validated automatically by the hardware [@multicians-protection] [@multicians-papers]. The hardware that shipped this design was the Honeywell 6180 in 1973 [@wiki-protection-ring]. The pattern matters more than the gear. Some user code needed to run with more privilege than its caller and less privilege than the kernel. Multics arranged eight such layers from user code at the outermost ring down to the supervisor at ring 0 [@wiki-multics].

<Definition term="Trusted Computing Base (TCB)">
The set of hardware, firmware, and software whose correct operation is necessary to enforce a security policy. If any component of the TCB can be subverted, the policy can be subverted. The smaller the TCB, the easier it is to audit; the larger it is, the more places an attacker can find a foothold.
</Definition>

A few years later at Carnegie Mellon, William Wulf, Roy Levin, and the Hydra team took a different swing at the same problem. Hydra was a capability-based, object-oriented microkernel that ran on the C.mmp multiprocessor between 1971 and 1975 [@wiki-hydra]. Where Multics multiplied rings, Hydra multiplied *vocabulary*: every protected resource was an object addressable only through capability tokens, and security-critical subsystems lived not inside the kernel but as user-mode capability-holders trusted by the kernel to enforce their own policy. Levin et al.'s 1975 SOSP paper "Policy/Mechanism Separation in HYDRA" gave the design its slogan, and that slogan has outlived the system that produced it [@levy-capabook].

<Sidenote>Hydra's "policy versus mechanism" phrasing still appears verbatim in modern object-capability literature, in the design discussion of WebAssembly's component model, and in seL4's published rationale.</Sidenote>

For two decades the L4 family answered "but is this fast enough to be practical?" Jochen Liedtke's 1993 prototype, hand-coded in i386 assembly, ran inter-process communication twenty times faster than Carnegie's Mach microkernel [@wiki-l4]. His 1995 SOSP paper "On µ-Kernel Construction" was inducted into the ACM SIGOPS Hall of Fame in 2015 and is the foundational statement of the minimal-kernel, maximal-user-mode-trusted-services design. By the mid-2000s OKL4, a commercial L4 derivative, was shipping in billions of mobile devices [@wiki-l4].

<Definition term="Microkernel">
A kernel design that pushes as much functionality as possible out of kernel mode and into user-mode "servers" that communicate via inter-process calls. Filesystem code, networking stacks, even device drivers can run as user-mode processes. The kernel itself shrinks to a few thousand lines of code that schedule processes, route messages, and enforce memory isolation, and nothing else.
</Definition>

In 2009 the lineage reached an end that nobody had reached before. Gerwin Klein, Kevin Elphinstone, Gernot Heiser and the NICTA team published *seL4: Formal Verification of an OS Kernel* at SOSP, reporting a machine-checked proof of functional correctness from a formal specification down to the C implementation [@sel4-sosp-paper]. seL4 was open-sourced in July 2014 [@wiki-sel4]; the seL4 Foundation's About page states plainly that seL4 stands out because of its thoroughgoing formal verification [@sel4-about]. A kernel of about ten thousand lines of C, proved correct down to the byte, with sub-microsecond inter-process calls.

Schroeder and Saltzer asked it for hardware rings. Hydra asked it for capabilities. Liedtke asked it for inter-process speed. Klein and Heiser asked it of formal logic. The question stayed the same: how do you let some user-mode code hold a secret that some other code in the same machine is not allowed to read, when both pieces of code are scheduled by the same kernel? The Multics answer was rings. The Hydra answer was capabilities. The L4 answer was a tiny kernel plus IPC. The seL4 answer was a tiny kernel plus IPC, plus a proof.

The Microsoft answer, in July 2015, was a hypervisor.

<Mermaid caption="The user-mode-in-a-higher-privilege lineage. Each generation moves the privilege ceiling up so that the security-critical user-mode code lives below it. Timeline rows, in order: Multics rings (Schroeder and Saltzer 1972), Hydra capabilities (Wulf et al. 1974-1975), L4 microkernel (Liedtke 1993), Windows NT (Cutler 1993), Vista Protected Processes (2007), seL4 verification (Klein et al. 2009), Windows 8.1 PPL (2013), Windows 10 IUM and trustlets (2015), VBS Enclaves for third parties (2024), Administrator Protection rollout (2025-2026).">
timeline
    title User-mode-in-higher-privilege lineage
    1972 : Multics 8-ring hardware
         : Honeywell 6180 ring brackets
    1974 : Hydra capabilities
         : Policy vs mechanism
    1993 : L4 microkernel
         : Fast user-mode IPC
         : Windows NT ships ring 0/3
    2007 : Vista Protected Processes
    2009 : seL4 verification
    2013 : Windows 8.1 PPL
    2015 : Windows 10 IUM ships
         : Trustlets 0-3 enumerated
    2024 : VBS Enclaves go third-party
    2026 : Administrator Protection
</Mermaid>

If the architectural answer was already in the 1970s academic literature, why did Microsoft wait until 2015 to ship it on Windows? Because three earlier attempts to ship user-mode isolation on Windows -- under three different names, in three different decades -- each failed in the same way.

## 3. Three Tries Before Trustlets

Before 2015 Microsoft tried three times to ship user-mode isolation on Windows. All three shipped in production. All three failed in the same way.

### 2007: Vista Protected Processes

Windows Vista introduced *Protected Processes* in January 2007. The motivation was not credential security; it was Digital Rights Management. The Protected Media Path required a set of binaries -- `audiodg.exe`, `mfpmp.exe`, and a handful of others involved in Blu-ray playback -- whose memory non-protected processes could not read, whose threads could not be debugged from outside, and whose DLL imports could not be hijacked at runtime [@wiki-pmp]. The kernel enforced these rules by refusing to grant the relevant access masks (`PROCESS_VM_READ`, `PROCESS_VM_WRITE`, `THREAD_ALL_ACCESS`) to handles requested from non-protected processes.

The mechanism was elegant. The threat model was not. Alex Ionescu announced in January 2007 -- within weeks of Vista's general availability -- that he had developed a bypass method for the Protected Media Path [@wiki-pmp]. The same NT kernel that enforced the protection was the kernel an attacker would compromise to bypass it. A signed kernel driver, or any of the long stream of subsequent kernel vulnerabilities, would walk straight through.

### 2012: AppContainer and the LowBox token

Windows 8 introduced [AppContainer](https://paragmali.com/blog/who-is-this-code----the-quiet-33-year-reinvention-of-app-ide) process isolation in October 2012, originally to support Universal Windows Platform store apps [@wiki-uwp]. Each AppContainer process ran with a *LowBox* token: a low-integrity primary token plus a SID, plus a set of named capabilities (`internetClient`, `picturesLibrary`, and so on), plus a per-AppContainer named-object subtree under `\Sessions\<N>\AppContainerNamedObjects\<SID>`. The NT kernel checked the SID against object DACLs at every object access, denying access by default and granting it only where the AppContainer's declared capabilities matched the requested operation.

This is a Hydra-style capability lattice bolted onto NT's existing access-control system. It is a useful sandboxing primitive for *untrusted* code, and modern browsers (the Edge renderer, the Chromium sandbox) consume it for exactly that purpose. It is not a defence against an attacker who already has kernel code execution. In August 2018 James Forshaw at Google Project Zero published an exploit for Issue 1550 that turned the AppContainer named-object namespace itself into an arbitrary-directory-creation primitive [@forshaw-2018]:

> The AppInfo service... calls the undocumented API CreateAppContainerToken... As the API is called without impersonating the user... the object directories are created with the identity of the service, which is SYSTEM.

A low-integrity caller could direct that SYSTEM-owned creation at any directory it pleased and use the result to elevate. The lattice held; the lattice's *enforcer* did not. AppContainers continue to ship, doing their actual job (sandboxing untrusted code) reasonably well. They were never going to answer the trustlet question (isolating trusted code from a compromised kernel) because they are NT-kernel-enforced.

### 2013: Protected Process Light (PPL) and `RunAsPPL`

Windows 8.1 generalised the Vista mechanism into a *signer-level lattice*. Each protected process now had a two-dimensional protection level: a signer (`WinTcb`, `Windows`, `Antimalware`, `Authenticode`, others) and a protection type (`PsProtectedSignerTcb`, `PsProtectedSignerAuthenticode`, others). Higher-signer processes could manipulate lower-signer ones; same-signer processes could not see across the line. The first canonical use case was anti-malware services that registered an Early Launch Anti-Malware (ELAM) driver and then ran their user-mode service as a Protected Process Light [@msdocs-protecting-am].

<Definition term="Protected Process Light (PPL)">
A Windows 8.1 process attribute that constrains which other processes can request high-privilege access to it. PPL extends the Vista Protected Process mechanism with a signer-level lattice (WinTcb > Windows > Antimalware > Authenticode > None) and a protection type. The NT kernel enforces the rules. LSASS running as a PPL is the canonical use case, exposed to administrators via the `RunAsPPL` registry value [@itm4n-runasppl].
</Definition>

Alex Ionescu's 2013 essay "The Evolution of Protected Processes Part 3" documented the resulting Signing Levels table -- Signature Level 12 named "Windows," Level 13 "Windows Protected Process Light," Level 14 "Windows TCB" [@ionescu-ppp3] [@ionescu-ppp1]. That table is the load-bearing reference for every later trustlet design: every IUM binary on a 2026 Windows machine must satisfy *at least* Signature Level 12. Microsoft shipped LSASS-as-PPL ("LSA Protection," exposed through the `RunAsPPL` registry value under `HKLM\SYSTEM\CurrentControlSet\Control\Lsa`) as the canonical example: a way to keep the lower-privileged half of an administrator's session from reading credential material out of LSASS memory.

It worked, for some values of "worked." It worked against pass-the-hash tools that ran as an ordinary administrator without a signed kernel driver. It did not work against an attacker willing to load any signed driver, and -- as became clear in 2021 -- it did not work even from userland once the bypass class was identified.

In August 2018 James Forshaw, in the same Project Zero post that exposed the AppContainer issue, also documented a `DefineDosDevice` plus Known-DLL hijack technique. By creating a symbolic link in the NT object manager namespace that aliased a Known DLL section, an administrative caller could induce a target PPL process to load arbitrary code at the next image load [@forshaw-2018]. In 2021 the researcher who blogs as itm4n weaponised the same primitive into `PPLdump`, a fileless userland tool that dumped `lsass.exe` memory from an administrator command prompt with no kernel driver involved [@itm4n-runasppl]. itm4n's writeup is honest about what this means:

> Like any other protection though, it is not bulletproof and it is not sufficient on its own, but it is still particularly efficient.

Microsoft closed the `DefineDosDevice` corner of this class in Windows 10 21H2 build 19044.1826, shipped in July 2022 [@itm4n-end-of-ppldump]. That is nine years of mainstream PPL deployment during which the LSASS-as-PPL credential boundary was bypassable without ring 0 access at all.

### The pattern

Three primitives. Three different protection mechanisms. One common failure mode.

| Mechanism | Year | Enforcer | Threat model | Defeated by | Status today |
|-----------|------|----------|---------------|--------------|--------------|
| Vista Protected Process | 2007 | NT kernel | Untrusted user code reading DRM-protected media buffers | Signed kernel drivers; Ionescu Jan 2007 [@wiki-pmp] | Superseded by PPL for non-DRM use |
| AppContainer / LowBox | 2012 | NT kernel | Untrusted store-app code escaping its capability sandbox | SYSTEM-owned directory creation via service impersonation [@forshaw-2018] | Active for sandboxing untrusted code; not a trustlet substitute |
| Protected Process Light (`RunAsPPL`) | 2013 | NT kernel | Userland administrative attacker reading LSASS credential material | `DefineDosDevice` plus Known-DLL hijack; PPLdump 2021 [@itm4n-runasppl] | Active as defence-in-depth; closed in build 19044.1826, July 2022 |
| Isolated User Mode / trustlets | 2015 | Hypervisor + Secure Kernel | VTL0 kernel attacker reading user-mode secrets | Secure-call interface bugs; agent-side RPC residual [@amar-bh2020] | Active; subject of this article |

Three rows, one diagnosis. Every NT-kernel-enforced isolation primitive shares the attacker's TCB. Improving the lattice the NT kernel enforces does not move the security ceiling, because the NT kernel itself can be compromised; once it is, any policy decision the NT kernel makes is the attacker's policy decision. Microsoft's own VBS hardware-requirements page admits the diagnosis verbatim:

<PullQuote>
"VBS uses hardware virtualization and the Windows hypervisor to create an isolated virtual environment that becomes the root of trust of the OS that assumes the kernel can be compromised." -- Microsoft, OEM VBS hardware requirements [@msdocs-oem-vbs]
</PullQuote>

> **Note:** `RunAsPPL` is useful defence in depth. It is not, and has never been, a substitute for Credential Guard. itm4n's 2021 PPLdump release was the proof for the userland half of that statement; signed-driver loaders are the proof for the ring-zero half. If your threat model includes a determined attacker with administrative rights, Credential Guard is the boundary; PPL is the speed bump in front of it [@itm4n-runasppl].

If every primitive the NT kernel enforces shares the attacker's TCB, the kernel that enforces user-mode isolation has to be a *different* kernel. In July 2015 Microsoft shipped one.

## 4. July 2015: The Hypervisor Becomes the Arbiter

On 29 July 2015 Microsoft shipped Windows 10 build 10240 [@wiki-win10-history]. Two new ideas shipped with it. The first was Hyper-V's hypervisor running *underneath* the NT kernel even on a laptop, not just on a server hosting virtual machines [@wiki-hyperv]. The second was a separate kernel running alongside the NT kernel, at a different Virtual Trust Level. Together those two ideas produce a substrate where the long-time equation "SYSTEM kernel write primitive equals every secret in user-mode memory" is no longer true.

<Definition term="Virtual Trust Level (VTL)">
A hypervisor-managed privilege axis added on top of x86's existing ring 0 / ring 3 split. Each VTL has its own kernel mode and its own user mode. Higher VTLs can read and write lower-VTL memory; lower VTLs cannot read or write higher-VTL memory at all. The Hyper-V Top-Level Functional Specification reserves up to 16 VTLs; the current Hyper-V implementation defines `#define HV_NUM_VTLS 2` [@msdocs-vsm].
</Definition>

The Hyper-V Top-Level Functional Specification states the rule directly: *"VSM achieves and maintains isolation through Virtual Trust Levels (VTLs)... Architecturally, up to 16 levels of VTLs are supported; however a hypervisor may choose to implement fewer than 16 VTL's. Currently, only two VTLs are implemented"* [@msdocs-vsm]. The NT kernel runs in VTL0 ring 0; user-mode applications run in VTL0 ring 3. The [Secure Kernel](https://paragmali.com/blog/when-system-isnt-enough-the-windows-secure-kernel-and-the-en) runs in VTL1 ring 0; trustlets run in VTL1 ring 3. Each VTL transition takes the CPU through a VMEXIT and back, with VMCS save and restore on each crossing [@quarkslab-virtual-journey].

<Sidenote>The architectural cap of sixteen VTLs is in the published specification but is not deployed. Stocking the unused slots would require both hypervisor changes and a new design for who manages the additional kernel images. The two-VTL design is the entire shipped product.</Sidenote>

Quarkslab's reverse-engineering team put the practical consequence in one sentence in their IUM-debugging writeup: *"VTL0 is the Normal World, where the traditional kernel-mode and user-mode code run in ring 0 and ring 3, respectively. On top of that, a new world appears: VTL1 is the privileged Secure World, where the Secure Kernel runs in ring 0, and a limited number of IUM processes run in ring 3. Code running in VTL0, even in ring 0, cannot access the higher-privileged VTL1"* [@quarkslab-debug-ium].

That sentence is the architectural fact the whole article rests on. The hypervisor configures each guest physical page's permissions on a per-VTL basis using the CPU's Second Level Address Translation tables. A page can be readable from VTL0 and VTL1, readable from VTL1 only, or readable from neither.<MarginNote>On Intel hardware, the per-VTL permissions are implemented with Extended Page Tables (EPT); on AMD they use Nested Page Tables (NPT). The hypervisor keeps the per-VTL EPT/NPT entries in its own memory, not in the guest's.</MarginNote>

<Definition term="Second Level Address Translation (SLAT)">
The hardware mechanism (Intel EPT, AMD NPT) that lets a hypervisor define page-level read, write, and execute permissions independent of the guest's own page tables. With VTLs, SLAT entries are per-VTL: a page's permissions when the CPU is executing VTL1 code can differ from the same page's permissions when the CPU is executing VTL0 code. A SYSTEM-privilege VTL0 attacker who edits the NT kernel's page tables cannot change the VTL1-side permissions, because those live in hypervisor-managed structures that VTL0 page-table writes do not touch.
</Definition>

<Mermaid caption="VTL0 versus VTL1. The NT kernel and ordinary user-mode applications share Virtual Trust Level 0. The Secure Kernel and Isolated User Mode processes (trustlets) share Virtual Trust Level 1. The Hyper-V hypervisor mediates both, with per-VTL SLAT permissions enforcing that VTL0 code, even at ring 0, cannot read VTL1-only pages.">
flowchart LR
    subgraph VTL0["VTL0 (Normal World)"]
        ring3_0["Ring 3: lsass.exe, vmwp.exe, user apps"]
        ring0_0["Ring 0: NT kernel + signed drivers"]
        ring3_0 --> ring0_0
    end
    subgraph VTL1["VTL1 (Secure World)"]
        ring3_1["Ring 3: LsaIso.exe, vmsp.exe, trustlets"]
        ring0_1["Ring 0: Secure Kernel (securekernel.exe)"]
        ring3_1 --> ring0_1
    end
    VTL0 -. ALPC over agent ALPC port .-> VTL1
    VTL1 -. read VTL0 memory .-> VTL0
    hv["Hyper-V hypervisor: per-VTL SLAT permissions"]
    VTL0 --> hv
    VTL1 --> hv
</Mermaid>

The VTL hierarchy is not symmetric. VTL1 code can read VTL0 memory; that is how a trustlet can dispatch the contents of an `lsass.exe` RPC request the moment after VTL0 wrote it. VTL0 code cannot read VTL1 memory under any condition the hypervisor permits. A kernel write primitive in VTL0 lets the attacker corrupt the NT kernel's data structures, modify drivers, and walk every VTL0 process's pages. The attacker can do every one of those things and not be one byte closer to the contents of `LsaIso.exe`.

Microsoft's IUM documentation at Windows 10 RTM named two trustlets explicitly: **Trustlet ID 0 = the Secure Kernel Process** (hosts Device Guard and Hypervisor-protected Code Integrity policy decisions), and **Trustlet ID 1 = `LSAISO.EXE`** (Credential Guard's isolated LSA, holding NTLM hashes and Kerberos Ticket-Granting Tickets out of VTL0 reach). Two more (IDs 2 and 3, covered in §6) also shipped on the RTM image and were enumerated a week later by Ionescu's Black Hat reverse-engineering [@msdocs-ium] [@ionescu-bh2015]. Microsoft Learn's IUM page introduces the vocabulary the rest of this piece will use:

> Trustlets (also known as trusted processes, secure processes, or IUM processes) are programs running as IUM processes in VSM... With VSM enabled, the Local Security Authority (LSASS) environment runs as a trustlet.

A week after Windows 10 shipped, on 5 August 2015, Alex Ionescu walked into a Black Hat USA briefing room in Mandalay Bay and reverse-engineered the entire thing in front of an audience [@ionescu-bh2015-infocondb]. His talk, "Battle of the SKM and IUM: How Windows 10 Rewrites OS Architecture," is the canonical first public account of the trustlet model and the source from which Microsoft's own later documentation borrows terminology one for one [@ionescu-bh2015]. Almost every concrete fact in the next section -- the syscall allow-list, the EKUs, the `.tpolicy` section, the Trustlet Instance GUID -- traces back to that single deck.

Now we know what world a trustlet lives in. What architecturally *is* one?

## 5. The Five Gates

A trustlet is not a special process *class* the way a Protected Process is. It is an ordinary Portable Executable binary that has been loaded under five very specific conditions. Walk through them once and you will be able to recognise a trustlet in a `dumpbin /headers` listing. The status is mechanical, not categorical. Chapter 9 of *Windows Internals, Seventh Edition, Part 2* (Allievi, Russinovich, Ionescu, Solomon) covers the same architecture from the kernel-team side as a reference complement to Ionescu's BH2015 reverse-engineering [@windows-internals-7e-pt2].

<Definition term="Trustlet">
A Windows user-mode process that runs in Virtual Trust Level 1 user mode (ring 3 of the Secure World), scheduled by the Secure Kernel and isolated from VTL0 by Hyper-V's per-VTL SLAT enforcement. A binary becomes a trustlet only if it satisfies five load-time conditions: a process attribute, two signing EKUs at Signature Level 12, a `.tpolicy` PE section containing `s_IumPolicyMetadata`, a Trustlet Instance GUID, and a stripped-down loader path. Trustlets are sometimes also called "trusted processes," "secure processes," or "IUM processes" [@msdocs-ium].
</Definition>

<Definition term="Isolated User Mode (IUM)">
The user-mode environment of Virtual Trust Level 1. IUM is, structurally, ring 3 of VTL1. Its inhabitants are trustlets; its kernel is the Secure Kernel; its system-call surface is approximately one-tenth of NT's. Quarkslab's IUM-debugging writeup describes IUM as the place where *"a limited number of IUM processes run in ring 3"* of VTL1; Microsoft's Win32 documentation describes the same architectural placement with different wording [@quarkslab-debug-ium] [@msdocs-ium].
</Definition>

### Gate 1: the process attribute

VTL0 user-mode code cannot call `CreateProcess` and produce a trustlet. The Win32 API does not expose the necessary primitive. A trustlet is born via a direct `NtCreateUserProcess` syscall that carries a `PsAttributeSecureProcess` attribute with a 64-bit Trustlet ID. Only callers that already live in VTL1, or callers in VTL0 that hold a specific brokering capability, can request that attribute and have the Secure Kernel honour it [@ionescu-bh2015].

This is intentional. The Win32 layering is one of the surfaces an attacker can compromise, so the trustlet boot path bypasses it. There is no "trustlet via shell" -- not for an administrator, not for SYSTEM, not for the Secure Kernel itself other than through the documented internal path.

### Gate 2: two EKUs at Signature Level 12

The binary must be signed with a certificate chain that contains two specific Enhanced Key Usage identifiers, and the resulting Signing Level must be 12 or higher. From Ionescu's BH2015 deck verbatim: *"They must have a Signature Level of 12... This means they must have the Windows System Component Verification EKU (1.3.6.1.4.1.311.10.3.6)... They must have the IUM EKU 1.3.6.1.4.1.311.10.3.37"* [@ionescu-bh2015].

<Definition term="Enhanced Key Usage (EKU)">
An X.509 certificate extension that restricts which purposes a certificate can be used for. An EKU is an object identifier (OID); a code-signing certificate that claims an OID of `1.3.6.1.4.1.311.10.3.6` is asserting it is valid for the "Windows System Component Verification" purpose. The Windows code-integrity subsystem (`ci.dll`) checks the requested EKU against the actual certificate at signature time and refuses to load the image if the EKU is missing or the certificate is not chained to a trusted root [@ionescu-ppp3].
</Definition>

Both EKUs are required. The Windows System Component Verification EKU establishes the binary as a Microsoft-signed Windows component. The IUM EKU asserts the binary's *intent* to load as a trustlet. A PPL EKU may sit on top, layering the PPL signer-level check on the trustlet check, but the two-EKU minimum is what Signing Level 12 enforces.

<Sidenote>The system-component EKU check is skipped when both Test Signing is enabled and the local machine trusts the Microsoft Test Root. That is the exact attack class Ionescu names verbatim in the BH2015 deck: "compromise the platform via Test Signing" disables the signing gate that defines trustlet identity.</Sidenote>

### Gate 3: the `.tpolicy` section and `s_IumPolicyMetadata`

Every trustlet image must contain a PE section named `.tpolicy` marked `IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ`. The section must export the symbol `s_IumPolicyMetadata`, a structure with three required components: a version byte set to 1, a 64-bit Trustlet ID that must match the one the process attribute requested, and a per-trustlet policy table containing entries for ETW (event tracing), debug permissions, crash-dump key release, and other trustlet-specific runtime knobs [@ionescu-bh2015].

The Secure Kernel parses this section at load time via an internal routine the deck names `SkpspFindPolicy`. A binary with no `.tpolicy` section, or with one whose Trustlet ID disagrees with the process-attribute Trustlet ID, or whose version byte is anything other than 1, fails the gate. The Secure Kernel does not "infer" a trustlet identity; it reads it out of the binary the attacker would have had to sign.

### Gate 4: the Trustlet Instance GUID

Once gates 1-3 pass, the trustlet calls a secure-service routine the deck names `IumSetTrustletInstance`, identified by secure-call ordinal `0x80000001`. That routine binds the running process to a Trustlet Instance GUID, the runtime identity by which the Secure Kernel discriminates one instance of a trustlet from another. Hyper-V partition GUIDs flow into this identifier for the vTPM trustlets, so that the secrets a partition's vTPM holds are scoped to that partition's Instance GUID.

The same Instance GUID can be shared across distinct Trustlet IDs. That is the architectural primitive Microsoft uses for trustlet-to-trustlet authentication: the host-side Hyper-V vTPM (`vmsp.exe`, Trustlet ID 2) and the vTPM provisioning trustlet (ID 3) cooperate on a single partition's secrets by sharing the partition's Instance GUID. The Secure Kernel's `SkCapabilities` table hardcodes which Trustlet IDs are permitted to invoke which secure-storage operations against an Instance GUID; for the 2015-era IUM surface, the only ID-discriminated rules are `CheckByTrustletId 2` for `SecureStorageGet` and `CheckByTrustletId 3` for `SecureStorageSet` [@ionescu-bh2015].

### Gate 5: the stripped-down loader

A trustlet's image loader is not the standard NT loader. The Secure Kernel routes trustlet loads through a path the deck names `LdrpIsSecureProcess`, which skips an unusually long list of features. Application Verifier hooks: skipped. Image File Execution Options registry checks: skipped. SxS / Fusion DLL redirection: skipped. The CSRSS connection ordinary NT processes establish during startup: skipped (the `BASE_STATIC_SERVER_DATA` structure CSRSS would normally hand back is fabricated locally on the trustlet's heap so dependent calls do not crash). Safer, AuthZ, Software Restriction Policies: all skipped. Any DLL load triggered from VTL0: refused.

The result is a loader path with no attack surface against VTL0 environment variables, no susceptibility to NT's normal "load this DLL instead" knobs, and no opportunity for the user's CSRSS process to inject anything into the trustlet's address space. The system-call surface available inside the trustlet is restricted to roughly fifty allowed entries. Ionescu's deck states the count verbatim: *"Only 48 system calls are currently allowed from IUM Trustlets"* [@ionescu-bh2015].

<Mermaid caption="The trustlet load sequence. The Secure Kernel checks the process attribute, the two EKUs at Signature Level 12, the .tpolicy section and Trustlet ID, and the loader strip-down in sequence. The Instance GUID is bound after the image is fully loaded. Failure at any gate aborts the load.">
sequenceDiagram
    participant Caller as Caller (VTL1 or brokered VTL0)
    participant NT as NtCreateUserProcess
    participant CI as ci.dll (CipMincryptToSigningLevel)
    participant SK as Secure Kernel (SkpspFindPolicy)
    participant Ldr as LdrpIsSecureProcess
    participant Iset as IumSetTrustletInstance
    Caller->>NT: Create with PsAttributeSecureProcess + Trustlet ID
    NT->>CI: Verify EKUs System Component plus IUM and Signing Level ge 12
    CI-->>NT: Pass or fail
    NT->>SK: Parse .tpolicy, validate s_IumPolicyMetadata
    SK-->>NT: Pass or fail
    NT->>Ldr: Strip down loader and deny VTL0-triggered DLL loads
    Ldr-->>NT: Image mapped under IUM rules
    NT->>Iset: Bind Trustlet Instance GUID
    Iset-->>NT: Trustlet alive in VTL1
</Mermaid>

| Gate | What it checks | Where it lives | Failure outcome |
|------|----------------|----------------|-----------------|
| 1. Process attribute | `PsAttributeSecureProcess` with 64-bit Trustlet ID, requested via `NtCreateUserProcess` | NT kernel boot path | Normal NT process; no IUM bit ever set [@ionescu-bh2015] |
| 2. EKUs + Signing Level | Windows System Component EKU (`1.3.6.1.4.1.311.10.3.6`) AND IUM EKU (`1.3.6.1.4.1.311.10.3.37`); Signing Level >= 12 | `ci.dll` integrity check, `CipMincryptToSigningLevel` | Load refused; no trustlet [@ionescu-ppp3] [@ionescu-bh2015] |
| 3. `.tpolicy` + `s_IumPolicyMetadata` | PE section with version 1, matching Trustlet ID, and per-trustlet policy entries | Secure Kernel `SkpspFindPolicy` | Load refused; no trustlet [@ionescu-bh2015] |
| 4. Trustlet Instance GUID | `IumSetTrustletInstance` secure-call ordinal `0x80000001`; per-partition scoping for vTPM | Secure Kernel runtime | Process exists but cannot bind to per-instance secret storage |
| 5. Loader strip-down | Skip Application Verifier, IFEO, SxS, CSRSS, Safer, AuthZ, SRP; deny VTL0-triggered DLL loads | NT `LdrpIsSecureProcess` | Normal NT loader runs; image loads but is not isolated |

The pseudocode below walks each gate in order against a fake binary descriptor. It is not a loader, it is not an exploit, and it is not a security tool. It is a teaching aid: if you can read it, you can read the trustlet load path.

<RunnableCode lang="js" title="Trustlet gate check (pseudocode)">{`
// Trustlet load-time gate check (educational pseudocode).
// Inspired by Ionescu BH2015 reverse-engineering of Win10 RTM (2015).
// Not a real loader; not a security tool.

const WINDOWS_SYSTEM_COMPONENT_EKU = "1.3.6.1.4.1.311.10.3.6";
const IUM_EKU                      = "1.3.6.1.4.1.311.10.3.37";
const MIN_SIGNING_LEVEL            = 12; // "Windows"

function loadTrustlet(bin) {
  // Gate 1: process attribute
  if (!bin.attr || !bin.attr.PsAttributeSecureProcess) {
    return "fail at gate 1: no PsAttributeSecureProcess attribute";
  }
  const requestedId = bin.attr.PsAttributeSecureProcess.trustletId;

  // Gate 2: two EKUs at Signing Level 12+
  const ekus = (bin.cert && bin.cert.ekus) || [];
  if (!ekus.includes(WINDOWS_SYSTEM_COMPONENT_EKU)) {
    return "fail at gate 2: missing Windows System Component EKU";
  }
  if (!ekus.includes(IUM_EKU)) {
    return "fail at gate 2: missing IUM EKU";
  }
  if ((bin.cert.signingLevel || 0) < MIN_SIGNING_LEVEL) {
    return "fail at gate 2: signing level below 12";
  }

  // Gate 3: .tpolicy section with s_IumPolicyMetadata
  const tpol = bin.sections && bin.sections[".tpolicy"];
  if (!tpol || !tpol.exports || !tpol.exports.s_IumPolicyMetadata) {
    return "fail at gate 3: no .tpolicy section with s_IumPolicyMetadata";
  }
  const meta = tpol.exports.s_IumPolicyMetadata;
  if (meta.version !== 1 || meta.trustletId !== requestedId) {
    return "fail at gate 3: malformed or mismatched s_IumPolicyMetadata";
  }

  // Gate 4: Trustlet Instance GUID (bound at runtime via IumSetTrustletInstance)
  const instance = bin.runtime && bin.runtime.instanceGuid;
  if (!instance) {
    return "fail at gate 4: no Trustlet Instance GUID bound";
  }

  // Gate 5: stripped-down loader (skip Application Verifier, IFEO, SxS, CSRSS,
  // Safer, AuthZ, SRP; deny VTL0-triggered DLL loads).
  // We don't simulate the loader here; we just refuse VTL0-injected DLL loads.
  if (bin.loaderTriggers && bin.loaderTriggers.fromVtl0) {
    return "fail at gate 5: VTL0-triggered DLL load denied";
  }

  return "trustlet loaded: id=" + requestedId
       + " instance=" + instance;
}

// Smoke test.
const sample = {
  attr:    { PsAttributeSecureProcess: { trustletId: 1 } },
  cert:    { ekus: [
    "1.3.6.1.4.1.311.10.3.6",
    "1.3.6.1.4.1.311.10.3.37",
  ], signingLevel: 12 },
  sections: { ".tpolicy": { exports: {
    s_IumPolicyMetadata: { version: 1, trustletId: 1 },
  } } },
  runtime: { instanceGuid: "<lsa-iso-instance>" },
  loaderTriggers: { fromVtl0: false },
};
console.log(loadTrustlet(sample));
`}</RunnableCode>

> **Key idea:** A trustlet is what passes all five gates. There is no other definition. Status is mechanical, not categorical: it is what the Secure Kernel's load path produces when a properly signed binary with a properly formed `.tpolicy` section calls `NtCreateUserProcess` with a proper secure-process attribute.

All five gates pass. The binary is now a trustlet. It is running in VTL1 user mode. The hypervisor refuses to map its pages into VTL0. Now what does it do? Who does it talk to?

## 6. The Inbox Roster

Five gates. Pass them all and you become a trustlet. Microsoft passes them on behalf of -- as of mid-2026 -- this list.

### The agent / trustlet pattern

Before the roster, the pattern. Almost every shipping trustlet has a partner: an agent process in VTL0 that does the high-volume work of integrating with the rest of the operating system, and the trustlet itself in VTL1 holding the secret material. The two talk over an Asynchronous Local Procedure Call port whose server end is hosted by the trustlet.

<Definition term="Asynchronous Local Procedure Call (ALPC)">
A Windows inter-process communication primitive optimised for fast, fixed-size message exchange between processes on the same machine. The NT kernel hosts ALPC ports as named kernel objects (e.g., `\RPC Control\LSA_ISO_RPC_SERVER`); clients open a port and exchange messages with the server. For trustlets, the ALPC server runs inside the trustlet in VTL1; clients in VTL0 send requests, the Secure Kernel marshals the request across the VTL boundary, and the trustlet returns a result back to VTL0. The hash never leaves VTL1; the request and response do.
</Definition>

<Mermaid caption="The agent / trustlet pattern. The VTL0 agent process (lsass.exe) does the bulk of the work that interacts with the rest of Windows. The trustlet (LsaIso.exe in VTL1) holds only the long-lived secrets and exposes a narrow ALPC RPC API. Requests flow VTL0 -> Secure Kernel -> VTL1; responses flow back. The agent has the network and protocol code; the trustlet has the secrets.">
flowchart LR
    NetClient[Network or local client]
    Agent["lsass.exe (VTL0 agent)<br/>protocol parsing<br/>session state<br/>network I/O"]
    SK["Secure Kernel<br/>(VTL1 ring 0)<br/>marshals secure calls"]
    Trustlet["LsaIso.exe (VTL1 trustlet)<br/>NTLM hashes<br/>Kerberos TGTs<br/>EncryptData / DecryptData"]
    NetClient -->|"network protocol"| Agent
    Agent -->|"ALPC: LSA_ISO_RPC_SERVER"| SK
    SK -->|"IUM Base API"| Trustlet
    Trustlet -->|"opaque blob"| SK
    SK --> Agent
</Mermaid>

The roster below names the agent for each trustlet where Microsoft has published one. Where the agent is not publicly named, the row says so.

### Trustlet ID 0 -- the Secure Kernel Process

The first inhabitant of VTL1 user mode. Hosts Device Guard and Hypervisor-protected Code Integrity policy decisions. Architecturally close to a daemon: it does not service external clients; it provides services the Secure Kernel itself relies on for policy decisions about whether a given image is permitted to load in VTL0 [@ionescu-bh2015].

### Trustlet ID 1 -- `LsaIso.exe` (Credential Guard)

The canonical trustlet. Holds NTLM hashes and Kerberos Ticket-Granting Tickets. Its agent in VTL0 is `lsass.exe`, the Local Security Authority Subsystem Service that has held those secrets directly for every version of Windows NT until 2015. The ALPC port name is `LSA_ISO_RPC_SERVER`. The IUM-side API the trustlet exposes is narrow: `EncryptData` and `DecryptData` on opaque blobs, plus a handful of internal management operations [@msdocs-credential-guard].

The Microsoft Learn explanation is the verbatim public account:

> With Credential Guard enabled, the LSA process in the operating system talks to a component called the isolated LSA process that stores and protects those secrets, LSAIso.exe. Data stored by the isolated LSA process is protected using VBS and isn't accessible to the rest of the operating system. LSA uses remote procedure calls to communicate with the isolated LSA process [@msdocs-credential-guard].

A VTL0 caller -- including SYSTEM-in-the-NT-kernel -- can ask the trustlet to encrypt a freshly supplied credential or to authenticate a freshly received challenge. It cannot ask the trustlet to expose the underlying NTLM hash. The hash never leaves VTL1. That is the entire point.

### Trustlet ID 2 -- `vmsp.exe` (Hyper-V vTPM, host side)

The Hyper-V [Virtual Trusted Platform Module](https://paragmali.com/blog/the-tpm-in-windows-one-primitive-twenty-five-years-and-the-c) on the host side. One `vmsp.exe` instance per guest partition; the agent is `vmwp.exe`, the Hyper-V Virtual Machine Worker Process for that partition. The Instance GUID is the partition's GUID, so that the keys a partition's vTPM holds are scoped to that partition and that partition only. Storage primitives include a Mailbox primitive (protected by a per-instance Security Cookie) and a Secure Storage primitive that produces Ingress and Egress blobs encrypted with per-Instance IDK material [@ionescu-bh2015] [@msdocs-guarded-fabric].

Shielded VMs on Windows Server 2016 and later consume `vmsp.exe`. A shielded VM, per Microsoft Learn, *"has a virtual TPM, is encrypted using BitLocker, and can run only on healthy and approved hosts in the fabric"* [@msdocs-guarded-fabric]. The vTPM keys live in the host's `vmsp.exe` trustlet; the [BitLocker volume master key](https://paragmali.com/blog/bitlocker-on-windows-architecture-attacks-and-the-limits-of-) in the guest is sealed against that vTPM; and a SYSTEM-privilege NT-kernel write primitive on the host cannot read the partition's vTPM secrets even though the host can otherwise reach the partition's memory.

### Trustlet ID 3 -- vTPM provisioning trustlet

Pushes initial secrets into a partition's Instance GUID at vTPM creation time. The Secure Kernel's `SkCapabilities` array hardcodes `CheckByTrustletId 2` for `SecureStorageGet` and `CheckByTrustletId 3` for `SecureStorageSet`; those are the only Trustlet-ID-checked secure-storage operations in the 2015-era IUM secure-call surface [@ionescu-bh2015]. The pair of trustlets cooperates on the same Instance GUID so the provisioning trustlet writes and `vmsp.exe` reads, with the Secure Kernel enforcing that no other trustlet can do either.

### Enhanced Sign-in Security (ESS) biometric matching component (Windows 11+)

Microsoft Learn documents the architectural placement of Windows Hello's facial-recognition algorithm verbatim:

> When ESS is enabled, the face algorithm is protected using VBS to isolate it from the rest of Windows. The hypervisor is used to specify and protect memory regions, so that they can only be accessed by processes running in VBS. The hypervisor allows the face camera to write to these memory regions providing an isolated pathway... Sensors that support ESS have a certificate embedded during manufacturing [@msdocs-ess].

The page also documents the certificate chain that authenticates the camera to the matcher and the match-on-sensor requirement for fingerprint readers under ESS. Microsoft does *not* publicly name the binary that hosts the face algorithm, and it does not publicly assign that binary a Trustlet ID. The architectural placement is a trustlet. The naming is not on the record.

### Administrator Protection / Adminless issuer (Windows 11, rolling out 2025-26)

In October 2025 Microsoft shipped a preview of Administrator Protection in KB5067036 [@kb5067036] and reverted the rollout in the same update note [@msdocs-admin-protection]. The Microsoft Learn page describes the security model:

> Once authorized, Windows uses a hidden, system-generated, profile-separated user account to create an isolated admin token. This token is issued to the requesting process and is destroyed once the process ends, ensuring that admin privileges don't persist. Administrator protection introduces a new security boundary with support to fix any reported security bugs [@msdocs-admin-protection].

The implementation surface that issues those tokens is not publicly named. The architectural family resemblance to a trustlet is strong, and the "new security boundary with support to fix any reported security bugs" line is the formal commitment Microsoft makes for VBS-isolated components. Whether the issuer is a trustlet, a VBS Enclave, or a separately isolated VTL0 process is, as of mid-2026, not on the public record.

### Third-party VBS Enclaves (Windows 11 24H2 and later)

For the first time since 2015, the trustlet primitive is exposed to third-party developers. A VBS Enclave is a DLL signed with a Trusted Signing certificate and loaded into a VTL1 enclave region of a host process via `CreateEnclave` and `CallEnclave`. The OS support is narrow:

> Windows 11 Build 26100.2314 or later... Windows Server 2025 or later... Visual Studio 2022 version 17.9 or later... The Windows Software Development Kit (SDK) version 10.0.22621.3233 or later, which provides veiid.exe (the VBS Enclave import ID binding utility) and signtool.exe... A Trusted Signing account [@msdocs-vbs-enclaves].

Azure SQL's "Always Encrypted with secure enclaves" is the public flagship consumer. The architectural difference from an inbox trustlet is the API surface and the enclave-versus-process model: a VBS Enclave is a region inside an existing process's address space, not a separately scheduled process. The threat model is identical: the host (the rest of the process, including its VTL0 code) is the attacker, the enclave is the defender [@pulapaka-vbs-enclaves].

### Roster table

| Trustlet ID | Binary | VTL0 agent | ALPC endpoint | Secret / operation | Source |
|-------------|--------|------------|----------------|---------------------|--------|
| 0 | Secure Kernel Process | (internal; no external agent) | (internal) | Device Guard / HVCI policy decisions | [@ionescu-bh2015] |
| 1 | `LsaIso.exe` | `lsass.exe` | `LSA_ISO_RPC_SERVER` | NTLM hashes, Kerberos TGTs; `EncryptData` / `DecryptData` | [@msdocs-credential-guard] [@ionescu-bh2015] |
| 2 | `vmsp.exe` | `vmwp.exe` (per partition) | per-instance, partition GUID scoped | Hyper-V vTPM, host side; secure storage `Get` | [@ionescu-bh2015] [@msdocs-guarded-fabric] |
| 3 | vTPM provisioning trustlet | (Hyper-V provisioning agent) | per-instance, partition GUID scoped | Initial secret provisioning; secure storage `Set` | [@ionescu-bh2015] |
| (unpublished) | ESS face-algorithm component | Hello biometric pipeline; sensor-issued cert auth | not publicly named | Face template matching (fingerprint matching under ESS is match-on-sensor) | [@msdocs-ess] |
| (unpublished) | Administrator Protection issuer | UAC / Authorization Manager broker | not publicly named | Just-in-time admin token issuance | [@msdocs-admin-protection] |
| (third-party) | VBS Enclave DLL | host process (`CreateEnclave` caller) | direct calls via `CallEnclave` | Application-defined; e.g., Azure SQL Always Encrypted | [@msdocs-vbs-enclaves] [@pulapaka-vbs-enclaves] |

<Sidenote>The published authoritative trustlet list still stops at Trustlet IDs 0-3 from August 2015. Every roster published after that point has been inferred from secondary evidence: kernel symbols, ALPC port enumeration via `NtQuerySystemInformation`, documented architectural placements. Microsoft has not republished an authoritative roster for any later Windows release.</Sidenote>

<Aside label="Where the public record runs out">
Two trustlets in the list above are *architecturally* trustlets per Microsoft's published documentation but have not been publicly named or numbered. The ESS face-algorithm matcher is documented to live in VBS-isolated memory, with sensor-certificate authentication and template-encryption keys held in VBS, but the binary's name and Trustlet ID are not on the public record [@msdocs-ess]. The Administrator Protection token issuer's implementation surface is even less precisely specified -- "a hidden, system-generated, profile-separated user account" inside "a new security boundary," but no commitment to whether the issuer is a trustlet, a VBS Enclave, or a separate isolated process [@msdocs-admin-protection]. This article will not invent names or numbers for either. Empirical enumeration via `NtQuerySystemInformation(SystemIsolatedUserModeInformation)` on a current Windows 11 build is the only way to obtain a current roster, and that route is outside the scope of this piece.
</Aside>

> **Note:** Credential Guard prevents the *memory-resident* NTLM hash or Kerberos TGT from being read out of VTL0. It does not protect typed-in credentials, the agent-side relay surface, plaintext-secret protocols (CredSSP / NTLMv1 / MS-CHAPv2 / Digest), or liveness; the full four-item enumeration with citations lives in Section 10. Microsoft documents one corner of the limit verbatim: Credential Guard *"doesn't prevent an attacker with malware on the PC from using the privileges associated with any credential"* [@msdocs-credential-guard].

The published roster stops at Trustlet IDs 0-3 from 2015. The actual roster on a 2026 box is bigger. How much bigger Microsoft hasn't said. That is one of the open problems Section 9 will pick up.

## 7. Competing Approaches

Microsoft is not alone. The same threat model -- "protect user-mode code from a compromised OS kernel" -- has been answered six other ways. None is strictly better than a trustlet. None is strictly worse. The right answer depends on what platform you are on, what threat model you have, and what workload you are trying to protect.

<Definition term="Trusted Execution Environment (TEE)">
A hardware-enforced or hypervisor-enforced execution context whose memory and state are inaccessible to the surrounding host operating system, including its kernel. The Open Mobile Terminal Platform (OMTP) first defined the term, and GlobalPlatform now publishes the standard APIs (TEE Client API for the host, TEE Internal Core API for the trusted code). Windows trustlets, Intel SGX enclaves, ARM TrustZone Trusted Applications, AMD SEV-SNP confidential VMs, Apple's Secure Enclave, and seL4 user-mode security servers are all variants of TEE [@wiki-tee].
</Definition>

### Intel SGX

Software Guard Extensions launched with the sixth-generation Intel Core processors (Skylake) in 2015 [@wiki-sgx]. SGX adds two CPU instructions with different privilege requirements: `ENCLS` (ring 0; the OS issues leaves like `ECREATE` on behalf of a user-mode application) and `ENCLU` (ring 3; the application issues leaves like `EENTER` and `EEXIT` to enter and leave its enclave) [@intel-sdm-sgx]. The result is a user-mode-controllable enclave whose memory is encrypted on the way out of the CPU's Enclave Page Cache to DRAM. The CPU microcode itself, plus the Quoting Enclave, is the TCB. Neither the OS kernel nor the hypervisor sits in the trust path.

That sounded ideal in 2015. It has not aged well. Foreshadow (USENIX Security 2018, Van Bulck et al.) demonstrated that transient-execution attacks could extract not only enclave memory but the platform's attestation key [@foreshadow-usenix]. The Foreshadow team's site states the consequence:

<PullQuote>
"Foreshadow demonstrates how speculative execution can be exploited for reading the contents of SGX-protected memory as well as extracting the machine's private attestation key... due to SGX's privacy features, an attestation report cannot be linked to the identity of its signer. Thus, it only takes a single compromised SGX machine to erode trust in the entire SGX [deployment base]." -- Foreshadow project site [@foreshadow-attack-eu]
</PullQuote>

SGAxe (attestation-key extraction) [@sgaxe], Plundervolt (software-controlled undervolting to fault SGX computations) [@plundervolt], SgxPectre (branch-target injection across the enclave boundary) [@sgxpectre], and others followed. Intel deprecated SGX on 11th-generation Core and later client CPUs, which incidentally removed Ultra HD Blu-ray playback on officially licensed software including PowerDVD [@wiki-sgx]. SGX continues on Xeon for confidential cloud workloads but is no longer a target architects pick on Windows clients.

<Sidenote>The Ultra HD Blu-ray collapse is the closest the SGX deprecation has come to mainstream visibility. PowerDVD's SGX dependency meant that a client SGX deprecation broke a consumer product line, and Cyberlink had to ship updates rerouting around the dropped CPU feature.</Sidenote>

### AMD SEV-SNP and Intel TDX

AMD's Secure Encrypted Virtualization with Secure Nested Paging (SEV-SNP), introduced on EPYC 7003 (Milan, launched 15 March 2021) [@wiki-amd-epyc], and Intel's Trust Domain Extensions (TDX), introduced on 4th-generation Xeon Scalable (Sapphire Rapids, launched 10 January 2023) [@wiki-sapphire-rapids], provide *whole-VM* confidential computing [@amd-sev-overview] [@intel-tdx-overview]. AMD's verbatim claim: *"SEV-SNP adds strong memory integrity protection to help prevent malicious hypervisor-based attacks like data replay, memory re-mapping, and more to create an isolated execution environment"* [@amd-sev-overview]. Intel's verbatim claim about TDX: *"A CPU-measured Intel TDX module enables Intel TDX. This software module runs in a new CPU Secure Arbitration Mode (SEAM) as a peer virtual machine manager (VMM)"* [@intel-tdx-overview]. The AMD SEV-SNP whitepaper "Strengthening VM Isolation with Integrity Protection and More" is the canonical technical reference [@amd-sev-snp-whitepaper].

The granularity is different from a trustlet. SEV-SNP and TDX isolate an entire virtual machine from its hypervisor and host. They do not isolate a process from its own VM's kernel. For "this user-mode process should be protected from a SYSTEM kernel write primitive on the same OS," a trustlet is the primitive; for "this entire VM should be protected from a compromised cloud provider," a CVM is the primitive. Use the right one.

### ARM TrustZone and OP-TEE

The two-world hardware split that has shipped on every Cortex-A processor since the mid-2000s -- the Wikipedia ARM architecture article states verbatim that *"the Security Extensions, marketed as TrustZone Technology, is in ARMv6KZ and later application profile architectures,"* the lineage every Cortex-A core inherits [@wiki-arm-architecture]. The CPU enforces a Non-Secure World and a Secure World; switching between the two is mediated by a Secure Monitor Call (`SMC`) instruction. OP-TEE is the canonical open-source secure-world OS for Cortex-A TrustZone, with Trusted Applications running as user-mode binaries in Secure World EL-0 and the OP-TEE OS itself running at EL-1 [@optee-about]. The OP-TEE about page describes the design: *"OP-TEE is a Trusted Execution Environment (TEE) designed as companion to a non-secure Linux kernel running on Arm; Cortex-A cores using the TrustZone technology"* [@optee-about].

TrustZone is the closest non-Windows analogue to a trustlet at the architectural level. The vocabulary maps one for one.

| Concept | Windows VBS / IUM | ARM TrustZone / OP-TEE |
|---------|--------------------|--------------------------|
| Isolation primitive | Hyper-V hypervisor + SLAT | TrustZone Address Space Controller; CPU NS/S bit |
| Secure-side kernel | Secure Kernel (VTL1 ring 0) | OP-TEE OS (Secure World EL-1) |
| Secure-side user mode | IUM (VTL1 ring 3) | Trusted Applications (Secure World EL-0) |
| Agent / supplicant | The trustlet's VTL0 agent (e.g., `lsass.exe`) | `tee-supplicant` and TEE Client API on the Linux side |
| Trust gate | Microsoft EKUs + Signature Level 12 | OP-TEE TA signing key configured at build time |

### Apple Secure Enclave Processor (SEP)

Apple's answer is a dedicated on-die security subsystem. SEP is a separate processor core, isolated from the Application Processor on the same SoC, with its own boot ROM, its own AES engine, and its own random number generator. It has been in every iPhone since iPhone 5s (2013), every Apple Silicon Mac, every Apple Watch from Series 1 [@apple-sep]. Apple's verbatim description:

> The Secure Enclave Processor runs an Apple-customized version of the L4 microkernel. It's designed to operate efficiently at a lower clock speed that helps to protect it against clock and power attacks [@apple-sep].

SEP is the strongest counter to microarchitectural side channels among the production options, because the cores genuinely do not share microarchitectural state with the Application Processor. The price is that everything is firmware-class: patching a SEP bug means rolling SEP firmware on every Apple device, not pushing an OS update. The cycle is slower and more centralised.

### seL4 plus user-mode security servers

The academic conscience of the lineage. About ten thousand lines of formally verified C, with machine-checked proofs of functional correctness, confidentiality, and integrity [@sel4-sosp-paper] [@sel4-about]. Sub-microsecond IPC. The price is that seL4 is a separation microkernel, not a desktop OS; building a Credential-Guard-equivalent on seL4 means designing the application architecture from the microkernel up, not retrofitting it onto a Windows-compatible stack. seL4 has shipping deployments in defence (the DARPA HACMS programme), automotive ECUs, and Qualcomm's Hexagon DSP secure OS.

### When to pick which

A decision table of the kind a colleague would actually use.

| You want | Pick |
|----------|------|
| Protect a user-mode Windows process from a SYSTEM kernel write primitive | Trustlet (inbox) or VBS Enclave (third-party) [@msdocs-vbs-enclaves] |
| Protect an entire VM from your cloud provider's host | AMD SEV-SNP or Intel TDX [@amd-sev-overview] [@intel-tdx-overview] |
| Protect a user-mode Linux-on-ARM service from a compromised Linux kernel | TrustZone + OP-TEE Trusted Application [@optee-about] |
| Hold an iPhone owner's Touch ID / Face ID template safely from iOS | Apple SEP [@apple-sep] |
| Build a high-assurance system with a machine-checked proof of kernel correctness | seL4 [@sel4-sosp-paper] |
| Run Intel SGX enclaves on Xeon for confidential cloud | SGX (modulo Foreshadow-class side channels) [@foreshadow-attack-eu] |

Trustlets are the right answer for Windows. They are not the right answer for every platform, every threat model, or every workload. They are also not without limits *on Windows itself*. What are those?

## 8. The Floor of the Threat Model

By 2020 the trustlet model had been shipping for five years. Two researchers at the Microsoft Security Response Center, Saar Amar and Daniel King, pointed a fuzzer at the secure-call interface for two weeks and reported back with five VTL0-to-VTL1 bugs [@amar-bh2020]. Their Black Hat USA 2020 talk, "Breaking VSM by Attacking Secure Kernel," is the most important public document on what the trustlet model actually guarantees and what it does not [@amar-publications].

The talk is honest in a way Microsoft is rarely honest about its own products. The slides enumerate the bugs by CVE number, name the specific Secure Kernel routines they exploited, and -- unusually -- list the hardening changes Microsoft shipped because of what was found. Reading the deck is the closest thing to a Q-and-A with the Secure Kernel team.

### Bug class 1: the secure-call interface is the floor

The Secure Kernel exposes about three dozen "secure services" callable from VTL0 via the `IumInvokeSecureService` dispatcher. Each takes a parameter block from VTL0, parses it inside VTL1, and returns. That dispatcher is, by definition, the largest VTL0-controllable input surface in the model. Amar and King retargeted the Hyperseed hypercall fuzzer, originally written by Daniel King and Shawn Denbow for hypercall fuzzing, at `securekernel!IumInvokeSecureService` [@amar-bh2020]. Two weeks of fuzzing produced five bugs.

Two of them shipped with public CVE numbers in 2020. CVE-2020-0917 is an out-of-bounds read in the secure-call surface; CVE-2020-0918 is a design flaw in `SkmmUnmapMdl` where a VTL0 caller could pass a fully attacker-controlled Memory Descriptor List to `SkmiReleaseUnknownPTEs` [@nvd-cve-2020-0917] [@nvd-cve-2020-0918] [@amar-bh2020]. The NVD entries describe both with the same boilerplate ("Windows Hyper-V Elevation of Privilege Vulnerability") and classify the CWE as "Insufficient Information"; the technical detail lives in the Amar/King deck.

Microsoft hardened in response. The Amar/King deck enumerates what changed:

- The Secure Kernel pool moved to segment heap in mid-2019, breaking the heap layout the public exploit depended on.
- Four W+X regions in VTL1 were reduced to +X only, eliminating attacker-controlled code-injection targets.
- `SkpgContext`, a HyperGuard-style control-flow integrity check for the Secure Kernel, was introduced [@amar-bh2020].

<Definition term="Malwarelet">
Alex Ionescu's term for an attacker-controlled trustlet, enabled by a substrate compromise rather than a trustlet bug. If Test Signing is on, or if a production Microsoft signing key leaks, or if Secure Boot can be bypassed, an attacker can sign and load their own "trustlet" that passes the five gates of Section 5 and operates with VTL1 privilege. The trustlet model itself remains intact; the trust roots underneath it are what fail [@ionescu-bh2015].
</Definition>

### Bug class 2: denial of service is not a security boundary

Amar's deck states the rule that excludes liveness from the VBS threat model verbatim:

<PullQuote>
"VTL0 can DOS VTL1 by design." -- Saar Amar and Daniel King, Black Hat USA 2020 [@amar-bh2020]
</PullQuote>

The hypervisor schedules VTL1; VTL0 is the agent for almost every communication channel into VTL1; VTL0 can stop talking to VTL1 at any time. None of this is, in Microsoft's stated model, a security violation. A VTL0 kernel attacker who can prevent Credential Guard from issuing tickets has not stolen any credential; they have, in the language of the threat model, achieved denial of service, which is out of scope. This matters in practice: a defender cannot reason about a trustlet "always being available." They can only reason about its memory not being readable from VTL0 *when it is available*.

### Bug class 3: the agent RPC surface lives in VTL0

The trustlet's pages are safe even from VTL0 ring 0. The agent process that services the trustlet's ALPC port is *not* safe. The agent is `lsass.exe` for Credential Guard, `vmwp.exe` for the vTPM, presumably the Hello biometric pipeline for ESS. Every byte of every protocol whose state machine the agent implements is reachable from VTL0. The hash never leaves VTL1; the *authentication outcomes* the hash produces can be relayed.

In December 2022 Oliver Lyak published "Pass-the-Challenge: Defeating Windows Defender Credential Guard" [@lyak-pass-the-challenge]. The technique recovers usable NTLM challenge responses from encrypted credential blobs that `LsaIso.exe` returns to `lsass.exe` in VTL0:

> In this blog post, we present new techniques for recovering the NTLM hash from an encrypted credential protected by Windows Defender Credential Guard. While previous techniques for bypassing Credential Guard focus on attackers targeting new victims who log into a compromised server, these new techniques can also be applied to victims logged on before the server was compromised [@lyak-pass-the-challenge].

<Aside label="Pass-the-Challenge in one paragraph">
A network authentication protocol that uses NTLM works in challenge-response form: the server sends a challenge, the client encrypts it with its NTLM hash, the server (or a domain controller) verifies the response. With Credential Guard, the client's NTLM hash lives in `LsaIso.exe`; only `LsaIso.exe` can perform the encryption. A VTL0 attacker who can talk to `lsass.exe` can ask `lsass.exe` to ask `LsaIso.exe` to compute an NTLM response for an attacker-supplied challenge. The attacker never sees the hash; they see an authentication response computed with it. Many real-world relay attacks need only the response, not the hash. Lyak's writeup is the worked example; the architectural fact is that the agent RPC channel is a VTL0 surface even though the hash itself is not.
</Aside>

Microsoft documents one corner of the limit verbatim: Credential Guard *"doesn't prevent an attacker with malware on the PC from using the privileges associated with any credential"* [@msdocs-credential-guard]. The "use" is the agent-side operation; the trustlet is doing the cryptography, and the cryptography is being used by the attacker.

### Bug class 4: trustlet-to-trustlet via shared Instance GUIDs

Trustlets that share an Instance GUID can read and write storage blobs the Secure Kernel scopes per-Instance. The pair `vmsp.exe` and the vTPM provisioning trustlet uses exactly this primitive: provisioning writes, `vmsp.exe` reads, the Secure Kernel hard-codes which Trustlet IDs may invoke `SecureStorageSet` versus `SecureStorageGet` on each Instance GUID. The defence is in the `SkCapabilities` table; bugs in that table are exploit-class.

In Ionescu's vocabulary, a "malwarelet" is the worst case here: an attacker-controlled trustlet -- enabled by a [Secure Boot](https://paragmali.com/blog/secure-boot-in-windows-the-chain-from-sector-zero-to-userini) or Test Signing compromise -- could request access to the Instance GUIDs of other trustlets, and any missing rule in `SkCapabilities` would let it read what those trustlets stored. There are no public exploits in this class as of mid-2026. There also is not a published audit of the table.

### Bug class 5: substrate compromise (Secure Boot, firmware, signing keys)

If Test Signing is on; if a production signing key leaks; if Secure Boot can be bypassed to boot a kernel that accepts attacker-controlled trustlet roots; if the UEFI firmware itself permits a DMA attack against early-boot memory -- the entire trustlet model is moot. Ionescu's BH2015 deck states the diagnosis: *"VBS' key weakness is its reliance on Secure Boot"* [@ionescu-bh2015]. Rafal Wojtczuk's Black Hat USA 2016 attack-surface analysis empirically validated the warning, demonstrating one non-critical VBS-feature bypass and one critical firmware exploit [@wojtczuk-bh2016]. The firmware below VBS is the substrate trustlets sit on; the trustlet model is no stronger than that substrate.

<Mermaid caption="The residual attack surface against the trustlet model. Five attack classes hit five different places: the secure-call interface (Amar 2020); design exclusion of denial-of-service from the threat model; the agent RPC surface (Pass-the-Challenge); trustlet-to-trustlet storage via shared Instance GUIDs; substrate compromise (firmware, Secure Boot, signing keys). The first two were demonstrated against shipping Windows; the last three remain class-level concerns that bound what the model can guarantee.">
flowchart TD
    Attacker["VTL0 kernel attacker"]
    SK["Secure Kernel"]
    Trustlet["Trustlet (VTL1 user)"]
    Agent["VTL0 agent process (lsass.exe, vmwp.exe...)"]
    Substrate["Substrate: UEFI firmware, Secure Boot, signing roots"]
    Attacker -->|"1. Secure-call interface bugs<br/>CVE-2020-0917, CVE-2020-0918"| SK
    Attacker -->|"2. DoS by design (out of scope)"| SK
    Attacker -->|"3. Agent RPC surface<br/>Pass-the-Challenge"| Agent
    Agent -->|"authentication outcome"| Trustlet
    Attacker -->|"4. Trustlet-to-trustlet<br/>via shared Instance GUID"| Trustlet
    Substrate -->|"5. Substrate compromise<br/>malwarelets, BootHole-class"| SK
    Substrate --> Trustlet
</Mermaid>

<Sidenote>The Hyperseed fuzzer had a prior life. Daniel King and Shawn Denbow first presented it at OffensiveCon 2019 as a hypercall fuzzer [@amar-bh2020]. The retargeting at the secure-call interface is the same tool, pointed at a different parser. The two-weeks-five-bugs result is therefore not "Microsoft wrote bad code" but "a well-built fuzzer aimed at a complex parser will find bugs in ~2 weeks." That is the empirical bar for an unverified TCB.</Sidenote>

> **Key idea:** The trustlet model is hypervisor-strong against the VTL0 kernel; it is not stronger than the substrate it sits on. Five attack classes -- secure-call interface bugs, designed-out denial-of-service, the agent RPC residual, trustlet-to-trustlet via shared Instance GUIDs, and substrate compromise -- bound what the model can guarantee. None of them invalidates trustlets; all of them are reasons to deploy trustlets *alongside* other controls rather than as a sole defence.

The trustlet model has a finite, audited attack surface. The surface is not zero. Liveness is not promised. The firmware and Secure Boot underneath everything still matter. What is new on this surface in 2024 to 2026?

## 9. Open Problems

Three things you might expect Microsoft to have published by 2026 -- the current inbox trustlet roster, an architecture diagram of Administrator Protection on par with Credential Guard's, and a public CVE wave around VBS Enclaves -- are still partial or missing. Here is the frontier.

**1. Trustlet enumeration drift.** Ionescu's August 2015 enumeration of Trustlet IDs 0 through 3 remains the only authoritative published list. Eleven years later, the ESS biometric matcher has not been named with a Trustlet ID and the Administrator Protection issuer has not been committed to as a trustlet at all. A researcher with a debugger and the Quarkslab IUM-debugging recipe can recover the current roster empirically [@quarkslab-debug-ium]; Microsoft has not republished it.

**2. VBS Enclave trust-boundary hardening.** Microsoft's Security Response Center published a blog post in June 2025 -- "Everything Old Is New Again" -- explicitly committing to host-to-enclave pointer validation, copy-before-check discipline, and TOCTOU avoidance as the active hardening surface for VBS Enclaves [@ms-everything-old]. The post is unambiguous that a CVE wave is foreseeable as researchers turn their attention to the host-enclave seam. As of the publication of this article no public CVE has been issued against a VBS Enclave-using product, but Microsoft's narrowing of supported Windows builds in 2025 (from "Windows 11 24H2 or later" to "Windows 11 Build 26100.2314 or later") is the kind of build-floor adjustment that historically precedes a documented hardening change [@msdocs-vbs-enclaves].

**3. Side channels against VTL1.** Transient-execution attacks against VTL1 memory have not been publicly demonstrated end to end. The Foreshadow class of attacks against SGX is the existence proof that a co-resident TEE can leak through microarchitectural side channels, and the threat model explicitly includes them [@foreshadow-attack-eu]. There is no VBS-specific transient-execution mitigation; platform-wide mitigations (Kernel Virtual Address Shadow, Retpoline, Indirect Branch Restricted Speculation) are the only defence. A demonstration of "Foreshadow-against-LsaIso" would not be surprising; its absence to date is, given the research community's interest, mildly so.

**4. Debugging asymmetry.** Researchers have a working trustlet-debugging recipe; defenders have an explicit "no" from Microsoft. The Quarkslab writeup walks through nested virtualisation to attach to a trustlet under controlled conditions [@quarkslab-debug-ium]; Microsoft's product-facing page states verbatim that *"it is not possible to attach to an IUM process"* and that *"other APIs, such as CreateRemoteThread, VirtualAllocEx, and Read/WriteProcessMemory will also not work as expected when used against Trustlets"* [@msdocs-ium]. The asymmetry favours offence: an attacker with the time, hardware, and tooling Quarkslab demonstrates can study trustlet internals in ways a defender on a production box cannot. Live-system trustlet introspection for incident response is the missing capability.

**5. Administrator Protection transparency.** As of 10 May 2026, the Administrator Protection feature has been shipped in preview (KB5067036, 28 October 2025), then reverted in the same update note pending a future re-rollout [@kb5067036] [@msdocs-admin-protection]. There is no architecture diagram on the level of Credential Guard's "how it works" page. There is no published Trustlet ID. There is no public commitment to whether the token issuer is a trustlet, a VBS Enclave, or something else inside the new security boundary. For a feature that materially changes the local-elevation model of Windows, that is unusual reticence.

**6. Cross-architecture portability.** A workload that wants to run as a trustlet on Windows, a Confidential VM on Linux, a Trusted Application on ARM, and a Secure Enclave Application on Apple silicon must, today, be written four times. GlobalPlatform's TEE Client API standardises one side of TrustZone, the Open Enclave SDK abstracts a subset of SGX and TrustZone, and VBS Enclaves do their own thing. No universal portable TEE API exists. For workloads where portability matters more than peak isolation, this is the open problem with the most direct commercial pressure behind it.

<Aside label="Why no current trustlet roster?">
Two answers, both incomplete. The defensive answer: an enumerated trustlet list is an attacker's targeting list, and Microsoft prefers not to publish targeting lists for components whose exact attack surface is still under active study. The historical answer: the 2015 list was a side-effect of Ionescu reverse-engineering Windows 10 RTM. There has been no comparable public reverse-engineering push for any post-2015 Windows release at the same level of completeness, and Microsoft has not chosen to fill the gap with first-party documentation. Empirical enumeration via `NtQuerySystemInformation(SystemIsolatedUserModeInformation)` works on a live system, but doing it on every Windows 11 servicing build is a research programme, not a citation.
</Aside>

These are questions a researcher with a year of grant time could move the field on. The next section is the question a practitioner has today.

## 10. Practitioner Guide

What changes in a real workflow once you know what a trustlet is? Four short answers.

### Windows administrator

Verify Credential Guard is actually running before you assume it is. Two ways.

> **Note:** **GUI:** Run `msinfo32` and check *Virtualization-based security Services Running*. You should see at least "Credential Guard" and ideally "Hypervisor enforced Code Integrity." **PowerShell:** `Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard`. The properties `SecurityServicesRunning` and `VirtualizationBasedSecurityStatus` are the load-bearing ones; values of 1 and 2 respectively indicate Credential Guard is running with VBS in full enforcement [@msdocs-credential-guard].

Enumerating live trustlets on a 2026 box requires more care than enumerating ordinary processes. Process Explorer's *Image* tab carries an IUM marker for trustlet processes. SysInternals Sigcheck on a candidate binary surfaces the Signing Level. The Microsoft Learn IUM page is explicit that *"other APIs, such as CreateRemoteThread, VirtualAllocEx, and Read/WriteProcessMemory will also not work as expected when used against Trustlets"* [@msdocs-ium] -- the same APIs many EDR products rely on for behavioural monitoring will silently fail or report sentinel values when targeted at a trustlet. Plan detections accordingly.

### Security researcher

The Quarkslab blog post "Debugging Windows Isolated User Mode (IUM) Processes" is the canonical recipe for attaching to a trustlet under nested virtualisation [@quarkslab-debug-ium]. The empirical enumeration path is `NtQuerySystemInformation` with class `SystemIsolatedUserModeInformation`; the structure returned includes a count of running trustlets and their identifying metadata.

<Sidenote>The driver-side pattern Microsoft documents for "is this process a trustlet?" is `IsSecureProcess`, an internal Win32K predicate the IUM page names as the canonical check. Tools that need to behave differently against trustlets (memory scanners, integrity checkers, EDR sensors) should call the supported equivalent rather than parsing process attributes by hand [@msdocs-ium].</Sidenote>

### Application developer (VBS Enclaves)

If you are writing third-party code that needs trustlet-class isolation, the primitive you target is a VBS Enclave, not a trustlet. The toolchain is specific:

- Visual Studio 2022 version 17.9 or later.
- Windows SDK version 10.0.22621.3233 or later (provides `veiid.exe`, the VBS Enclave import ID binding utility, and `signtool.exe`).
- A Trusted Signing account for production signing [@msdocs-vbs-enclaves].

The architectural rule is *never trust the host*. The host process's address space is reachable by the enclave; the enclave's address space is not reachable by the host. Range-validate every pointer the host hands the enclave; copy before you check (so the host cannot mutate the data between your check and your use); avoid TOCTOU windows. Microsoft's "Everything Old Is New Again" post is explicit that this is the hardening surface researchers are looking at right now [@ms-everything-old].

The development guide includes a sample with a comment that captures the discipline:

> Every DLL loaded in an enclave requires a configuration. This configuration is defined using a global const variable named __enclave_config of type IMAGE_ENCLAVE_CONFIG... // DO NOT SHIP DEBUGGABLE ENCLAVES TO PRODUCTION [@msdocs-vbs-enclaves-dev-guide].

The `IMAGE_ENCLAVE_POLICY_DEBUGGABLE` flag is for development only. The `VbsEnclaveTooling` repository on GitHub provides a NuGet package and a code generator that make the cross-VTL marshalling less error-prone, plus reference documentation including `Edl.md`, `HelloWorldWalkthrough.md`, and `CodeGeneration.md` [@vbs-enclave-tooling].

<Spoiler kind="hint" label="Minimal VBS Enclave development checklist">
1. Confirm OS support: Windows 11 Build 26100.2314+ or Windows Server 2025+ [@msdocs-vbs-enclaves].
2. Install Visual Studio 2022 17.9+ and Windows SDK 10.0.22621.3233+.
3. Acquire a Trusted Signing account; configure `signtool.exe` for it.
4. Define `__enclave_config` as `IMAGE_ENCLAVE_CONFIG`; set family/image/SVN fields.
5. Use `veiid.exe` to bind import IDs.
6. Sign the enclave DLL with `signtool.exe` and the Trusted Signing certificate.
7. Test with `IMAGE_ENCLAVE_POLICY_DEBUGGABLE` set; remove it before production.
8. Range-validate every host-supplied pointer; copy before check.
</Spoiler>

### Defender

Know what Credential Guard does *not* protect, because that is where most exposure remains.

> **Note:** The trustlet protects memory-resident NTLM hashes and Kerberos TGTs from a VTL0 kernel attacker. It does not protect: - Supplied credentials at the logon prompt (keyloggers, screen-scrapers, hardware shimming). - The agent RPC channel (Pass-the-Challenge-class relay against `lsass.exe` is reachable from VTL0) [@lyak-pass-the-challenge]. - Protocols that require a usable secret in plaintext: CredSSP, NTLMv1, MS-CHAPv2, Digest. These are unsupported with the trustlet-protected token by design [@msdocs-credential-guard]. - Liveness: a VTL0 kernel attacker can stop talking to VTL1 and prevent the trustlet from being available. Denial of service is out of the VBS threat model [@amar-bh2020]. The summary: trustlets shrink the credential-theft attack surface, they do not eliminate it.

The trustlet model is finite, audited, and useful. Use the lock; do not assume the lock is the only thing on the door.

## 11. Frequently asked questions

<FAQ title="Frequently asked questions">

<FAQItem question="Is a trustlet just a Protected Process by another name?">
No. Protected Process Light (PPL) and trustlets sit in the same lineage but differ at the architectural level. A PPL is enforced by the NT kernel, which is also the attacker's likely foothold; itm4n's 2021 PPLdump showed the result over nine years of LSASS-as-PPL deployment [@itm4n-runasppl]. A trustlet is enforced by the Hyper-V hypervisor and the Secure Kernel, both running in a different Virtual Trust Level from the NT kernel; a VTL0 kernel write primitive does not touch the trustlet's pages [@quarkslab-debug-ium]. The signing-level lattice is similar (both rely on Signature Level 12); the enforcement architecture is not.
</FAQItem>

<FAQItem question="Can I write my own trustlet?">
Not directly. Inbox trustlets require the Microsoft IUM EKU (`1.3.6.1.4.1.311.10.3.37`), which Microsoft does not grant to third parties [@ionescu-bh2015]. Since Windows 11 24H2, the third-party-shippable equivalent is a VBS Enclave: a DLL signed with a Trusted Signing certificate, loaded into an enclave region of a host process via `CreateEnclave` and `CallEnclave`. The architectural threat model is identical (the host is the attacker, the enclave is the defender); the API surface and the enclave-versus-process model differ. VBS Enclaves require Windows 11 Build 26100.2314 or later, Windows SDK 10.0.22621.3233 or later, Visual Studio 2022 17.9 or later, and a Trusted Signing account [@msdocs-vbs-enclaves].
</FAQItem>

<FAQItem question="Does Credential Guard mean my credentials are unstealable?">
No. It means that the *memory-resident* NTLM hash or Kerberos TGT cannot be read out of `LsaIso.exe` by a VTL0 kernel attacker. It does not mean credentials are unstealable. Section 10 enumerates the four classes of residual exposure -- typed-in credentials, the agent-side RPC relay (Pass-the-Challenge) [@lyak-pass-the-challenge], plaintext-secret protocols (CredSSP / NTLMv1 / MS-CHAPv2 / Digest are unsupported with the trustlet-protected token), and liveness (denial of service against VTL1 is out of the VBS threat model) -- with citations [@msdocs-credential-guard] [@amar-bh2020].
</FAQItem>

<FAQItem question="If a VTL1 trustlet is compromised, are all bets off?">
For that trustlet, yes; for the model, by design. The Secure Kernel plus trustlets are the VBS TCB. Amar and King's 2020 work demonstrated practical VTL0-to-VTL1 vulnerabilities (CVE-2020-0917, CVE-2020-0918) [@amar-bh2020] [@nvd-cve-2020-0917] [@nvd-cve-2020-0918]; Microsoft hardened in response, moving the Secure Kernel pool to segment heap, reducing four W+X regions to +X only, and introducing `SkpgContext` HyperGuard for VTL1 [@amar-bh2020]. The surface remains finite and audited; the trustlet model is hypervisor-strong against the VTL0 kernel and not stronger than the substrate it sits on.
</FAQItem>

<FAQItem question="Is Windows Hello biometric data stored in the TPM?">
Not on ESS-capable systems. The Microsoft Learn page is clear that *"when ESS is enabled, the face algorithm is protected using VBS to isolate it from the rest of Windows... The hypervisor is used to specify and protect memory regions, so that they can only be accessed by processes running in VBS"* [@msdocs-ess]. The biometric *template* is encrypted with VBS-only keys and lives in VBS-isolated memory. The TPM still has a role -- it holds the per-user Hello *private keys* that authenticate against the local credential provider -- but the biometric template itself does not live in the TPM [@msdocs-tpm].
</FAQItem>

<FAQItem question="Does Administrator Protection remove UAC?">
No. The Microsoft Learn page describes the new model: an authorised user triggers a Windows Hello-backed prompt; Windows then *"uses a hidden, system-generated, profile-separated user account to create an isolated admin token. This token is issued to the requesting process and is destroyed once the process ends"* [@msdocs-admin-protection]. The in-session prompt is still there; the elevated token's *origin* is what changed (from a split-token impersonation of the same account to a transient system-generated admin account). The October 2025 preview shipped in KB5067036 and was then reverted in the same update note pending a future rollout [@kb5067036]. As of 10 May 2026 the feature is not generally available.
</FAQItem>

</FAQ>

<StudyGuide slug="vbs-trustlets-what-actually-runs-in-the-secure-kernel" keyTerms={[
  { term: "Trustlet", definition: "A user-mode process running in VTL1 user mode, scheduled by the Secure Kernel, isolated from VTL0 by per-VTL SLAT permissions. Defined by passing five load-time gates." },
  { term: "Virtual Trust Level (VTL)", definition: "A hypervisor-managed privilege axis added on top of x86 rings. Currently two VTLs are implemented out of an architecturally supported sixteen." },
  { term: "Isolated User Mode (IUM)", definition: "Ring 3 of VTL1. The user-mode environment trustlets run in. Restricted to about 48 of NT's ~480 syscalls." },
  { term: "Secure Kernel", definition: "The kernel that runs in VTL1 ring 0. Schedules trustlets, parses .tpolicy sections, enforces SkCapabilities rules on secure-call invocations." },
  { term: "IUM EKU", definition: "The Enhanced Key Usage OID 1.3.6.1.4.1.311.10.3.37. Required alongside the Windows System Component Verification EKU for a binary to be loaded as a trustlet at Signature Level 12." },
  { term: "Trustlet Instance GUID", definition: "A runtime identifier the Secure Kernel uses to scope per-instance secrets. Set via IumSetTrustletInstance; shared between cooperating trustlets (e.g., vmsp.exe and the vTPM provisioning trustlet) so they can read each other's storage blobs under SkCapabilities control." },
  { term: "Malwarelet", definition: "Ionescu's term for an attacker-controlled trustlet, enabled by a Test Signing or Secure Boot compromise rather than by a trustlet-internal bug." },
  { term: "ALPC", definition: "Asynchronous Local Procedure Call: Windows IPC primitive used by VTL0 agent processes to communicate with their VTL1 trustlet counterparts." }
]} questions={[
  { q: "Name the five gates a Windows binary must pass at load time to become a trustlet.", a: "(1) PsAttributeSecureProcess process attribute with a 64-bit Trustlet ID. (2) Two EKUs at Signature Level 12: Windows System Component Verification (1.3.6.1.4.1.311.10.3.6) and IUM (1.3.6.1.4.1.311.10.3.37). (3) A .tpolicy PE section exporting s_IumPolicyMetadata with matching Trustlet ID. (4) A Trustlet Instance GUID bound via IumSetTrustletInstance. (5) The stripped-down LdrpIsSecureProcess loader path." },
  { q: "Why does a SYSTEM-privilege NT-kernel write primitive on Windows 11 25H2 fail to read LsaIso.exe memory?", a: "Because the NT kernel runs in VTL0, LsaIso.exe runs in VTL1, and the Hyper-V hypervisor configures per-VTL SLAT entries that refuse VTL0 read access to VTL1-only pages. The attacker's kernel write primitive can edit NT kernel structures but cannot change the hypervisor-managed SLAT entries." },
  { q: "What does Pass-the-Challenge demonstrate about the limits of Credential Guard?", a: "That while the NTLM hash itself never leaves VTL1, the agent process (lsass.exe in VTL0) can be asked to ask the trustlet to compute an authentication response for an attacker-supplied challenge. The resulting response is reachable by the VTL0 attacker and is sufficient for many relay attacks. The hash is protected; the authentication outcomes it produces are not." },
  { q: "What is the practical floor of the trustlet attack surface that Amar and King exposed at Black Hat USA 2020?", a: "The secure-call interface (IumInvokeSecureService) parses VTL0-controlled inputs in VTL1. Hyperseed retargeted at it found five VTL0->VTL1 bugs in two weeks, including CVE-2020-0917 (OOB read in the secure-call surface) and CVE-2020-0918 (SkmmUnmapMdl design flaw). Microsoft responded with segment-heap migration, W+X reduction, and SkpgContext (Secure Kernel HyperGuard)." },
  { q: "What is the third-party equivalent of an inbox trustlet on Windows 11 24H2 and later?", a: "A VBS Enclave: a DLL signed with a Trusted Signing certificate and loaded into an enclave region of a host process via CreateEnclave / CallEnclave. Requires Windows 11 Build 26100.2314 or later, Windows SDK 10.0.22621.3233 or later, and Visual Studio 2022 17.9 or later." }
]} />
