<?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: sandbox</title><description>Posts tagged sandbox.</description><link>https://paragmali.com/</link><language>en-US</language><lastBuildDate>Sun, 07 Jun 2026 04:13:09 GMT</lastBuildDate><atom:link href="https://paragmali.com/tags/sandbox/rss.xml" rel="self" type="application/rss+xml"/><item><title>Windows Sandbox vs Windows Defender Application Guard: Two Hyper-V Sandboxes, Different Threat Models</title><link>https://paragmali.com/blog/windows-sandbox-vs-wdag/</link><guid isPermaLink="true">https://paragmali.com/blog/windows-sandbox-vs-wdag/</guid><description>Two Hyper-V-backed isolation containers shipped in Windows -- one survived, one was retired. The story of why disposable beat persistent, and what each model was actually for.</description><pubDate>Thu, 14 May 2026 00:00:00 GMT</pubDate><content:encoded>
**Two Windows features, the same plumbing, opposite fates.** Windows Sandbox (2019) and Windows Defender Application Guard (2017) both spin up a Hyper-V child partition using the Host Compute Service (HCS) API [@learn-microsoft-com-hcs-overview] on top of the same Virtualization-Based Security [@learn-microsoft-com-oem-vbs] substrate. Sandbox is disposable, on-demand, and aimed at running an untrusted executable. WDAG was persistent, automatic, and aimed at rendering an untrusted website inside Microsoft Edge. WDAG was deprecated for Edge [@learn-microsoft-com-application-guard] and then removed entirely in Windows 11 24H2 [@learn-microsoft-com-guard-overview]; Sandbox still ships. The reason is not that one model was wrong -- it is that operational economics and threat models diverged. This article explains the shared substrate, the architectural differences, the deprecation story, and what replaced WDAG in the 2026 Windows isolation stack.
&lt;h2&gt;1. The Hyper-V Isolation Layer Both Features Share&lt;/h2&gt;
&lt;p&gt;Open two Microsoft Learn pages side by side -- the Windows Sandbox architecture page [@learn-microsoft-com-sandbox-architecture] and the Microsoft Defender Application Guard overview [@learn-microsoft-com-guard-overview] -- and the descriptions almost rhyme. The MDAG overview opens by calling its container an &quot;isolated Hyper-V-enabled container&quot;; the Sandbox architecture page describes its guest as a dynamically generated, &quot;kernel-isolated&quot; Windows image that the host can destroy. Two pages, two teams, the same noun and the same verb. The two features are siblings: built by overlapping teams, on top of the exact same compute substrate, and shipped roughly two years apart.&lt;/p&gt;
&lt;p&gt;That substrate is named, and worth naming carefully, because the rest of the article rests on it.&lt;/p&gt;

The Windows API that creates, starts, configures, queries, and destroys Hyper-V &quot;compute systems&quot; -- the umbrella term Microsoft uses for either a virtual machine or a container with its own kernel. The HCS reference docs [@learn-microsoft-com-reference-apioverview] list functions like `HcsCreateComputeSystem`, `HcsStartComputeSystem`, and `HcsGetComputeSystemProperties` -- a kernel32-shaped surface for child partitions, parameterized by JSON.
&lt;p&gt;When Microsoft first documented HCS publicly, it framed it as a kernel32-equivalent for child partitions. The HCS overview [@learn-microsoft-com-hcs-overview] describes the API in terms of &lt;em&gt;compute systems&lt;/em&gt; (the umbrella term for either a virtual machine or a container with its own kernel), with &quot;configurations and properties... stored in a JSON file which will then be passed through the HCS APIs to create the compute system.&quot; The API reference [@learn-microsoft-com-reference-apioverview] notes that the DLL &quot;exports a set of C-style Windows API functions, using JSON schema as configuration&quot;; Microsoft&apos;s &lt;code&gt;hcsshim&lt;/code&gt; repository [@github-com-microsoft-hcsshim] provides the Go binding used by Moby and containerd. The framing matters. HCS is not a sandbox. It is the &lt;em&gt;mechanism&lt;/em&gt; a sandbox feature uses to ask Hyper-V for &quot;a kernel of my own, isolated from yours, please.&quot; What the consumer does with that kernel -- what it boots, what it shares, when it tears it down -- is the threat model. That is where Sandbox and WDAG diverge.&lt;/p&gt;
&lt;p&gt;Underneath HCS sits Hyper-V itself, and underneath Hyper-V sits Virtualization-Based Security (VBS).&lt;/p&gt;

A Windows feature that runs the normal Windows kernel as a Hyper-V guest, then uses a second, smaller secure kernel running at a higher Virtual Trust Level (VTL1) to isolate keys, policies, and code-integrity decisions from the main kernel. The OEM VBS guidance [@learn-microsoft-com-oem-vbs] documents the boot path: the hypervisor launches first, then hosts the NT kernel in a child partition.
&lt;p&gt;VBS itself does not run untrusted user code. Its job is to give Windows a hypervisor that ships in the box on every supported SKU and that is anchored by Secure Boot. Once that hypervisor exists, the cost of &quot;spawn a second Windows guest just for this task&quot; stops being &quot;boot a full VM (minutes, gigabytes)&quot; and starts being &quot;ask HCS for a child partition (seconds, hundreds of megabytes).&quot; Hyper-V on Windows [@learn-microsoft-com-windows-about] is the desktop face of that hypervisor; Hyper-V isolation containers [@learn-microsoft-com-hyperv-container] on Windows Server are the server face. Sandbox and WDAG are two desktop-flavored consumers of the same pipe.&lt;/p&gt;

flowchart TD
    HW[Hardware: VT-x/AMD-V, IOMMU, TPM]
    HV[Hyper-V hypervisor]
    Root[Root partition: host NT kernel]
    HCS[Host Compute Service API]
    WS[Windows Sandbox child partition]
    WDAG[WDAG child partition]
    HVC[Hyper-V Windows Container]
    HW --&amp;gt; HV
    HV --&amp;gt; Root
    Root --&amp;gt; HCS
    HCS --&amp;gt; WS
    HCS --&amp;gt; WDAG
    HCS --&amp;gt; HVC
&lt;p&gt;Notice the diagram&apos;s most important detail: the host NT kernel is &lt;em&gt;also&lt;/em&gt; a Hyper-V guest. It runs in the root partition, which has full physical-device access; the sandbox and WDAG partitions run as L1 guests with no physical-device access at all. The Hyper-V VM boundary -- the membrane between root and L1 -- is what Microsoft commits to defending. That commitment is published: the Microsoft Security Servicing Criteria for Windows [@microsoft-com-servicing-criteria] names the Hyper-V VM as a serviced security boundary, and the Hyper-V Bounty Program [@microsoft-com-hyper-v] pays up to $250,000 for a guest-to-host escape. The boundary has a dollar sign attached to it, which is how you know it counts.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The Hyper-V VM boundary is the serviced security boundary. Windows Sandbox and WDAG ride on top of that boundary; neither product &lt;em&gt;is&lt;/em&gt; the boundary. Bugs in the host-side broker, the clipboard channel, or the policy engine are not Hyper-V escapes -- they are integration bugs in features that happen to run inside a VM.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This distinction matters operationally. When the Windows Sandbox configuration docs [@learn-microsoft-com-wsb-file] warn that enabling vGPU &quot;can potentially increase the attack surface of the sandbox,&quot; they are talking about widening a brokered channel from the L1 guest into the root partition&apos;s display stack. That channel runs &lt;em&gt;through&lt;/em&gt; the VM boundary; abusing it does not require breaking Hyper-V. The same logic applies to mapped folders, the clipboard, networking, and the Edge-window remoting path WDAG used. Each is a deliberate hole in the boundary, made for a reason, with its own threat model.&lt;/p&gt;
&lt;p&gt;With the substrate established, the next question is what Sandbox does on top of it. The answer turns on a single observation: the host already has a Windows image. Why download another one?&lt;/p&gt;
&lt;h2&gt;2. Windows Sandbox: The Disposable Desktop&lt;/h2&gt;
&lt;p&gt;Windows Sandbox shipped in Windows 10 1903 [@learn-microsoft-com-sandbox-install], the May 2019 update, as an optional Windows feature named &lt;code&gt;Containers-DisposableClientVM&lt;/code&gt; -- the install page documents both the version requirement and the exact PowerShell feature name. The naming is loud about the design goal. A &lt;em&gt;container&lt;/em&gt; (rather than a VM you manage with &lt;code&gt;vmconnect&lt;/code&gt;), &lt;em&gt;disposable&lt;/em&gt; (state is destroyed on close), for &lt;em&gt;client&lt;/em&gt; workloads (interactive desktop apps, not server workloads). Microsoft&apos;s original Windows Sandbox announcement on the Kernel Internals blog [@web-archive-org-p-301849] (preserved on the Internet Archive after Microsoft retired the canonical URL) frames the use cases plainly: trying out an installer, opening a suspicious download, testing an executable from email -- the same use cases the current Sandbox overview [@learn-microsoft-com-sandbox-overview] still enumerates.&lt;/p&gt;
&lt;p&gt;The trick that makes this feel cheap is the &lt;em&gt;dynamic base image&lt;/em&gt;.&lt;/p&gt;

The disk template Windows Sandbox uses to boot its guest. According to the Windows Sandbox architecture page [@learn-microsoft-com-sandbox-architecture], the on-disk package is approximately 30 MB compressed and expands to about 500 MB when installed. The expansion is mostly *pointers* back to the host&apos;s own immutable OS files; pristine private copies exist only for files the guest may legitimately mutate.
&lt;p&gt;A traditional VM ships a 4-8 GB VHDX with its own copy of Windows. The dynamic base image inverts that. Read-only files in the host -- &lt;code&gt;ntdll.dll&lt;/code&gt;, the bulk of &lt;code&gt;System32&lt;/code&gt;, the side-by-side cache -- are reflected into the guest by reference. The guest sees a complete Windows install at boot. The host barely paid for it.&lt;/p&gt;
&lt;p&gt;Memory uses an even sharper trick: &lt;em&gt;direct map&lt;/em&gt;.&lt;/p&gt;

A Hyper-V memory-sharing optimization, described on the Windows Sandbox architecture page [@learn-microsoft-com-sandbox-architecture], in which immutable physical pages are shared read-only between the host and the sandbox guest. When the guest loads `ntdll.dll`, the same physical RAM that already holds `ntdll.dll` on the host is mapped into the guest&apos;s address space rather than duplicated.
&lt;p&gt;The page is read-only from both sides, so a guest exploit cannot scribble into the host&apos;s copy. The win is memory pressure: dozens of megabytes for the guest&apos;s mutable state instead of hundreds of megabytes for a fresh Windows kernel image. The same architecture page describes the memory model directly: &quot;containers collaborate with the host to dynamically determine how host resources are allocated. This method is similar to how processes normally compete for memory on the host... If the host is under memory pressure, it can reclaim memory from the container much like it would with a process.&quot; A sandbox that does little uses little, and a sandbox that does a lot pulls pages from the host the same way a heavy host process would.Direct map is conceptually the same trick Linux uses to share &lt;code&gt;glibc&lt;/code&gt; between processes -- the same physical pages of a read-only binary backing many virtual address spaces. The Windows Sandbox application is to use that primitive across a Hyper-V partition boundary, not just across processes inside one kernel.&lt;/p&gt;
&lt;p&gt;Graphics use a third mechanism. A vGPU runs the guest&apos;s display through WDDM 2.5+, sharing the host GPU much like another process on the host would. When the host GPU lacks a compatible WDDM driver, the guest falls back to WARP, Microsoft&apos;s CPU-backed Direct3D rasterizer. The same Sandbox architecture page [@learn-microsoft-com-sandbox-architecture] cited above documents both branches: &quot;a system with a compatible GPU and graphics drivers (WDDM 2.5 or newer) is required. Incompatible systems render apps in Windows Sandbox with Microsoft&apos;s CPU-based rendering technology, Windows Advanced Rasterization Platform (WARP).&quot; That fallback is slow but means Sandbox starts on essentially any Pro+ Windows 10/11 install, not only on machines with modern discrete GPUs.&lt;/p&gt;

sequenceDiagram
    participant U as User
    participant Host as Host (root partition)
    participant HCS as HCS API
    participant G as Sandbox guest
    U-&amp;gt;&amp;gt;Host: Launch WindowsSandbox.exe (with optional .wsb)
    Host-&amp;gt;&amp;gt;HCS: HcsCreateComputeSystem(JSON)
    HCS-&amp;gt;&amp;gt;G: Allocate partition, mount dynamic base image
    HCS-&amp;gt;&amp;gt;G: Direct-map immutable host pages
    HCS-&amp;gt;&amp;gt;G: Start guest (boot stripped NT, run LogonCommand)
    G--&amp;gt;&amp;gt;Host: RDP-style remoting of desktop
    U-&amp;gt;&amp;gt;G: Drop .exe, run, observe
    U-&amp;gt;&amp;gt;Host: Close Sandbox window
    Host-&amp;gt;&amp;gt;HCS: HcsTerminateComputeSystem
    HCS-&amp;gt;&amp;gt;G: Destroy partition, discard all writable state
&lt;p&gt;What the user &lt;em&gt;configures&lt;/em&gt; is the small set of seams between guest and host. Configuration lives in a &lt;code&gt;.wsb&lt;/code&gt; file, an XML document the user double-clicks to launch a customized sandbox. The Windows Sandbox configuration page [@learn-microsoft-com-wsb-file] enumerates every supported element: &lt;code&gt;&amp;lt;vGPU&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;Networking&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;MappedFolders&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;LogonCommand&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;MemoryInMB&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;AudioInput&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;VideoInput&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;ProtectedClient&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;PrinterRedirection&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;ClipboardRedirection&amp;gt;&lt;/code&gt;. Each line is a knob on the boundary.&lt;/p&gt;
&lt;p&gt;A minimal hostile-binary harness disables almost every shared channel:&lt;/p&gt;
&lt;p&gt;{&lt;code&gt;// Produce a Windows Sandbox configuration that minimizes shared channels. // Run this in Node, save the output as triage.wsb, then double-click it. const sample = &quot;C:\\\\Users\\\\analyst\\\\Samples\\\\suspicious.exe&quot;; const wsb = [   &quot;&amp;lt;Configuration&amp;gt;&quot;,   &quot;  &amp;lt;VGpu&amp;gt;Disable&amp;lt;/VGpu&amp;gt;&quot;,   &quot;  &amp;lt;Networking&amp;gt;Disable&amp;lt;/Networking&amp;gt;&quot;,   &quot;  &amp;lt;AudioInput&amp;gt;Disable&amp;lt;/AudioInput&amp;gt;&quot;,   &quot;  &amp;lt;VideoInput&amp;gt;Disable&amp;lt;/VideoInput&amp;gt;&quot;,   &quot;  &amp;lt;PrinterRedirection&amp;gt;Disable&amp;lt;/PrinterRedirection&amp;gt;&quot;,   &quot;  &amp;lt;ClipboardRedirection&amp;gt;Disable&amp;lt;/ClipboardRedirection&amp;gt;&quot;,   &quot;  &amp;lt;ProtectedClient&amp;gt;Enable&amp;lt;/ProtectedClient&amp;gt;&quot;,   &quot;  &amp;lt;MappedFolders&amp;gt;&quot;,   &quot;    &amp;lt;MappedFolder&amp;gt;&quot;,   &quot;      &amp;lt;HostFolder&amp;gt;C:\\\\Users\\\\analyst\\\\Samples&amp;lt;/HostFolder&amp;gt;&quot;,   &quot;      &amp;lt;SandboxFolder&amp;gt;C:\\\\Samples&amp;lt;/SandboxFolder&amp;gt;&quot;,   &quot;      &amp;lt;ReadOnly&amp;gt;true&amp;lt;/ReadOnly&amp;gt;&quot;,   &quot;    &amp;lt;/MappedFolder&amp;gt;&quot;,   &quot;  &amp;lt;/MappedFolders&amp;gt;&quot;,   &quot;&amp;lt;/Configuration&amp;gt;&quot; ].join(&quot;\\n&quot;); console.log(wsb); console.log(&quot;\\nSample path inside guest: &quot; + sample.replace(&quot;C:\\\\Users\\\\analyst&quot;, &quot;C:&quot;));&lt;/code&gt;}&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The default &lt;code&gt;.wsb&lt;/code&gt;-less Windows Sandbox launch enables networking and clipboard redirection. The configuration docs [@learn-microsoft-com-wsb-file] warn that enabled networking &quot;could expose untrusted applications to the internal network.&quot; For malware triage, the explicit &lt;code&gt;&amp;lt;Networking&amp;gt;Disable&amp;lt;/Networking&amp;gt;&lt;/code&gt; form above is the right starting point.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A few cost-of-doing-business limits are worth flagging up front. Only one Sandbox can run at a time, per the Sandbox overview [@learn-microsoft-com-sandbox-overview], which also restricts the feature to Pro/Enterprise/Education SKUs (&quot;Windows Sandbox is currently not supported on Windows Home edition&quot;) and inherits the VBS-capable-hardware requirement from the OEM VBS guidance [@learn-microsoft-com-oem-vbs]. And while in-sandbox state survives a &lt;code&gt;shutdown /r&lt;/code&gt; inside the guest (a Windows 11 22H2 refinement called out in the same overview page), state still dies when the host UI window closes -- &quot;disposable&quot; is the contract, not a marketing word.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;Containers-DisposableClientVM&lt;/code&gt; partition, then, is essentially a Hyper-V container with a desktop bolted on, a shared OS image, a few configurable channels, and a strict &quot;destroy on close&quot; lifecycle. WDAG, the older sibling, took the same building blocks and arranged them around a completely different question: not &quot;run this executable once&quot; but &quot;render this website transparently.&quot;&lt;/p&gt;
&lt;h2&gt;3. WDAG: The Persistent Browser Container&lt;/h2&gt;
&lt;p&gt;Windows Defender Application Guard shipped in Windows 10 1709 -- the Fall Creators Update [@learn-microsoft-com-build-16299] (Microsoft&apos;s UWP what&apos;s-new page identifies &quot;Windows 10 build 16299 (also known as the Fall Creators Update or version 1709)&quot;), with a GA start date of October 17, 2017 [@learn-microsoft-com-and-education] per the Microsoft Lifecycle page. The WDAG install guide [@learn-microsoft-com-app-guard] still lists standalone-mode support starting at &quot;Windows 10 Enterprise edition, version 1709 and later.&quot; At launch it integrated with the legacy EdgeHTML-based Edge; later it integrated with Chromium-based Edge for Business and, beginning in 2020, with Microsoft 365 Apps for Enterprise to wrap untrusted Office documents -- the MDAG overview [@learn-microsoft-com-guard-overview] explicitly enumerates the file types: &quot;Application Guard helps prevent untrusted Word, PowerPoint, and Excel files from accessing trusted resources.&quot; The product was rebranded from &quot;Windows Defender Application Guard&quot; to &quot;Microsoft Defender Application Guard&quot; along the way -- the &lt;code&gt;isolatedapplauncher.h&lt;/code&gt; header notes [@learn-microsoft-com-api-isolatedapplauncher] state directly that &quot;Windows Defender Application Guard (WDAG) is now Microsoft Defender Application Guard (MDAG). The WDAG name is deprecated, but it is still used in some APIs.&quot; This article uses WDAG for continuity.&lt;/p&gt;
&lt;p&gt;The use case was specific and corporate: in a managed enterprise, an employee may need to follow a customer&apos;s link, open an inbound attachment, or read a marketing site -- content that originates &lt;em&gt;outside&lt;/em&gt; the organization&apos;s network perimeter. WDAG&apos;s job was to render that content in a partition that &lt;em&gt;cannot&lt;/em&gt; talk to the intranet, so that a renderer exploit chained to a kernel LPE inside the guest could not pivot to corporate file shares, Active Directory, or the user&apos;s own home directory.&lt;/p&gt;
&lt;p&gt;The trust boundary was policy-defined.&lt;/p&gt;

The Group Policy mechanism Windows uses to enumerate &quot;what counts as inside the enterprise network,&quot; documented under Configure Microsoft Defender Application Guard [@learn-microsoft-com-app-guard-2]. Administrators populate Enterprise Network Domains, Cloud Resources, Internal Proxies, and IPv4/IPv6 Subnets. Anything inside the list is rendered in the host browser; anything outside is reissued inside the WDAG container.
&lt;p&gt;This is the property Windows Sandbox does not have. Sandbox is a manual tool; WDAG was transparent. Click an external link in Outlook, and a separate Edge window opens hosting the rendered page -- the user did not have to know about, configure, or invoke a sandbox. The host-side broker forwarded the navigation; the in-container Edge rendered the page; an RDP-family remoting path relayed pixels back to the host UI. Microsoft has not publicly named the inner protocol, but its visible behavior -- per-window remoting of a single app onto the host desktop -- is the signature of the Remote Applications Integrated Locally (RAIL) virtual channel specified in &lt;a href=&quot;https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-rdperp/&quot; rel=&quot;noopener&quot;&gt;[MS-RDPERP]&lt;/a&gt;, the RDP extension that &quot;presents a remote application... as a local user application.&quot; The implementation may differ in detail; the architectural shape is the same.&lt;/p&gt;
&lt;p&gt;Where Sandbox is &quot;destroy on close,&quot; WDAG was &quot;warm at logon.&quot;&lt;/p&gt;

sequenceDiagram
    participant U as User
    participant Logon as Winlogon
    participant Broker as Host WDAG broker
    participant Cont as WDAG container
    participant Edge as Container Edge
    Logon-&amp;gt;&amp;gt;Broker: User signs in
    Broker-&amp;gt;&amp;gt;Cont: HCS pre-warm container (pruned image)
    Cont-&amp;gt;&amp;gt;Edge: Boot, idle, await navigation
    U-&amp;gt;&amp;gt;Broker: Click https://external.example
    Broker-&amp;gt;&amp;gt;Broker: Network Isolation policy check (out of zone)
    Broker-&amp;gt;&amp;gt;Edge: Reissue URL inside container
    Edge--&amp;gt;&amp;gt;Broker: Render via RDP-family remoting (RAIL-shaped)
    Broker--&amp;gt;&amp;gt;U: Display container window on host desktop
&lt;p&gt;The configuration surface was correspondingly broader than Sandbox&apos;s &lt;code&gt;.wsb&lt;/code&gt;. Group Policy settings controlled clipboard direction (upload, download, both, neither), file print to host, microphone and camera access, hardware acceleration, and -- most operationally consequential -- whether downloads escape the container at all. The Microsoft Edge WDAG configuration guidance [@learn-microsoft-com-application-guard] listed knobs like &lt;code&gt;ApplicationGuardUploadBlockingEnabled&lt;/code&gt; and &lt;code&gt;ApplicationGuardPassiveModeEnabled&lt;/code&gt; for security-versus-usability tuning.&lt;/p&gt;
&lt;p&gt;WDAG also exposed a small detection API for applications running inside it, the &lt;code&gt;IsolatedAppLauncher&lt;/code&gt; COM interface [@learn-microsoft-com-api-isolatedapplauncher]. The methods &lt;code&gt;IsProcessInWDAGContainer&lt;/code&gt; and &lt;code&gt;IsProcessInIsolatedContainer&lt;/code&gt; let an app know &quot;am I in the guest?&quot; -- useful for, say, disabling drivers that cannot work inside the partition. The header carries the deprecation notice today.The detection-from-inside API is itself a useful tell about WDAG&apos;s threat model. If the guest needed to &lt;em&gt;know&lt;/em&gt; it was the guest, that means software running inside it could legitimately do guest-specific things -- license activation, optional installers, telemetry. WDAG was meant to host fully functional applications, not a stripped renderer. That breadth is part of what made its operational cost real.&lt;/p&gt;
&lt;p&gt;What it improved over the AppContainer-wrapped Edge of 2016 was substantial. A renderer-to-kernel exploit in the container&apos;s stripped Windows guest landed in a throwaway kernel that had no view of the host filesystem, no route to the corporate intranet, no host clipboard write path, and no persistence across reboot. The kernel attack surface of the partition is, &lt;em&gt;in the limit&lt;/em&gt;, the Hyper-V VM boundary. That is a serviced Microsoft boundary; an AppContainer-wrapped Edge is not.&lt;/p&gt;
&lt;p&gt;So why was WDAG retired and Sandbox kept? Because in the WDAG threat model, the &quot;warm partition for every employee, all day long, on every device&quot; became a tax that the in-browser sandbox could finally outrun.&lt;/p&gt;
&lt;h2&gt;4. The Lifecycle Divergence&lt;/h2&gt;
&lt;p&gt;The clearest way to see why two siblings on the same substrate diverged is to put their lifecycles side by side. Sandbox boots on user gesture, runs as long as one window stays open, dies on close. WDAG booted at user logon, stayed resident the entire session, and discarded state only when the user signed out (or, for the persistent variant, never). The same Hyper-V partition primitive, allocated at very different points in the day, for very different durations, was being asked to solve very different problems.&lt;/p&gt;

The point at which a security primitive&apos;s instance lifetime is anchored. Sandbox is *gesture-bound*: an instance exists because a user explicitly launched it. WDAG was *session-bound*: an instance existed because a user signed in. AppContainer is *process-bound*: an instance exists because a sandboxed binary is running. Each binding implies a different cost model and a different set of failure modes.
&lt;p&gt;Gesture binding is cheap in expectation. Most users open Sandbox seldom -- when a particular file looks suspicious, when an installer wants admin rights for unclear reasons, when an analyst is reproducing a sample. The cost of the partition is paid on demand and only by the user who needed it. The &lt;code&gt;Containers-DisposableClientVM&lt;/code&gt; feature in the Sandbox install docs [@learn-microsoft-com-sandbox-install] ships an &lt;em&gt;option&lt;/em&gt;: enabled but unused, it costs nothing beyond the dynamic base image on disk.&lt;/p&gt;
&lt;p&gt;Session binding has the opposite cost profile. Every employee, on every device, pays for a Hyper-V child partition at every logon, whether or not they ever browse an untrusted URL. The partition holds memory. The host-side broker holds memory. The pre-warmed Edge holds memory. The Network Isolation policy must be evaluated on every navigation. The clipboard, downloads, and print paths require ongoing brokering. Even on a workstation that idles all day in front of an intranet portal, WDAG was a tax line item.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; A serviced security boundary is cheap when it is allocated by gesture and expensive when it is allocated by session. WDAG bet that the per-session cost would amortize against frequent untrusted browsing; that bet failed for most enterprise users because the marginal extra protection over an in-browser sandbox (Edge ESM + ACG + CET) was small for the &lt;em&gt;typical&lt;/em&gt; navigation, even though it remained large for the &lt;em&gt;worst-case&lt;/em&gt; navigation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The threat models also point in opposite directions. Sandbox optimizes for a single, contained interaction with an untrusted artifact: drop the &lt;code&gt;.exe&lt;/code&gt;, run it, watch what happens, close the window. Anything the artifact does inside the partition -- registry writes, scheduled-task creation, persistence attempts -- is annihilated when the window closes. WDAG was optimizing for the opposite: many small, transparent interactions with untrusted content (clicks, navigations, document opens), all of which had to feel seamless or the user would route around them.&lt;/p&gt;

gantt
    title Sandbox vs WDAG lifecycle on a typical 8-hour workday
    dateFormat HH:mm
    axisFormat %H:%M
    section Windows Sandbox
    Idle (no cost) :done, ws1, 09:00, 11:00
    Analyst opens suspicious.exe :crit, ws2, 11:00, 11:15
    Idle (no cost) :done, ws3, 11:15, 16:00
    Test installer :crit, ws4, 16:00, 16:10
    Idle (no cost) :done, ws5, 16:10, 17:00
    section WDAG (legacy)
    Pre-warmed partition resident :crit, wd1, 09:00, 17:00
&lt;p&gt;The lifecycle asymmetry also asymmetrically rewards engineering effort. Hardening an in-browser renderer pays off thousands of times per session -- every page load benefits. Hardening a per-employee Hyper-V partition pays off only on the navigations that actually leave the trusted zone, which for most users is a small fraction of clicks. As the in-browser side became dramatically stronger between 2018 and 2023 -- Site Isolation, V8 sandboxing, Arbitrary Code Guard (ACG), Control-flow Enforcement Technology (CET), the new Edge Enhanced Security Mode -- the marginal value of WDAG dropped while its operational cost did not.&lt;/p&gt;
&lt;p&gt;That set up the deprecation decision.&lt;/p&gt;
&lt;h2&gt;5. The Deprecation Decision&lt;/h2&gt;
&lt;p&gt;The retirement happened in two visible steps and a long invisible runway.&lt;/p&gt;
&lt;p&gt;The first visible step was the Edge-side deprecation, announced in 2023. The Microsoft Edge and Microsoft Defender Application Guard [@learn-microsoft-com-application-guard] page now opens with the banner: &quot;Microsoft Defender Application Guard, including the Windows Isolated App Launcher APIs, is deprecated for Microsoft Edge for Business and will no longer be updated.&quot; The same page makes the operational substitute explicit: &quot;The additional security features in Edge make it very secure without needing Application Guard,&quot; then enumerates Defender SmartScreen, Enhanced Security Mode, website typo protection, and Data Loss Prevention as the &lt;em&gt;replacement set&lt;/em&gt; for the WDAG-for-Edge scenario.&lt;/p&gt;
&lt;p&gt;The second visible step was the OS-level removal in Windows 11 version 24H2. The MDAG overview banner [@learn-microsoft-com-guard-overview] is unambiguous: &quot;Starting with Windows 11, version 24H2, Microsoft Defender Application Guard, including the Windows Isolated App Launcher APIs, is no longer available.&quot; The &lt;code&gt;isolatedapplauncher.h&lt;/code&gt; API reference [@learn-microsoft-com-api-isolatedapplauncher] carries the matching deprecation notice for the COM surface. Code paths that called &lt;code&gt;IIsolatedAppLauncher&lt;/code&gt; on a 24H2 box now hit a removed feature; the API names remain in the documentation as historical record.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The Edge-side deprecation and the OS-side removal in Windows 11 24H2 are often conflated in coverage. They are different. The Edge-side deprecation stopped updates and the New-Tab-Page entry point; existing fleets on earlier Windows versions retained the underlying feature. The 24H2 removal pulled the kernel-mode plumbing -- the Sandbox install page [@learn-microsoft-com-sandbox-install] even calls out a side-effect: &quot;Beginning in Windows 11, version 24H2, inbox store apps like Calculator, Photos, Notepad and Terminal are not available inside Windows Sandbox,&quot; because the underlying app-isolation broker was reworked as part of the same cleanup.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The invisible runway behind those two banners is the more interesting story. Microsoft&apos;s Security Servicing Criteria for Windows [@microsoft-com-servicing-criteria] page names &quot;Hyper-V VM&quot; as a serviced security boundary but does not name WDAG or Application Guard. WDAG was always a &lt;em&gt;feature&lt;/em&gt; that &lt;em&gt;used&lt;/em&gt; the Hyper-V VM boundary; it was never the boundary itself. A bug in the WDAG broker, the Network Isolation policy evaluator, the clipboard channel, or the host-side window remoting was an integration bug, not a Hyper-V escape, and so was never going to attract the kind of bounty payout -- &quot;up to $250,000 USD&quot; per the Hyper-V Bounty Program [@microsoft-com-hyper-v] -- that the underlying boundary attracts. The economic shape of WDAG&apos;s bug surface always favored deprecation: it was a complex, brokered feature whose worst plausible CVE was a privilege-escalation inside Edge, not a guest-to-host RCE.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; WDAG&apos;s deprecation was overdetermined. (1) The session-bound cost model could not be amortized for most users. (2) The in-browser mitigations (ACG, CET, Edge ESM, Site Isolation) closed the marginal-security gap on the &lt;em&gt;typical&lt;/em&gt; navigation. (3) The integration-bug class -- broker, clipboard, policy -- was never going to be on Microsoft&apos;s serviced security boundary list. Each reason alone could have justified retirement; all three together made it inevitable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What was &lt;em&gt;kept&lt;/em&gt; is just as telling. The Hyper-V VM boundary stayed; the bounty program stayed; the HCS API stayed; Windows Sandbox stayed; Hyper-V isolation containers on Windows Server stayed. Microsoft did not retire any of the things that made WDAG technically possible -- only the specific arrangement of those things into a session-bound, transparent, browser-targeted feature. The mechanism was kept; the productization was retired.&lt;/p&gt;
&lt;h2&gt;6. The WDAG Replacement Stack&lt;/h2&gt;
&lt;p&gt;There is no single replacement for WDAG. There is a stack of complementary features, each of which absorbs a slice of WDAG&apos;s old job. The Edge-deprecation page enumerates the substitute set; what follows pulls each item back to its primary source.&lt;/p&gt;
&lt;h3&gt;6.1 Edge Enhanced Security Mode (in-browser sandbox)&lt;/h3&gt;
&lt;p&gt;WDAG-for-Edge is now Microsoft Edge Enhanced Security Mode (ESM). The browse-safer page [@learn-microsoft-com-browse-safer] is explicit about what changed: &quot;Enhanced security mode in Microsoft Edge mitigates memory-related vulnerabilities by disabling just-in-time (JIT) JavaScript compilation and enabling additional operating system protections for the browser. These protections include Hardware-enforced Stack Protection and Arbitrary Code Guard (ACG).&quot;&lt;/p&gt;
&lt;p&gt;Functionally, ESM gives up the Hyper-V partition boundary and replaces it with a set of process-mitigation policies that make the in-browser sandbox materially harder to escape. The renderer still runs on the host kernel, but with JIT disabled (closing a large class of write-then-execute primitives), CET enforcing shadow stacks, and ACG blocking dynamic code generation. For &lt;em&gt;unfamiliar&lt;/em&gt; sites only, the browser flips into this mode automatically; familiar sites keep JIT on for performance.The trade is explicit on the page: &quot;Developers should be aware that the WebAssembly (WASM) interpreter running in enhanced security mode might not yield the expected level of performance.&quot; ESM is consciously slower in exchange for a smaller attack surface, and it concedes that this trade is only worth making on a subset of navigations. WDAG made the same concession at the partition level; ESM makes it at the mitigation level.&lt;/p&gt;
&lt;h3&gt;6.2 Smart App Control (OS-level binary trust)&lt;/h3&gt;
&lt;p&gt;For downloads -- the other half of &quot;untrusted content reaches the user&quot; -- the replacement is Smart App Control [@learn-microsoft-com-control-overview]. The Microsoft Learn page describes it as &quot;an app execution control feature that combines Microsoft&apos;s app intelligence services and Windows&apos; code integrity features to protect users from untrusted or potentially dangerous code.&quot; The Windows 11 Security Book [@learn-microsoft-com-driver-control] clarifies the mechanism: Smart App Control &quot;blocks untrusted or unsigned applications&quot; by predicting safety from a cloud intelligence service, and &quot;blocks unknown script files and macros from the web.&quot;&lt;/p&gt;
&lt;p&gt;The replacement logic is direct. WDAG protected the host kernel from a malicious download by running the download inside a Hyper-V partition. Smart App Control protects the host kernel from a malicious download by &lt;em&gt;not running it at all&lt;/em&gt; unless app intelligence predicts it is safe or it is signed by a trusted CA. The first approach contains the blast radius after execution; the second prevents execution altogether. For the common case -- a user clicking through a suspicious-looking installer -- prevention strictly dominates containment.&lt;/p&gt;
&lt;h3&gt;6.3 Defender for Endpoint network protection (policy-defined trust boundary)&lt;/h3&gt;
&lt;p&gt;For the policy-defined &quot;enterprise vs not-enterprise&quot; boundary that the Network Isolation policy used to draw for WDAG, the replacement is Defender for Endpoint&apos;s network protection [@learn-microsoft-com-network-protection] feature. The page describes it as &quot;expand[ing] the scope of Microsoft Defender SmartScreen to block all outbound HTTP(S) traffic that attempts to connect to poor-reputation sources (based on the domain or hostname),&quot; operating at the OS level. The same page is precise about which processes it covers on Windows: it enforces against non-Microsoft browsers and non-browser processes (for example, PowerShell), and its own Note states that &quot;on Windows, network protection doesn&apos;t monitor Microsoft Edge. For processes other than Microsoft Edge and Internet Explorer, web protection scenarios use network protection for inspection and enforcement.&quot; For Microsoft Edge on Windows, the same reputational source feed (SmartScreen) operates inside the browser; Network Protection is the cross-process extension of that feed to the other process classes.&lt;/p&gt;
&lt;p&gt;The shift is from &lt;em&gt;isolate, then permit anything inside the isolated zone&lt;/em&gt; (WDAG) to &lt;em&gt;enforce a reputational/IOC-based block list at the host network stack&lt;/em&gt; (Defender). The replacement gives up the partition boundary in exchange for matching coverage across every process on the device, not just Edge.&lt;/p&gt;
&lt;h3&gt;6.4 Office Protected View and Office SmartScreen (per-document admission)&lt;/h3&gt;
&lt;p&gt;The Office slice of WDAG -- the variant that wrapped untrusted Word, PowerPoint, and Excel files in a Hyper-V partition -- was always a layered feature on top of an older, cheaper primitive: Office Protected View [@support-microsoft-com-8e43-2bbcdbcb6653]. The Microsoft Support page describes the primitive directly: &quot;files from these potentially unsafe locations are opened as read only or in Protected View. By using Protected View, you can read a file, see its contents and enable editing while reducing the risks.&quot; The page also calls out the WDAG layering explicitly: &quot;If your machine has Application Guard for Microsoft 365 enabled, documents that previously opened in Protected View will now open in Application Guard for Microsoft 365&quot; -- which is exactly the slice that 24H2 removed. Without WDAG, Protected View is the residual primitive: documents from the internet, untrusted senders, or unsafe locations open read-only in a stripped-down Office process until the user opts in to editing.&lt;/p&gt;
&lt;p&gt;Around Protected View sits a second admission layer: SmartScreen-derived reputation checks on the artifact itself. The Microsoft 365 Apps internet-macros guidance [@learn-microsoft-com-macros-blocked] sets the policy directly: &quot;VBA macros are a common way for malicious actors to gain access to deploy malware and ransomware. Therefore, to help improve security in Office, we&apos;re changing the default behavior of Office applications to block macros in files from the internet.&quot; The page describes how Office uses the Mark of the Web -- the same signal SmartScreen uses for binaries -- to decide whether macros in a given document are admitted. Where the WDAG-for-Office configuration would have re-rendered the document inside a Hyper-V partition regardless of macro content, the 2026 replacement turns the question off: macros in internet-origin documents simply do not run.&lt;/p&gt;
&lt;p&gt;Together, the two features are the document analog of Smart App Control + Defender network protection: a read-only fallback for the artifact itself (Protected View) and a reputation-driven admission policy for its riskiest payload (macros). Neither replaces a partition; the union covers WDAG&apos;s Office slice at the cost of giving up the kernel boundary around the document.&lt;/p&gt;

flowchart LR
    WDAG[WDAG legacy feature]
    WDAG --&amp;gt;|Untrusted browsing| ESM[Edge Enhanced Security Mode&lt;br /&gt;JIT off, ACG, CET on unfamiliar sites]
    WDAG --&amp;gt;|Untrusted downloads| SAC[Smart App Control&lt;br /&gt;Cloud-AI binary trust]
    WDAG --&amp;gt;|Network trust zoning| DNP[Defender network protection&lt;br /&gt;Host-stack URL/domain enforcement]
    WDAG --&amp;gt;|Office documents| OFP[Office Protected View / SmartScreen]
&lt;p&gt;The stack is not a one-for-one swap. Each feature trades the Hyper-V VM boundary for something cheaper to operate. The aggregate covers the WDAG threat model at &lt;em&gt;typical&lt;/em&gt; navigation cost; for the unusual case where an enterprise still wants a hard kernel boundary around an untrusted workload, Microsoft&apos;s recommended fallback [@learn-microsoft-com-application-guard] is explicit: &quot;If your organization requires container-based isolation, we recommend Windows Sandbox or Azure Virtual Desktop (AVD).&quot; The Hyper-V partition is still there. It just is no longer running every employee&apos;s browser, all day, by default.&lt;/p&gt;
&lt;h2&gt;7. Why Sandbox Survives&lt;/h2&gt;
&lt;p&gt;If the deprecation reasoning above is right, the natural question is why the &lt;em&gt;same&lt;/em&gt; substrate, allocated by &lt;em&gt;the same&lt;/em&gt; HCS API, in &lt;em&gt;the same&lt;/em&gt; kind of Hyper-V partition, survived as Windows Sandbox. The answer is that everything that made WDAG expensive maps to an operational advantage in Sandbox.&lt;/p&gt;
&lt;p&gt;The lifecycle is gesture-bound, not session-bound. The cost is paid by the user who explicitly asked for it, when they asked for it, and not before. The Sandbox install page [@learn-microsoft-com-sandbox-install] ships the feature disabled by default; turning it on costs the dynamic base image on disk and nothing on memory until launch.&lt;/p&gt;
&lt;p&gt;The substitute set is empty. For the &quot;run an untrusted executable once&quot; threat model, no in-process mitigation suite plays the role ESM plays for browsing. There is no in-AppContainer answer to &quot;I want to detonate &lt;code&gt;suspicious.exe&lt;/code&gt; and observe it&quot;; Smart App Control prevents execution rather than containing it; AppLocker policies refuse the run rather than sandboxing it. The use case Sandbox fills is what is &lt;em&gt;left over&lt;/em&gt; when prevention fails or is operationally unacceptable, and there is no cheaper way to fill it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The &quot;run an untrusted executable&quot; threat model lacks a cheaper substitute, so Sandbox&apos;s Hyper-V partition cost is the floor. WDAG&apos;s &quot;render an untrusted website&quot; threat model gained a cheaper substitute (Edge ESM), so WDAG&apos;s same Hyper-V partition cost stopped being the floor and became the ceiling -- and was retired.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The integration surface is also far smaller. Sandbox exposes one launcher binary, one configuration file format, no policy engine, no clipboard direction policies (just on/off), no network zoning, no pre-warmed worker process, no host-side broker for an embedded browser. The host-side attack surface is correspondingly thin: a &lt;code&gt;.wsb&lt;/code&gt; parser, an HCS caller, a window-host process. Each WDAG-style integration bug class -- network-isolation evaluator, browser broker, document-routing logic -- has no analog.&lt;/p&gt;
&lt;p&gt;A useful contrast: the &lt;code&gt;isolatedapplauncher.h&lt;/code&gt; [@learn-microsoft-com-api-isolatedapplauncher] page exposes &lt;code&gt;IIsolatedAppLauncher&lt;/code&gt;, &lt;code&gt;IIsolatedProcessLauncher&lt;/code&gt;, &lt;code&gt;IsProcessInIsolatedContainer&lt;/code&gt;, and &lt;code&gt;IsProcessInWDAGContainer&lt;/code&gt;. That is the application surface of a feature that hosts third-party processes and lets them know they are inside it. Sandbox has nothing comparable, because no third-party application is supposed to ship with &quot;behave differently inside Sandbox&quot; logic. The guest is a fresh Windows install, the artifact is whatever the user dropped in, and the host does not need to expose an &quot;am I inside?&quot; predicate.&lt;/p&gt;
&lt;p&gt;Sandbox also benefits from the &lt;em&gt;negative space&lt;/em&gt; of the deprecation. The replacement stack (Edge ESM, Smart App Control, Defender network protection) collectively pushes the &lt;em&gt;unbearable&lt;/em&gt; workloads off the Hyper-V partition: the always-on browser, the heavyweight Office document host, the network-zone enforcer. What is left for Sandbox is the relatively small set of workloads where partition isolation is the right answer: malware triage, installer testing, one-off compatibility checks, isolated developer environments. The feature is now used closer to its design intent than it was in the WDAG era.This is the rare case where deprecating a sibling makes a feature &lt;em&gt;more&lt;/em&gt; aligned with its purpose. Before WDAG retirement, the question &quot;should this be in Sandbox or WDAG?&quot; had a complicated answer involving who you were and what you were doing. After 24H2, the question collapses: if you want a partition, you mean Sandbox.&lt;/p&gt;
&lt;h2&gt;8. The 2026 Isolation Stack&lt;/h2&gt;
&lt;p&gt;With WDAG gone, the post-24H2 Windows isolation taxonomy is best read as four orthogonal primitives, each answering a different question about an untrusted workload. None alone substitutes for WDAG&apos;s combined function; together they cover the ground WDAG used to. The tiers below are numbered for reference; the numbering does &lt;em&gt;not&lt;/em&gt; imply a single linear cost/strength axis. Process mitigations live &lt;em&gt;inside&lt;/em&gt; AppContainer-wrapped processes (they are not &quot;below&quot; AppContainer on a cost axis, they are within it), and admission primitives (Smart App Control, Defender network protection) are an orthogonal family that decides whether a binary or destination is allowed at all -- a question that runs before capability containment, not above or below it.&lt;/p&gt;

A mechanism that answers a specific question about how an untrusted workload is constrained. The four families that the 2026 Windows stack composes are: (1) *process-internal mitigations* that limit what a corrupted-memory exploit can do (ACG, CET, Edge ESM); (2) *capability sandboxes* that limit what a process can name and reach (AppContainer); (3) *admission policies* that decide whether a binary may run or a destination may be contacted at all (Smart App Control, Defender network protection); and (4) *kernel-partition boundaries* that put an entire second NT kernel between the workload and the user&apos;s data (Hyper-V VM). Each family has its own cost shape; only the kernel-partition boundary appears on the Microsoft Servicing Criteria for Windows [@microsoft-com-servicing-criteria] as a serviced security boundary.
&lt;h3&gt;Tier 1: Process mitigations (Edge ESM, ACG, CET)&lt;/h3&gt;
&lt;p&gt;The cheapest family runs the workload on the host kernel under a stricter set of process-level controls. Edge Enhanced Security Mode is the visible UI; the underlying primitives are the Hardware-enforced Stack Protection [@techcommunity-microsoft-com-p-2163340] (CET) and Arbitrary Code Guard [@learn-microsoft-com-protection-reference] (ACG) referenced from the browse-safer page [@learn-microsoft-com-browse-safer]. These mitigations apply &lt;em&gt;inside&lt;/em&gt; whatever container the binary already runs in -- an AppContainer-wrapped Edge renderer, or a non-AppContainer process -- and limit the privileges a corrupted-memory exploit can give itself. They are sufficient for the &lt;em&gt;vast majority&lt;/em&gt; of untrusted navigations after the JIT-disabling trade is made.&lt;/p&gt;
&lt;h3&gt;Tier 2: AppContainer (capability-bound sandbox)&lt;/h3&gt;
&lt;p&gt;The capability sandbox is the AppContainer [@learn-microsoft-com-appcontainer-isolation] primitive. The MSDN page enumerates the isolation slices under six section headings -- Credential isolation, Device isolation, File isolation, Network isolation, Process isolation, and Window isolation -- all enforced at the OS, none requiring a partition. AppContainer is the wrapper around Edge&apos;s renderer, around UWP apps, around modern app-isolation packages. It is the cheap container that scales to every process on the device. Inside an AppContainer, Tier 1 mitigations still apply; AppContainer constrains what the process can &lt;em&gt;name&lt;/em&gt;, Tier 1 constrains what a corrupted process can &lt;em&gt;do&lt;/em&gt;. They are complementary, not stacked.&lt;/p&gt;
&lt;h3&gt;Tier 3: Smart App Control + Defender network protection (policy at the OS edge)&lt;/h3&gt;
&lt;p&gt;Adjacent to the process tiers, the policy tier decides what binaries and what destinations are allowed at all. Smart App Control governs &lt;em&gt;what runs&lt;/em&gt;; Defender network protection governs &lt;em&gt;what the device talks to&lt;/em&gt;. These are not isolation primitives in the strict sense -- they are &lt;em&gt;admission&lt;/em&gt; primitives. They turn off the question before the partition has to answer it.&lt;/p&gt;
&lt;h3&gt;Tier 4: Hyper-V VM (Windows Sandbox, Windows Server Hyper-V isolation containers)&lt;/h3&gt;
&lt;p&gt;At the top of the cost curve sits the Hyper-V VM boundary -- the only tier whose worst-case escape pays the $250,000 bounty [@microsoft-com-hyper-v]. Windows Sandbox is the desktop face; Hyper-V isolation containers [@learn-microsoft-com-hyperv-container] on Windows Server (&quot;each container runs inside of a highly optimized virtual machine and effectively gets its own kernel&quot;) are the server face. The 2026 stack uses this tier sparingly, on user gesture, for workloads where the cheaper tiers are not enough.&lt;/p&gt;

flowchart TD
    T1[Tier 1: Process mitigations&lt;br /&gt;ACG, CET, Edge ESM]
    T2[Tier 2: AppContainer&lt;br /&gt;Capability-bound process sandbox]
    T3[Tier 3: Policy admission&lt;br /&gt;Smart App Control + Defender network protection]
    T4[Tier 4: Hyper-V VM&lt;br /&gt;Windows Sandbox / HV isolation containers]
    T1 ~~~ T2 ~~~ T3 ~~~ T4
    T1 -.-&amp;gt;|untrusted browsing| ESM2[Edge ESM]
    T2 -.-&amp;gt;|modern apps| UWP[UWP / packaged apps]
    T3 -.-&amp;gt;|admit/deny| SAC2[SAC + DNP]
    T4 -.-&amp;gt;|on-demand detonation| WS2[Windows Sandbox]
&lt;p&gt;The taxonomy is layered without being strictly linear. Read it as a four-question pipeline rather than a four-rung ladder. Tier 3 (Smart App Control + Defender network protection) decides &lt;em&gt;what runs and what the device talks to&lt;/em&gt; -- admission, not containment. Whatever Tier 3 admits then lands inside Tier 2 (AppContainer), which constrains the binary&apos;s &lt;em&gt;capabilities&lt;/em&gt; -- file, network, device, window, credential, and process scope. Inside that AppContainer-wrapped process, Tier 1 (ACG, CET, Edge ESM) applies &lt;em&gt;process-internal&lt;/em&gt; mitigations that limit what a corrupted-memory exploit can do. Tier 4 (Hyper-V VM) is the last-resort &lt;em&gt;kernel&lt;/em&gt; boundary: when the workload is hostile enough that the host kernel itself must be assumed reachable from the renderer, Windows Sandbox or a Hyper-V isolation container puts an entire NT kernel between the artifact and the user&apos;s data. WDAG used to plug a session-bound, transparent variant of Tier 4 underneath every Edge navigation; once Tier 1 hardening (ACG, CET, ESM) closed the marginal-security gap on the &lt;em&gt;typical&lt;/em&gt; navigation, that session-bound Tier 4 paid more than it saved, and was deleted.&lt;/p&gt;
&lt;h2&gt;9. Engineering Takeaways&lt;/h2&gt;
&lt;p&gt;A few rules of thumb fall out of the architectural comparison.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Use the lowest tier whose boundary you actually need.&lt;/strong&gt; If your threat is &quot;this binary may try to escape the renderer,&quot; AppContainer + process mitigations suffice. If your threat is &quot;this binary may try to escape the kernel,&quot; you need a Hyper-V partition; that means Sandbox or, for production, an isolation container. The Microsoft Servicing Criteria for Windows [@microsoft-com-servicing-criteria] lists which boundaries Microsoft commits to defending; anything not on that list is best treated as a depth-in-defense layer, not a primary boundary.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Disable shared channels first; renegotiate them only when forced.&lt;/strong&gt; The default &lt;code&gt;.wsb&lt;/code&gt;-less Sandbox enables networking and clipboard redirection -- per the configuration docs [@learn-microsoft-com-wsb-file], enabled networking &quot;can expose untrusted applications to the internal network.&quot; For malware triage, build the paranoid &lt;code&gt;.wsb&lt;/code&gt; from the snippet in section 2 and only loosen it when a specific analysis step requires it.&lt;/p&gt;
&lt;p&gt;{`
// A naive but useful decision tree. Print the recommended tier for a workload.
function pickTier(workload) {
  const w = workload.toLowerCase();
  if (w.includes(&quot;untrusted exe&quot;) || w.includes(&quot;malware&quot;)) {
    return &quot;Tier 4: Windows Sandbox (on-demand, gesture-bound)&quot;;
  }
  if (w.includes(&quot;untrusted website&quot;) || w.includes(&quot;unfamiliar site&quot;)) {
    return &quot;Tier 1: Edge Enhanced Security Mode + Tier 3: Defender network protection&quot;;
  }
  if (w.includes(&quot;untrusted document&quot;) || w.includes(&quot;office&quot;)) {
    return &quot;Tier 3: Smart App Control + Office Protected View&quot;;
  }
  if (w.includes(&quot;third-party app&quot;) || w.includes(&quot;uwp&quot;)) {
    return &quot;Tier 2: AppContainer&quot;;
  }
  return &quot;Default: ship the workload under Tier 2 AppContainer; escalate if the threat model justifies it.&quot;;
}&lt;/p&gt;
&lt;p&gt;[
  &quot;Detonate untrusted exe from email&quot;,
  &quot;Open an unfamiliar site for research&quot;,
  &quot;Open an untrusted Office document&quot;,
  &quot;Run a third-party packaged app&quot;
].forEach(w =&amp;gt; console.log(w + &quot;  -&amp;gt;  &quot; + pickTier(w)));
`}&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Avoid features whose own boundary is not a serviced boundary.&lt;/strong&gt; A useful litmus test is to read the Microsoft Security Servicing Criteria for Windows [@microsoft-com-servicing-criteria] and ask whether the feature&apos;s own claimed isolation appears there. WDAG didn&apos;t; Hyper-V VM did. Designs that rely on a non-serviced isolation are more brittle, both technically (no bounty pressure on the boundary) and operationally (no commitment to fix integration bugs out-of-band).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Plan for deprecation of integration features, not of primitives.&lt;/strong&gt; Microsoft retired WDAG; it did not retire HCS, Hyper-V isolation containers, or AppContainer. The primitives outlast the productizations. A codebase that depends on the primitives (calling HCS directly, wrapping a workload in AppContainer) is more durable than one that depends on a packaged feature (calling &lt;code&gt;IIsolatedAppLauncher&lt;/code&gt;, relying on Network Isolation policy semantics) whose lifecycle is set by product economics.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Workloads that traverse a Sandbox boundary leave specific telemetry. The host event log records &lt;code&gt;Containers-DisposableClientVM&lt;/code&gt; start/stop events; HCS partition allocations are visible to ETW; mapped-folder access from inside the guest crosses a brokered channel that surfaces in host file-system filters. If an incident response playbook expects to see &quot;Hyper-V partition created&quot; or &quot;container-disposable-client-vm started,&quot; those are the canonical signals from the substrate, not from Sandbox-specific telemetry.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;10. Open Problems and Future Direction&lt;/h2&gt;
&lt;p&gt;The 2026 stack is good. It is not finished. Several open problems remain visible at the seams.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Transparent, gesture-priced isolation.&lt;/strong&gt; WDAG&apos;s session-bound model failed; Sandbox&apos;s gesture-bound model is too explicit for everyday use. There is no current Windows feature that combines (a) automatic, policy-driven launch (&quot;this URL is outside the trust zone, isolate it&quot;), (b) Sandbox-style on-demand allocation, and (c) sub-second cold start. Each pair of those three is achievable -- WDAG had (a) and (b) but not (c); Sandbox has (b) and (c) but not (a); ESM has (a) and (c) but gives up the partition boundary. Closing all three simultaneously is the standing open problem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hardware-rooted attestation of the partition.&lt;/strong&gt; Today&apos;s Hyper-V partition is anchored by VBS, which is anchored by Secure Boot, which is anchored by the platform firmware. Microsoft Pluton [@learn-microsoft-com-security-processor] -- &quot;a secure crypto-processor built into the CPU... designed to provide the functionality of the Trusted Platform Module (TPM) and deliver other security functionality beyond what is possible with the TPM 2.0 specification&quot; -- raises the floor on what a guest can attest about, opening a path to confidential-VM-style guarantees on the client. The shape of that integration with Sandbox is not yet public.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Confidential client VMs.&lt;/strong&gt; On the server, Azure confidential VMs [@learn-microsoft-com-vm-overview] provide a &quot;hardware-enforced boundary between your application and the virtualization stack&quot; via AMD SEV-SNP and Intel TDX, with &quot;secure key release with cryptographic binding between the platform&apos;s successful attestation and the VM&apos;s encryption keys.&quot; Whether that boundary -- guest memory unreadable by the host kernel -- ever shows up under client Windows Sandbox or Hyper-V is an open architectural question. If it does, it changes the Hyper-V VM threat model: a malicious &lt;em&gt;host&lt;/em&gt; (or compromised host kernel) could no longer read guest memory, which would close a category of risk that currently sits outside the bounty scope.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI-agent action containment.&lt;/strong&gt; WDAG&apos;s specific shape -- a session-bound partition that transparently absorbed risky actions on behalf of a user -- is suggestive of an emerging problem: containing the actions of AI agents that take tool-using steps inside a user&apos;s session. Today&apos;s stack does not have a feature shaped quite like this. Sandbox is too explicit; AppContainer is too process-bound; ESM is browser-only; Smart App Control is admit/deny, not contain/observe. An &quot;AI-agent action sandbox&quot; would need WDAG&apos;s transparency without WDAG&apos;s resident-per-employee cost. The architectural question is whether the lessons of WDAG&apos;s retirement should make the next attempt look like Sandbox-with-a-policy-trigger or like AppContainer-with-a-stronger-boundary.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Shared-microarchitectural-state side channels.&lt;/strong&gt; The Hyper-V VM boundary is a logical boundary. The CPU caches, branch predictors, and prefetch units are still shared with the host. Spectre-class side channels survive partition boundaries and survive confidential-VM boundaries; the canonical Meltdown/Spectre disclosure [@meltdownattack-com] frames the primitive as a class of transient-execution attacks against the shared microarchitectural state itself, and Microsoft&apos;s KB4072698 guidance for speculative-execution side-channel vulnerabilities [@support-microsoft-com-b632-0d96f30c8c8e] catalogs a long succession of advisories (ADV180002, ADV180012, ADV180018 L1TF, ADV190013 MDS, ADV220002 MMIO Stale Data, CVE-2022-23825 Branch Type Confusion, CVE-2022-0001 Branch History Injection, CVE-2023-20569 AMD Return Address Predictor) that each required Hyper-V-host mitigation to keep the partition boundary effective. Mitigations close &lt;em&gt;known&lt;/em&gt; variants at the cost of performance, but the underlying primitive -- one core, two security domains -- does not change. The ideal sandbox -- sub-second cold start, single-digit-MB resident overhead, transparent policy launch, &lt;em&gt;and&lt;/em&gt; a partition boundary that closes all microarchitectural channels -- remains unachievable on shared silicon. This is not a Windows-specific problem; it is the lower bound on what any client-side sandbox can deliver.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The 2026 isolation stack is the right shape for the threats it was designed against: a malicious binary, an unfamiliar website, an untrusted document, a third-party app. It is not yet shaped for the threats that will dominate 2027 and beyond: confidential client compute, agent action containment, hardware-rooted attestation. Watching where the next partition-shaped primitive appears -- or fails to -- is how the architecture will continue to evolve.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;p&gt;Windows Sandbox and WDAG are the cleanest natural experiment Windows has run on Hyper-V isolation as a product. Same substrate, same partition primitive, same bounty-protected boundary; opposite lifecycle bindings, opposite threat models, opposite outcomes. The substrate survives because the substrate is the boundary; the productizations come and go because they are bets on how to spend that boundary&apos;s budget. WDAG bet on session-bound transparency and lost to a cheaper process-mitigation stack; Sandbox bet on gesture-bound disposability and remains the right answer for the workload it was designed for. The story is less about Hyper-V and more about lifecycle: when you allocate isolation matters as much as how much isolation you allocate.&lt;/p&gt;
</content:encoded><category>windows-security</category><category>hyper-v</category><category>sandbox</category><category>application-guard</category><category>isolation</category><category>hcs-api</category><category>vbs</category><author>noreply@paragmali.com (Parag Mali)</author></item><item><title>AppContainer and LowBox Tokens: Windows&apos;s Capability Sandbox</title><link>https://paragmali.com/blog/appcontainer-and-lowbox-tokens-windowss-capability-sandbox/</link><guid isPermaLink="true">https://paragmali.com/blog/appcontainer-and-lowbox-tokens-windowss-capability-sandbox/</guid><description>How a single bit in Windows&apos;s access token, two new SID alphabets, and a per-package namespace partition let the kernel give two co-tenanted apps opposite verdicts.</description><pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate><content:encoded>
Windows&apos;s modern application sandbox rests on a single kernel primitive (*AppContainer / LowBox*) that Microsoft preserved under two names: *LowBox* inside the kernel and the `NtCreateLowBoxToken` syscall, *AppContainer* in the public API. One bit in `_TOKEN.Flags`, two new SID alphabets (`S-1-15-2-*` for packages, `S-1-15-3-*` for capabilities), and a per-package partition of the Object Manager namespace let `SeAccessCheck` give two co-tenanted processes opposite verdicts. UWP apps, MSIX-packaged desktop apps, Microsoft Edge LPAC renderers, and Windows Sandbox all rest on this primitive. James Forshaw&apos;s twelve-year exploit-research line shaped its current hardening.
&lt;h2&gt;1. Two Calculators&lt;/h2&gt;
&lt;p&gt;Open &lt;code&gt;calc.exe&lt;/code&gt; on a stock Windows 11 25H2 installation. It is the Microsoft Store calculator, the UWP one, the one Windows ships in the default Start menu. Now download the legacy &lt;code&gt;win32calc.exe&lt;/code&gt; from a Windows 7 image and run it side by side. Same user. Same machine. Same disk, same DACLs on every file in your profile. The Mandatory Integrity Control label on &lt;code&gt;%USERPROFILE%\.ssh\id_ed25519&lt;/code&gt; is the default Medium for both processes&apos; access checks.&lt;/p&gt;
&lt;p&gt;Yet &lt;code&gt;win32calc.exe&lt;/code&gt; can read the SSH private key, and &lt;code&gt;calc.exe&lt;/code&gt; cannot. If you open both processes in Sysinternals Process Explorer and compare the Security tab side by side, every field that the classic Windows NT token model exposes looks identical. User SID: identical. Logon ID: identical. Primary group: identical. Privileges: identical. Even the integrity level, by the read path, looks the same on the user&apos;s profile files.&lt;/p&gt;
&lt;p&gt;The difference is one bit. In the kernel&apos;s &lt;code&gt;_TOKEN&lt;/code&gt; structure, the &lt;code&gt;TOKEN_LOWBOX&lt;/code&gt; flag (SDK constant value &lt;code&gt;0x4000&lt;/code&gt;, the 15th bit of the &lt;code&gt;TokenFlags&lt;/code&gt; word) is set on the UWP &lt;code&gt;calc.exe&lt;/code&gt; token and clear on &lt;code&gt;win32calc.exe&lt;/code&gt; [@nostarch-wsi]. Microsoft&apos;s documentation calls the resulting token an &lt;em&gt;AppContainer token&lt;/em&gt;; the kernel call that creates it preserved the older marketing name. &quot;AppContainer was originally named &lt;em&gt;LowBox&lt;/em&gt; (prior to the release of Windows 8). That legacy name can be seen in API names such as &lt;strong&gt;NtCreateLowBoxToken&lt;/strong&gt;&quot; [@ms-learn-legacy].&lt;/p&gt;

AppContainer was originally named _LowBox_ (prior to the release of Windows 8). That legacy name can be seen in API names such as **NtCreateLowBoxToken**. -- Microsoft Learn

The public-API name for a kernel-enforced per-application sandbox introduced in Windows 8 [@ms-learn-legacy]. An AppContainer process runs under a *LowBox token*, a special access token whose `LowBoxToken` flag, package SID, capability SIDs, and per-instance number cause the Security Reference Monitor to apply additional access-check rules beyond the classic user-and-groups DACL walk. The kernel-internal name *LowBox* is preserved in `NtCreateLowBoxToken` and several adjacent symbol names [@ms-learn-ntcreatelowboxtoken].
&lt;p&gt;If you read the eight sub-authorities of the Calculator&apos;s package SID -- something like &lt;code&gt;S-1-15-2-466767348-3739614953-2700836392-...&lt;/code&gt; (the trailing five sub-authorities are elided here) -- you are reading a deterministic SHA-256 hash of the &lt;a href=&quot;https://paragmali.com/blog/windows-app-identity-33-year-reinvention/&quot; rel=&quot;noopener&quot;&gt;package family name&lt;/a&gt;, sliced into eight 4-byte 32-bit sub-authorities [@ms-learn-derivesid] [@nostarch-wsi]. Every Windows 11 machine in the world derives the same SID for &lt;code&gt;Microsoft.WindowsCalculator_8wekyb3d8bbwe&lt;/code&gt;, and DACLs on the per-package data directories name precisely that SID. Section 5 will walk this in detail; for now, notice that the SID alphabet starts with &lt;code&gt;S-1-15-2&lt;/code&gt;, not the familiar &lt;code&gt;S-1-5-21&lt;/code&gt; of user SIDs. Microsoft introduced two new SID prefixes in Windows 8: &lt;code&gt;S-1-15-2-*&lt;/code&gt; for packages and &lt;code&gt;S-1-15-3-*&lt;/code&gt; for capabilities. Both live in identifier authority 15, the &lt;em&gt;App Package Authority&lt;/em&gt; [@ms-learn-well-known-sids].&lt;/p&gt;

flowchart TD
    A[User Alice opens id_ed25519] --&amp;gt; B{&quot;Is _TOKEN.Flags.LowBoxToken set?&quot;}
    B --&amp;gt;|&quot;No (win32calc.exe)&quot;| C[Classic SeAccessCheck&lt;br /&gt;against user + groups]
    B --&amp;gt;|&quot;Yes (UWP calc.exe)&quot;| D[LowBox access path&lt;br /&gt;add Package SID and&lt;br /&gt;Capability claims to check]
    C --&amp;gt; E[Read allowed]
    D --&amp;gt; F[Read denied:&lt;br /&gt;no fs:documentsLibrary capability]
&lt;p&gt;The question that opens this article is the same one Windows NT 3.1 could not ask in July 1993: &lt;em&gt;two processes, same user, same DACLs, how does the kernel give them different verdicts?&lt;/em&gt; For nineteen years it could not. The answer Microsoft shipped on October 26, 2012 is the &lt;em&gt;LowBox token&lt;/em&gt;, and we will trace one coherent primitive from its Vista-era ancestors through the four production deployments that exercise it on every Windows 11 machine in 2026. We will end with the exploit history that shaped its current hardening. The story starts on January 30, 2007, with a single browser, a single bit, and a structural defect Microsoft spent the next fourteen years repairing.&lt;/p&gt;
&lt;h2&gt;2. The Internet Explorer 7 Protected Mode Era&lt;/h2&gt;
&lt;p&gt;By the time Windows Vista went generally available on January 30, 2007 [@wiki-vista], a large majority of home Windows installs ran day-to-day under administrator accounts. The Wikipedia summary of &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&lt;/a&gt; captures the pre-Vista status quo plainly: in Windows 1.0 through Windows 9x, &quot;all applications had privileges equivalent to the operating system&quot; [@wiki-uac]. One browser exploit equalled total compromise of the user profile, and Internet Explorer 6 from 2001 was the single largest pathway between hostile content and the desktop.&lt;/p&gt;
&lt;p&gt;Vista&apos;s response had two halves. The first was User Account Control, the split-token model that asks the user to consent before promoting a Medium-integrity process to High. The second was Mandatory Integrity Control (MIC), the labelling primitive UAC depended on. MIC defined four integrity levels with their own SIDs: Low (&lt;code&gt;S-1-16-4096&lt;/code&gt;), Medium (&lt;code&gt;S-1-16-8192&lt;/code&gt;), High (&lt;code&gt;S-1-16-12288&lt;/code&gt;), and System (&lt;code&gt;S-1-16-16384&lt;/code&gt;) [@wiki-mic]. The Security Reference Monitor checks the label before the DACL; a label deny short-circuits the access check.&lt;/p&gt;

A Windows Vista security mechanism that adds an *integrity level* to every process and every securable object [@ms-learn-mic]. Levels are System, High, Medium, and Low; objects carry a `SYSTEM_MANDATORY_LABEL_ACE` in their SACL declaring a minimum integrity level for access. By default the system creates every object with an access mask of `SYSTEM_MANDATORY_LABEL_NO_WRITE_UP`, which prevents lower-IL writers from modifying higher-IL objects but does not block reads.
&lt;p&gt;The four MIC integrity-level SIDs are spaced at 0x1000-unit increments, the consecutive &lt;code&gt;SECURITY_MANDATORY_*_RID&lt;/code&gt; constants in &lt;code&gt;winnt.h&lt;/code&gt;: Low &lt;code&gt;S-1-16-4096&lt;/code&gt; (0x1000), Medium &lt;code&gt;S-1-16-8192&lt;/code&gt; (0x2000), High &lt;code&gt;S-1-16-12288&lt;/code&gt; (0x3000), System &lt;code&gt;S-1-16-16384&lt;/code&gt; (0x4000) [@wiki-mic]. They are in identifier authority 16, the &lt;em&gt;Mandatory Label Authority&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Internet Explorer 7, which had shipped on October 18, 2006 [@wiki-ie7], became MIC&apos;s first marquee consumer. IE 7&apos;s &lt;em&gt;Protected Mode&lt;/em&gt; launched the renderer at Low integrity, where it could not write to &lt;code&gt;%USERPROFILE%&lt;/code&gt; (Medium IL by default) or to &lt;code&gt;HKLM&lt;/code&gt;. A separate Medium-IL broker process named &lt;code&gt;ieuser.exe&lt;/code&gt; accepted COM requests from the renderer and performed privileged work on its behalf -- saving downloads, writing to non-Low locations -- only for operations the policy allowed. The renderer&apos;s private workspace lived at &lt;code&gt;%USERPROFILE%\AppData\LocalLow\&lt;/code&gt; and the corresponding Low-IL temporary internet files [@ie7-protected-mode-mirror]. Marc Silbey and Peter Brundrett documented the design on MSDN. The MSDN article is preserved at a third-party mirror today; the original Microsoft path retired with the IE developer area.&lt;/p&gt;
&lt;p&gt;The structural pattern was complete. A sandboxed worker process and a higher-privilege broker that re-elevates only the operations the policy permits is the topology of every later Windows-side capability sandbox.The same broker / target pattern reappears under several names: &lt;code&gt;RuntimeBroker.exe&lt;/code&gt; for UWP, &lt;code&gt;AppInfo&lt;/code&gt; for UAC consent prompts, &lt;code&gt;PrintWorkflowUserSvc&lt;/code&gt; for print, and the WebAuthn and voice-activation brokers for those WinRT capabilities. Each is structurally &lt;code&gt;ieuser.exe&lt;/code&gt; for a different resource. AppContainer&apos;s &lt;code&gt;RuntimeBroker.exe&lt;/code&gt; is structurally the same idea as &lt;code&gt;ieuser.exe&lt;/code&gt;. AppInfo (the UAC consent broker), the Print broker, the WebAuthn broker, and the voice-activation broker all share the lineage. Chromium&apos;s own broker / target sandbox, which we will meet in §3, was published less than two years later on the same conceptual base.&lt;/p&gt;

flowchart LR
    A[iexplore.exe&lt;br /&gt;Low IL renderer] --&amp;gt;|&quot;COM request&quot;| B[ieuser.exe&lt;br /&gt;Medium IL broker]
    A --&amp;gt;|&quot;file write&quot;| C[&quot;%USERPROFILE%/AppData/LocalLow/&lt;br /&gt;Low-IL workspace&quot;]
    B --&amp;gt;|&quot;write download&quot;| D[&quot;%USERPROFILE%/Downloads/&lt;br /&gt;Medium IL&quot;]
    B -.-&amp;gt;|&quot;policy decides&quot;| A
&lt;p&gt;So far so good. But Protected Mode had three structural defects, and each one mapped one-to-one to a specific Windows 8 fix five years later.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Defect one: read-up was not blocked by default.&lt;/strong&gt; The default mandatory-label ACE was &lt;code&gt;SYSTEM_MANDATORY_LABEL_NO_WRITE_UP&lt;/code&gt;. A Low-IL process could not write to Medium-IL objects, but it could read them. The Microsoft Learn page on MIC is explicit: &quot;the system creates every object with an access mask of &lt;strong&gt;SYSTEM_MANDATORY_LABEL_NO_WRITE_UP&lt;/strong&gt;&quot; [@ms-learn-mic]. Unless the object explicitly carried a &lt;code&gt;NO_READ_UP&lt;/code&gt; mandatory ACE -- which &lt;code&gt;%USERPROFILE%\.ssh\id_ed25519&lt;/code&gt; did not -- a compromised Low-IL IE could &lt;em&gt;read&lt;/em&gt; the user&apos;s SSH keys, browser passwords, OAuth refresh tokens, and any other Medium-IL secret. The Chromium sandbox FAQ flags the same observation: Vista&apos;s integrity levels were the only user-mode separation primitive available, and Chromium built a layered user-mode sandbox on top precisely because Vista&apos;s separation was insufficient [@chromium-sandbox-faq].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A &lt;code&gt;NO_WRITE_UP&lt;/code&gt; boundary is an &lt;em&gt;integrity&lt;/em&gt; boundary, not a &lt;em&gt;confidentiality&lt;/em&gt; boundary. It stops a low-trust process from corrupting your data, but it does not stop it from reading your data. For a browser renderer that processes hostile content, confidentiality is the load-bearing axis: the attacker&apos;s goal is exfiltration, not corruption. Without &lt;code&gt;NO_READ_UP&lt;/code&gt;, MIC alone could not contain a renderer compromise [@ms-learn-mic].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Defect two: identity was shared across instances.&lt;/strong&gt; Two Low-IL IE renderers ran with the same Low-IL SID and the same user SID. They could open handles to each other&apos;s named objects, shared memory sections, and process tokens. Integrity level is an axis; it is not a per-instance principal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Defect three: network access was unconstrained.&lt;/strong&gt; A Low-IL process could open any socket to any host. The Vista firewall was the freshly-introduced Windows Filtering Platform, but WFP had no notion of &quot;which app is this&quot; at all. As James Forshaw later put it on the Project Zero blog, &quot;Prior to XP SP2 Windows didn&apos;t have a built-in firewall... While XP SP2 introduced the built-in firewall, the basis for the one used in modern versions of Windows was introduced in Vista as the Windows Filtering Platform (WFP)&quot; [@p0-network]. WFP&apos;s per-application-package callout came years later, and the corresponding condition value &lt;code&gt;FWPM_CONDITION_ALE_PACKAGE_ID&lt;/code&gt; did not exist in 2007.&lt;/p&gt;
&lt;p&gt;Each defect became a specific Windows 8 mechanism. Defect one became the per-package namespace redirect plus the LowBox flag&apos;s &lt;code&gt;NO_READ_UP&lt;/code&gt;-by-construction effect on cross-package reads. Defect two became the AppContainerNumber per-instance key. Defect three became the WFP capability gate. Microsoft and Google looked at the same Vista primitives and reached opposite conclusions. Google built a user-mode sandbox on top of NT. Microsoft built a per-app principal &lt;em&gt;inside&lt;/em&gt; the NT token. The next two sections walk both branches.&lt;/p&gt;
&lt;h2&gt;3. Same Idea, Four Directions&lt;/h2&gt;
&lt;p&gt;Between January 2007 and October 2012, four different teams reached the same conclusion from four different starting points: &lt;em&gt;the kernel needs a per-application principal, not just a per-user one&lt;/em&gt;. They built it in four different places.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chromium sandbox, December 2008.&lt;/strong&gt; When Chrome 1.0 shipped on Windows in December 2008, it carried a layered user-mode sandbox: a broker process plus a target process, a restricted token derived from the user&apos;s token, a job object that limited the target&apos;s syscall surface, and (on Vista and later) the Low integrity level on the target. The Chromium sandbox design document spells out the rule that defined the choice. &quot;Do not re-invent the wheel: It is tempting to extend the OS kernel with a better security model. Don&apos;t. Let the operating system apply its security to the objects it controls&quot; [@chromium-sandbox]. The reasoning continues in the FAQ. &quot;the sandbox cannot provide any protection against bugs in system components such as the kernel it is running on&quot; [@chromium-sandbox-faq]. The Chromium design was a &lt;em&gt;user-mode-only&lt;/em&gt; sandbox, layered on top of NT primitives that already existed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Google Native Client, 2009.&lt;/strong&gt; Bennet Yee and co-authors at Google published &lt;em&gt;Native Client: A Sandbox for Portable, Untrusted x86 Native Code&lt;/em&gt; at IEEE S&amp;amp;P 2009 [@nacl-ieee]. NaCl declared its capabilities at launch and structurally inverted the question: rather than retrofit a sandbox onto an application, run untrusted code only with capabilities the host has explicitly granted. The Wikipedia retrospective records NaCl&apos;s eventual replacement by WebAssembly [@wiki-nacl], but the design idea -- &lt;em&gt;capability-declared-at-launch&lt;/em&gt; -- prefigured what Microsoft would call &lt;code&gt;SECURITY_CAPABILITIES.Capabilities[]&lt;/code&gt; three years later.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Apple iOS application sandbox, 2007 onward.&lt;/strong&gt; iPhone OS 1.0 (June 29, 2007) shipped with a kernel-side TrustedBSD-derived MAC sandbox that confined Apple&apos;s own built-in apps [@apple-platform-security-app]. The developer-facing surface -- third-party apps, Bundle IDs, Team IDs, and the entitlements dictionary -- arrived with iPhone OS 2.0 in July 2008 alongside the public App Store [@apple-platform-security-app]. From 2008 onward the per-app principal had two halves: a &lt;em&gt;Bundle ID&lt;/em&gt; (a string the developer picks, structurally like a Windows Package Family Name) and a &lt;em&gt;Team ID&lt;/em&gt; (the signer). Each app&apos;s profile lived at &lt;code&gt;Containers/Data/Application/&amp;lt;bundle-uuid&amp;gt;/&lt;/code&gt;, structurally similar to Windows&apos;s later &lt;code&gt;%LOCALAPPDATA%\Packages\&amp;lt;PFN&amp;gt;\&lt;/code&gt;. The capability set was an *entitlements dictionary* signed into the app bundle, structurally similar to Windows&apos;s later &lt;code&gt;&amp;lt;Capabilities&amp;gt;&lt;/code&gt; element.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Android per-app UID, September 23, 2008.&lt;/strong&gt; When Android 1.0 shipped on the T-Mobile G1 on September 23, 2008 [@wiki-android], it carried the most parsimonious solution: each app got its own UNIX UID. Classical Unix discretionary access control did the rest. Where Windows extended the SID alphabet, Android extended the UID allocation policy. The Android Application Sandbox page records the additional MAC layers Android added in later releases -- SELinux from Android 5.0 onward, seccomp-bpf from Android 8.0 onward, and per-physical-user partitions [@android-app-sandbox]. The kernel principal stayed a Unix UID.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Crispin Cowan, Jan 17, 2008.&lt;/strong&gt; Between the Vista release and the eventual Windows 8 ship, the &lt;em&gt;person&lt;/em&gt; who would write Microsoft&apos;s answer arrived on the security team. Crispin Cowan, the StackGuard co-author whose 1998 USENIX paper coined the term &lt;em&gt;stack canary&lt;/em&gt; [@stackguard], joined the Microsoft Windows security team in January 2008. ZDNet&apos;s coverage of the hire reads: &quot;Crispin Cowan, the Linux security expert behind StackGard [sic], the Immunix Linux distro and AppArmor, has joined the Windows security team... Crispin will join the team that worked on User Account Control&quot; [@zdnet-cowan]. Cowan&apos;s later USENIX Security 2013 keynote &lt;em&gt;Windows 8 Security: Supporting User Confidence&lt;/em&gt; credits him with the AppContainer design [@cowan-usenix13], and his University of Waterloo speaker page makes the attribution explicit: &quot;From 2008 to 2017, Dr. Cowan was with Microsoft, where he designed the App Container sandbox that is used by the Edge and Chrome web browsers, Microsoft Office, and Windows 10 to contain Universal Windows Apps&quot; [@crysp-cowan].&lt;/p&gt;
&lt;p&gt;The four mechanisms compare like this:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Operating system&lt;/th&gt;
&lt;th&gt;Principal name&lt;/th&gt;
&lt;th&gt;How it is named&lt;/th&gt;
&lt;th&gt;How it is enforced&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;2007&lt;/td&gt;
&lt;td&gt;iOS&lt;/td&gt;
&lt;td&gt;Bundle ID + Team ID (from 2008)&lt;/td&gt;
&lt;td&gt;Developer string + signer&lt;/td&gt;
&lt;td&gt;Mandatory per-app MAC + entitlements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2008&lt;/td&gt;
&lt;td&gt;Chromium on Windows&lt;/td&gt;
&lt;td&gt;(none, user UID only)&lt;/td&gt;
&lt;td&gt;User SID + restricted token&lt;/td&gt;
&lt;td&gt;User-mode broker + job object + IL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2008&lt;/td&gt;
&lt;td&gt;Android&lt;/td&gt;
&lt;td&gt;Per-app UNIX UID&lt;/td&gt;
&lt;td&gt;UID allocated at install&lt;/td&gt;
&lt;td&gt;Classical Unix DAC + SELinux + seccomp&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2012&lt;/td&gt;
&lt;td&gt;Windows 8&lt;/td&gt;
&lt;td&gt;Package SID + Capability SIDs&lt;/td&gt;
&lt;td&gt;Deterministic SHA-2 of moniker&lt;/td&gt;
&lt;td&gt;Kernel access token + WFP + Object Manager&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

Each design carries the same *intent* -- give the kernel a per-application principal -- and locates it differently. iOS keeps the per-app *policy data* (entitlements plus `.sb` profiles) in the code-signed bundle and enforces it mandatorily in the kernel via the TrustedBSD-derived `Sandbox.kext`, with no opt-out. Android puts the principal in the UID space the Unix kernel already understood, so existing DAC plumbing did the work. Chromium kept the principal *outside* the kernel entirely, layering a user-mode policy on top of the existing user-keyed access check. Microsoft did the structurally rarest thing: it extended the kernel&apos;s access token data structure. The other three retrofitted the principal on top of an existing access-check pipeline; Windows extended the pipeline itself. The next section walks why Microsoft picked option four.
&lt;p&gt;The name &lt;em&gt;LowBox&lt;/em&gt; is itself a vestige of Microsoft&apos;s internal marketing process. The Win32 surface chose &lt;em&gt;AppContainer&lt;/em&gt; in time for Windows 8&apos;s general availability; the kernel surface had already shipped under the older name. Microsoft Learn confirms the lineage in plain prose [@ms-learn-legacy], and the syscall &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt; preserves it.&lt;/p&gt;
&lt;p&gt;Microsoft had a choice: build a user-mode policy daemon like Apple, extend the user-keyed principal like Android, or keep everything in user mode like Chromium. The team chose the fourth option and extended the kernel access token. The next section walks how, year by year, that decision matured.&lt;/p&gt;
&lt;h2&gt;4. The Evolution, Generation by Generation&lt;/h2&gt;
&lt;p&gt;The Windows 8 ship on October 26, 2012 [@wiki-win8] [@win8-rtm] was not the end of the story. It was the start of a six-generation refinement that continues into 2026 Windows 11 25H2. The seven generations look like this on a timeline.&lt;/p&gt;

gantt
    title Per-application sandbox primitives in Windows
    dateFormat YYYY-MM
    axisFormat %Y
    section Gen 0
    Classic NT user-token model :done, g0, 1993-07, 33y
    section Gen 1
    MIC plus IE 7 Protected Mode :done, g1, 2007-01, 19y
    section Gen 2
    Chromium broker target sandbox :done, g2, 2008-12, 18y
    section Gen 3
    AppContainer LowBox token :done, g3, 2012-10, 14y
    section Gen 4
    Less Privileged AppContainer :done, g4, 2016-08, 10y
    section Gen 5
    MSIX TrustLevel appContainer :done, g5, 2018-11, 8y
    section Gen 6
    Windows Sandbox plus Hyper-V :done, g6, 2019-05, 7y
&lt;h3&gt;4.1 Generation 0: The classic NT user-token model (1993)&lt;/h3&gt;
&lt;p&gt;Dave Cutler&apos;s Windows NT 3.1 shipped in July 1993 with a security model that is still the foundation in 2026. Every process has an access token. The token carries &lt;code&gt;User&lt;/code&gt;, &lt;code&gt;Groups[]&lt;/code&gt;, &lt;code&gt;Privileges[]&lt;/code&gt;, and a &lt;code&gt;DefaultDacl&lt;/code&gt;. Every securable object has a security descriptor with a DACL. &lt;a href=&quot;https://paragmali.com/blog/windows-access-control-25-years-of-attacks/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;SeAccessCheck&lt;/code&gt;&lt;/a&gt; walks the DACL access control entry by access control entry against the caller&apos;s user and group SIDs. &lt;em&gt;Identity equals authority:&lt;/em&gt; if process A and process B both run as Alice, they are the same principal to the kernel. Whatever Alice can do, both of them can do. Chapter 7 of the seventh-edition &lt;em&gt;Windows Internals&lt;/em&gt; [@ms-press-wi7] gives the canonical post-LowBox reference for this data structure.&lt;/p&gt;
&lt;p&gt;This model has no answer to the single-user, multi-application threat. A user&apos;s text editor and a user&apos;s browser are indistinguishable principals. &lt;em&gt;Bridge: the next generation tried to fix that by adding an axis, not a principal.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;4.2 Generation 1: IE 7 Protected Mode plus MIC (Vista, 2007)&lt;/h3&gt;
&lt;p&gt;We already walked the mechanism in §2. Mandatory Integrity Control shipped with Vista&apos;s general availability on January 30, 2007 [@wiki-vista] [@wiki-mic]. Internet Explorer 7&apos;s Protected Mode launched its renderer at Low integrity with a Medium-IL &lt;code&gt;ieuser.exe&lt;/code&gt; broker [@wiki-uac] [@ie7-protected-mode-mirror]. The Wikipedia article on MIC names IE 7 as the marquee consumer: &quot;Internet Explorer 7 introduces a MIC-based &apos;Protected Mode&apos; setting&quot; [@wiki-mic]. &lt;em&gt;Bridge: integrity level is an axis, not a principal. Two Low-IL processes share identity.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;4.3 Generation 2: The Chromium broker / target sandbox (Chrome, 2008)&lt;/h3&gt;
&lt;p&gt;The parallel path Microsoft did not take. Chromium&apos;s design document calls the architecture explicitly: &quot;The Windows sandbox is a user-mode only sandbox&quot; [@chromium-sandbox]. The two-process structure is named: &quot;The minimal sandbox configuration has two processes: one that is a privileged controller known as the &lt;em&gt;broker&lt;/em&gt;, and one or more sandboxed processes known as the &lt;em&gt;target&lt;/em&gt;&quot; [@chromium-sandbox]. The FAQ adds the integrity-level note: the sandbox &quot;uses integrity levels&quot; on Vista and later, and the only Vista-era application the FAQ knows about that used them was Internet Explorer 7 [@chromium-sandbox-faq]. &lt;em&gt;Bridge: who is this app? remains unanswered in any user-mode-only design.&lt;/em&gt;&lt;/p&gt;

Do not re-invent the wheel: It is tempting to extend the OS kernel with a better security model. Don&apos;t. Let the operating system apply its security to the objects it controls. -- Chromium sandbox design document [@chromium-sandbox]
&lt;p&gt;The pull quote is the most interesting sentence in the article so far, because Microsoft -- which &lt;em&gt;was&lt;/em&gt; the operating system vendor -- did exactly the opposite four years later, and shipped AppContainer the same year (2012) Chrome 22 hit the desktop with its user-mode-only sandbox. The two browsers&apos; modern descendants now run &lt;em&gt;both&lt;/em&gt; designs simultaneously, but we are getting ahead of ourselves.&lt;/p&gt;
&lt;h3&gt;4.4 Generation 3: AppContainer / LowBox token (Windows 8, October 26, 2012)&lt;/h3&gt;
&lt;p&gt;This is the keystone. Microsoft made three structural extensions to the NT access-token model.&lt;/p&gt;
&lt;p&gt;First, two new SID alphabets. The &lt;em&gt;App Package Authority&lt;/em&gt; (identifier authority 15) carries Package SIDs at &lt;code&gt;S-1-15-2-*&lt;/code&gt; and Capability SIDs at &lt;code&gt;S-1-15-3-*&lt;/code&gt; [@ms-learn-well-known-sids]. A Package SID is a deterministic SHA-2-derived identifier for a specific package; a Capability SID names a permission the package declared in its manifest. Microsoft&apos;s &lt;code&gt;DeriveAppContainerSidFromAppContainerName&lt;/code&gt; API takes a moniker string and returns the corresponding Package SID [@ms-learn-derivesid], which means every Windows 11 machine derives the same &lt;code&gt;S-1-15-2-*&lt;/code&gt; for &lt;code&gt;Microsoft.WindowsCalculator_8wekyb3d8bbwe&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Second, a per-package partition of the Object Manager namespace. Every LowBox process&apos;s references to global named objects under &lt;code&gt;\BaseNamedObjects&lt;/code&gt; are rewritten by the Object Manager to land in &lt;code&gt;\Sessions\&amp;lt;n&amp;gt;\AppContainerNamedObjects\&amp;lt;package-sid&amp;gt;\&lt;/code&gt; and its &lt;code&gt;RPC Control&lt;/code&gt; sub-directory. The kernel captures handles to these directories at token-creation time and uses them at name-lookup time; the rewrite is invisible to the application code. We will name the exact field that holds the handles in §5.&lt;/p&gt;
&lt;p&gt;Third, an always-Low integrity level. Microsoft Learn states the rule plainly: &quot;if you &lt;em&gt;are&lt;/em&gt; in an app container, then the integrity level (IL) is always &lt;em&gt;low&lt;/em&gt;&quot; [@ms-learn-legacy]. The AppContainer flag is orthogonal to integrity level, but AppContainer membership forces the integrity level to Low. Two co-tenanted AppContainer processes can therefore both write to &lt;code&gt;\Sessions\&amp;lt;n&amp;gt;\AppContainerNamedObjects\&amp;lt;package-sid&amp;gt;\&lt;/code&gt; (their per-package directory is owned by the package SID) but neither can write to a Medium-IL object outside.&lt;/p&gt;
&lt;p&gt;The kernel surface is &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt; [@ms-learn-ntcreatelowboxtoken]. Its nine-parameter signature -- the existing token, the desired access, an &lt;code&gt;OBJECT_ATTRIBUTES&lt;/code&gt; parameter, the package SID, a capability count and array, a handle count and array -- is the literal data plane for everything we just described. The Win32 surface is &lt;code&gt;CreateAppContainerProfile&lt;/code&gt; [@ms-learn-createprofile], &lt;code&gt;DeriveAppContainerSidFromAppContainerName&lt;/code&gt; [@ms-learn-derivesid], the &lt;code&gt;SECURITY_CAPABILITIES&lt;/code&gt; struct [@ms-learn-security-capabilities], and the &lt;code&gt;PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES&lt;/code&gt; attribute key that &lt;code&gt;UpdateProcThreadAttribute&lt;/code&gt; accepts [@ms-learn-updateprocthread]. All five APIs share the same minimum supported client: Windows 8 desktop applications [@ms-learn-ntcreatelowboxtoken] [@ms-learn-createprofile] [@ms-learn-security-capabilities].&lt;/p&gt;
&lt;p&gt;The corresponding access-check rule is also explicit. Microsoft Learn formalises the dual-principal intersection: &quot;the permitted access is the intersection of that granted by the user/group SIDs and AppContainer SIDs&quot; [@ms-learn-implementing]. We will walk this five-step check field by field in §5.&lt;/p&gt;

sequenceDiagram
    participant App as Launcher (Medium IL)
    participant W32 as Win32 surface
    participant K as Kernel
    App-&amp;gt;&amp;gt;W32: CreateAppContainerProfile(name, caps)
    W32--&amp;gt;&amp;gt;App: package SID, profile root
    App-&amp;gt;&amp;gt;W32: InitializeProcThreadAttributeList
    App-&amp;gt;&amp;gt;W32: UpdateProcThreadAttribute (PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES)
    App-&amp;gt;&amp;gt;W32: CreateProcess(STARTUPINFOEX)
    W32-&amp;gt;&amp;gt;K: NtCreateLowBoxToken(parent, caps, handles)
    K--&amp;gt;&amp;gt;K: set _TOKEN.Flags.LowBoxToken, stamp PackageSid, Capabilities, assign AppContainerNumber
    K--&amp;gt;&amp;gt;W32: new token handle
    W32--&amp;gt;&amp;gt;App: PROCESS_INFORMATION
    Note over K: Every later SeAccessCheck consults the new flag and fields.
&lt;p&gt;Three motivations drive the next three generations. The &lt;code&gt;ALL_APP_PACKAGES&lt;/code&gt; group SID grants every AppContainer ambient access to a broad default-installed surface; tighter deny-by-default needs the LPAC variant. Win32 applications cannot opt in without source changes; MSIX brings them in via manifest. Kernel exploits bypass the boundary entirely; Windows Sandbox wraps the boundary in Hyper-V.&lt;/p&gt;
&lt;p&gt;The implementation correctness motivations are equally specific. James Forshaw&apos;s &lt;em&gt;Raising the Dead&lt;/em&gt; post on Project Zero in January 2016 walked an issue (P0 Issue 483) in the original &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt; plumbing that, in his own words, looked &quot;on the surface&quot; like at best a local denial of service but turned out to be an elevation of privilege [@p0-raising-dead]. &quot;The root cause of the vulnerability is the NtCreateLowBoxToken system call introduced in Windows 8 to support the creation of locked down tokens for Immersive Applications (formerly known as Metro) as well as IE11&apos;s Enhanced Protected Mode&quot; [@p0-raising-dead]. Microsoft patched the issue in bulletin MS15-111 on October 13, 2015. NVD records the specific CVE within MS15-111 that mapped to Forshaw&apos;s writeup as CVE-2015-2554, &quot;Windows Object Reference Elevation of Privilege Vulnerability&quot; [@nvd-cve-2015-2554]. Three years later, Forshaw&apos;s &lt;em&gt;Windows Exploitation Tricks: Exploiting Arbitrary Object Directory Creation&lt;/em&gt; post (P0 Issue 1550, August 2018) found a related bug in the AppInfo service&apos;s helper API: &quot;The AppInfo service, which is responsible for creating the new application, calls the undocumented API CreateAppContainerToken to do some internal housekeeping. Unfortunately this API creates object directories under the user&apos;s AppContainerNamedObjects object directory to support redirecting BaseNamedObjects and RPC endpoints by the OS&quot; [@p0-1550]. The directory was created &quot;with an explicit security descriptor which allows the user full access&quot; [@p0-1550], and a user-controlled symbolic link in the target could redirect the directory creation almost anywhere in the namespace.&lt;/p&gt;

Forshaw&apos;s two P0 writeups in 2016 and 2018 are textbook cases of *correct primitive, incorrect implementation*. The LowBox flag, the package SID, and the namespace partition are sound. The bugs were in adjacent code -- the kernel&apos;s reference-counting on handles passed to `NtCreateLowBoxToken` (Issue 483, fixed in MS15-111 as CVE-2015-2554 [@nvd-cve-2015-2554]) and the AppInfo service&apos;s overly permissive directory creation under `\AppContainerNamedObjects` (Issue 1550 [@p0-1550]). The lesson is that being *in the kernel* is not the same as being *correct in the kernel*. Forshaw&apos;s parallel work on the Diagnostics Hub Standard Collector Service -- &quot;we&apos;ll call it DiagHub for short&quot; [@p0-arbitrary-write-2018] -- belongs to the same class: brokers and helpers near the AppContainer boundary have repeatedly been the exploitable seams.
&lt;p&gt;&lt;em&gt;Bridge: ambient access via &lt;code&gt;ALL_APP_PACKAGES&lt;/code&gt; motivates the next generation.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;4.5 Generation 4: Less Privileged AppContainer (LPAC) (Windows 10 1607, August 2, 2016)&lt;/h3&gt;
&lt;p&gt;LPAC is one of the most parsimonious changes in the AppContainer chain. Microsoft introduced a synthetic capability with the SID prefix &lt;code&gt;WIN://NOALLAPPPKG&lt;/code&gt;; when this capability is present in the new token&apos;s &lt;code&gt;Capabilities[]&lt;/code&gt; array, the kernel omits the &lt;code&gt;ALL_APP_PACKAGES&lt;/code&gt; group SID from the new token. The Windows 10 1607 Anniversary Update was where the mechanism shipped [@wiki-win10-versions]. Microsoft Learn defines LPAC plainly: &quot;Less Privileged AppContainers (LPAC) are even more isolated than regular AppContainers and require further capabilities to gain access to resources that regular AppContainers already have access to such as the registry, files, and others. For example, LPAC cannot open any keys in the registry unless it has the &lt;em&gt;registryRead&lt;/em&gt; capability and cannot use COM unless it has the &lt;em&gt;lpacCom&lt;/em&gt; capability&quot; [@ms-learn-implementing].&lt;/p&gt;
&lt;p&gt;The Chromium repository carries the most explicit user of this surface today. &lt;code&gt;sandbox/policy/win/lpac_capability.h&lt;/code&gt; enumerates the LPAC capability constants Chromium passes to its renderer / utility / network-service / GPU process tokens: &lt;code&gt;kLpacChromeInstallFiles&lt;/code&gt;, &lt;code&gt;kLpacAppExperience&lt;/code&gt;, &lt;code&gt;kLpacCom&lt;/code&gt;, &lt;code&gt;kLpacCryptoServices&lt;/code&gt;, &lt;code&gt;kLpacEnterprisePolicyChangeNotifications&lt;/code&gt;, &lt;code&gt;kLpacIdentityServices&lt;/code&gt;, &lt;code&gt;kLpacInstrumentation&lt;/code&gt;, &lt;code&gt;kLpacMedia&lt;/code&gt;, &lt;code&gt;kLpacPnPNotifications&lt;/code&gt;, &lt;code&gt;kLpacPnpNotifications&lt;/code&gt;, &lt;code&gt;kLpacServicesManagement&lt;/code&gt;, &lt;code&gt;kLpacSessionManagement&lt;/code&gt;, &lt;code&gt;kRegistryRead&lt;/code&gt;, and several Media Foundation-specific constants [@chromium-lpac]. Microsoft Edge, the Chromium-based rebase Microsoft published on January 15, 2020 [@edge-chromium-ga], inherited all of them. The policy &lt;code&gt;RendererAppContainerEnabled&lt;/code&gt; documents the Edge consumer specifically: &quot;Launches Renderer processes into an App Container for more security benefits... If you enable this policy, Microsoft Edge launches the renderer process in an app container&quot;, available on Windows ≥ 96 [@ms-learn-edge-lpac].&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Bridge: LPAC is still a user-mode boundary. A kernel exploit still bypasses it.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;4.6 Generation 5: MSIX TrustLevel = appContainer (Windows 10 1809, November 13, 2018)&lt;/h3&gt;
&lt;p&gt;The Windows 10 October 2018 Update [@wiki-win10-versions] shipped MSIX with an explicit manifest opt-in for AppContainer. The MSIX schema page documents the two values &lt;code&gt;uap10:TrustLevel&lt;/code&gt; may take: &quot;mediumIL&quot; and &quot;appContainer&quot; [@ms-learn-manifest-app]. There are exactly two trust levels, not three. The widely-stated &quot;third trust level&quot; of &lt;code&gt;runFullTrust&lt;/code&gt; is in fact a &lt;em&gt;restricted capability&lt;/em&gt; that a &lt;code&gt;mediumIL&lt;/code&gt; package may declare under its &lt;code&gt;&amp;lt;Capabilities&amp;gt;&lt;/code&gt; element to register out-of-process COM and similar legacy-Win32 mechanisms [@ms-learn-capabilities]. Microsoft Learn&apos;s MSIX container migration page walks the conversion explicitly, showing the manifest going from &lt;code&gt;&amp;lt;rescap:Capability Name=&quot;runFullTrust&quot; /&amp;gt;&lt;/code&gt; to &lt;code&gt;uap10:TrustLevel=&quot;appContainer&quot;&lt;/code&gt; [@ms-learn-msix-container]. The Wikipedia article on App Installer (which MSIX redirects to) records the App Installer tool&apos;s introduction in the Windows 10 1607 Anniversary Update; the MSIX format itself debuted with Windows 10 1809 in 2018 [@wiki-msix].&lt;/p&gt;
&lt;p&gt;For developers, the win is that the MSIX deployment stack does all the LowBox plumbing on the application&apos;s behalf at install. The application binary itself does not change; the manifest changes, and the App Installer pipeline derives the package SID, creates the profile, and registers the per-package directories.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Bridge: still a user-mode boundary; many packaged apps stay at &lt;code&gt;mediumIL&lt;/code&gt; plus &lt;code&gt;runFullTrust&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;4.7 Generation 6: Windows Sandbox = AppContainer + Hyper-V (Windows 10 1903, May 21, 2019)&lt;/h3&gt;
&lt;p&gt;Microsoft announced Windows Sandbox on the Tech Community blog as &quot;a new lightweight desktop environment tailored for safely running applications in isolation&quot; [@windows-sandbox-announce]. It shipped in the Windows 10 May 2019 Update [@win10-1903-ga] [@wiki-win10-versions]. Three architectural primitives carry the design.&lt;/p&gt;
&lt;p&gt;The first is the Dynamic Base Image, the pristine read-only file set the sandbox&apos;s guest kernel boots from. Microsoft Learn describes its character: &quot;Most OS files are immutable and can be freely shared with Windows Sandbox. A small subset of operating system files are mutable and can&apos;t be shared, so the sandbox base image contains pristine copies of them&quot; [@ms-learn-sandbox-arch]. Footprint figures from the same page: &quot;Before Windows Sandbox is installed, the dynamic base image package is stored as a compressed 30-MB package. Once installed, the dynamic base image occupies about 500 MB of disk space&quot; [@ms-learn-sandbox-arch].&lt;/p&gt;
&lt;p&gt;The second is direct-map memory sharing. &quot;when &lt;code&gt;ntdll.dll&lt;/code&gt; is loaded into memory in the sandbox, it uses the same physical pages as those pages of the binary when loaded on the host&quot; [@ms-learn-sandbox-arch]. Read-only host code does not get re-paged into guest memory; the guest&apos;s virtual address space simply maps to the same physical frames.&lt;/p&gt;
&lt;p&gt;The third is the host-side AppContainer wrap. The host-side container manager has been reported in security-research write-ups to itself run in a LowBox token, though Microsoft&apos;s published Windows Sandbox architecture documentation describes only the Hyper-V partition boundary. The actual containment boundary is the &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 partition&lt;/a&gt;: Microsoft Learn&apos;s overview is explicit. The sandbox &quot;relies on the Microsoft hypervisor to run a separate kernel that isolates Windows Sandbox from the host&quot; [@ms-learn-sandbox-overview]. A kernel exploit inside the sandbox kernel does &lt;em&gt;not&lt;/em&gt; reach the host kernel, because the host kernel is on the other side of the Type-1 hypervisor partition.The 30 MB compressed / 500 MB installed Dynamic Base Image figures come from the Microsoft Learn architecture page [@ms-learn-sandbox-arch]; the &quot;few seconds to launch&quot; wall-clock figure and the &quot;data persists through restarts initiated within the sandbox&quot; Windows 11 22H2 persistence behaviour are from the overview page [@ms-learn-sandbox-overview]. The persistence behaviour explicitly excludes Windows Home edition.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Bridge: WDAG (the per-tab Edge variant of Hyper-V isolation) was retired in Windows 11 24H2. Per-app Hyper-V isolation is no longer the Microsoft direction. Microsoft Defender Application Guard&apos;s overview page documents the retirement: &quot;Microsoft Defender Application Guard, including the Windows Isolated App Launcher APIs, is deprecated for Microsoft Edge for Business and will no longer be updated&quot;; &quot;Starting with Windows 11, version 24H2, Microsoft Defender Application Guard, including the Windows Isolated App Launcher APIs, is no longer available&quot; [@ms-learn-mdag].&lt;/em&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Gen&lt;/th&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Key idea&lt;/th&gt;
&lt;th&gt;What it added&lt;/th&gt;
&lt;th&gt;Failure that motivated the next&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;1993&lt;/td&gt;
&lt;td&gt;Identity equals authority&lt;/td&gt;
&lt;td&gt;Classic NT access token&lt;/td&gt;
&lt;td&gt;Cannot distinguish two apps as same user&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2007&lt;/td&gt;
&lt;td&gt;Add a mandatory label&lt;/td&gt;
&lt;td&gt;Integrity levels, broker pattern&lt;/td&gt;
&lt;td&gt;NO_READ_UP missing; shared identity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2008&lt;/td&gt;
&lt;td&gt;Sandbox in user mode&lt;/td&gt;
&lt;td&gt;Broker / target, restricted token&lt;/td&gt;
&lt;td&gt;No per-app principal in the kernel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2012&lt;/td&gt;
&lt;td&gt;Per-app SID alphabets&lt;/td&gt;
&lt;td&gt;LowBox flag, package and capability SIDs&lt;/td&gt;
&lt;td&gt;ALL_APP_PACKAGES is broad; Win32 cannot opt in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2016&lt;/td&gt;
&lt;td&gt;Deny-by-default&lt;/td&gt;
&lt;td&gt;WIN://NOALLAPPPKG strips ALL_APP_PACKAGES&lt;/td&gt;
&lt;td&gt;Still user-mode; not auto-applied to Win32&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;2018&lt;/td&gt;
&lt;td&gt;Manifest opt-in for Win32&lt;/td&gt;
&lt;td&gt;MSIX TrustLevel=&quot;appContainer&quot;&lt;/td&gt;
&lt;td&gt;Still user-mode; kernel exploit bypasses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;td&gt;Hyper-V isolation wrap&lt;/td&gt;
&lt;td&gt;Dynamic Base Image + direct-map&lt;/td&gt;
&lt;td&gt;Per-app HV not the direction (WDAG retired)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;By 2026 a single Edge browser instance runs the host UI at Medium IL (Gen 0), uses MIC labels (Gen 1), runs a broker / target architecture (Gen 2), launches renderers as LowBox tokens (Gen 3) with &lt;code&gt;WIN://NOALLAPPPKG&lt;/code&gt; (Gen 4), is itself an MSIX-packaged app (Gen 5), and can be invoked inside Windows Sandbox (Gen 6). The chain is &lt;em&gt;layered&lt;/em&gt;, not &lt;em&gt;successive&lt;/em&gt;. The next section opens the LowBox token in WinDbg and walks the five token-level fields that produce these behaviours.&lt;/p&gt;
&lt;h2&gt;5. The Five Token-Level Fields&lt;/h2&gt;
&lt;p&gt;If you load WinDbg on a Windows 11 25H2 kernel debugging session and inspect the access token of a UWP &lt;code&gt;calc.exe&lt;/code&gt; process with &lt;code&gt;!token&lt;/code&gt;, then do the same for a plain &lt;code&gt;notepad.exe&lt;/code&gt;, the differences cluster in five fields. Four of them are pointer-valued; one of them is a single bit. Three of them are public in Microsoft&apos;s &lt;code&gt;winnt.h&lt;/code&gt;-adjacent surface. Two are internal field names that Alex Ionescu reverse-engineered in his Black Hat USA 2015 talk &lt;em&gt;Battle of SKM and IUM: How Windows 10 Rewrites OS Architecture&lt;/em&gt; [@ionescu-bh15], and that James Forshaw walks systematically in his 2024 No Starch Press book &lt;em&gt;Windows Security Internals&lt;/em&gt; [@nostarch-wsi].&lt;/p&gt;
&lt;h3&gt;5.1 &lt;code&gt;_TOKEN.Flags.LowBoxToken&lt;/code&gt; -- the bit&lt;/h3&gt;
&lt;p&gt;The bit. The kernel&apos;s &lt;code&gt;_TOKEN.Flags&lt;/code&gt; word carries a &lt;code&gt;LowBoxToken&lt;/code&gt; bit that &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt; sets and that no later operation modifies [@nostarch-wsi]. Every kernel access-check path consults this bit when deciding which evaluation rules to apply. If the bit is clear, the token is a classic NT user token and &lt;code&gt;SeAccessCheck&lt;/code&gt; proceeds in the 1993 way. If the bit is set, the kernel adds the package-SID claim check, the capability-claim check, and the namespace redirect that we will walk in §5.2 through §5.5.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The bit in &lt;code&gt;_TOKEN.Flags.LowBoxToken&lt;/code&gt; is what tells the kernel to evaluate the per-app principal alongside the per-user principal. Everything else in the LowBox token is bookkeeping for that decision.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The other four fields are the bookkeeping.&lt;/p&gt;
&lt;h3&gt;5.2 &lt;code&gt;_TOKEN.PackageSid&lt;/code&gt; -- the package principal&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;PackageSid&lt;/code&gt; is a &lt;code&gt;PSID&lt;/code&gt; field in the kernel token structure that carries the &lt;code&gt;S-1-15-2-*&lt;/code&gt; SID for the package. The Microsoft surface calls it the AppContainer SID. It is the per-application principal the kernel&apos;s access-check path checks DACL entries against. When you mount a per-package ACL like the one in &lt;code&gt;%LOCALAPPDATA%\Packages\Microsoft.WindowsCalculator_8wekyb3d8bbwe\&lt;/code&gt; and find an access control entry naming &lt;code&gt;S-1-15-2-466767348-3739614953-2700836392-...&lt;/code&gt;, that ACE is allowing or denying exactly the LowBox process whose &lt;code&gt;PackageSid&lt;/code&gt; matches.&lt;/p&gt;

A `PSID` value with the prefix `S-1-15-2-` derived deterministically by SHA-2 hash from the package&apos;s family name and used as the *per-application principal* in DACL evaluations against AppContainer processes [@ms-learn-derivesid]. The same package family name produces the same Package SID on every Windows 8 or later machine, which is why per-package directories on disk can be ACL&apos;d to a known SID at install time. The 32-bit sub-authorities are sequential 4-byte slices of the underlying hash.
&lt;p&gt;The signature of &lt;code&gt;DeriveAppContainerSidFromAppContainerName&lt;/code&gt; confirms the &lt;em&gt;derivation&lt;/em&gt; is intended to be deterministic and reproducible [@ms-learn-derivesid]: &quot;Gets the SID of the specified profile&quot;, with a Windows 8 minimum supported client. The reverse direction -- start from a Package SID and recover the moniker -- is not feasible without the inverse hash, which is by construction infeasible.&lt;/p&gt;
&lt;h3&gt;5.3 &lt;code&gt;_TOKEN.Capabilities[]&lt;/code&gt; -- the per-capability claim array&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Capabilities[]&lt;/code&gt; is a kernel array of &lt;code&gt;SID_AND_ATTRIBUTES&lt;/code&gt; structures, populated by &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt; from the &lt;code&gt;Capabilities&lt;/code&gt; parameter the caller passes [@ms-learn-ntcreatelowboxtoken]. Each capability SID lives in the &lt;code&gt;S-1-15-3-*&lt;/code&gt; alphabet [@ms-learn-well-known-sids] and represents a specific permission the package declared in its manifest [@ms-learn-capabilities]. Each capability is &lt;em&gt;checked&lt;/em&gt; by the relevant resource manager: the Windows Filtering Platform checks the &lt;code&gt;internetClient&lt;/code&gt; capability SID for outbound network packets, the Object Manager checks namespace capabilities for the per-package directories, the contacts WinRT broker checks the &lt;code&gt;contactsLibrary&lt;/code&gt; capability SID against the contacts ACL.&lt;/p&gt;

A `PSID` value with the prefix `S-1-15-3-` representing a per-package permission [@ms-learn-well-known-sids]. Capability SIDs are stamped into a LowBox token&apos;s `Capabilities[]` array at process creation and consulted by the kernel callout, broker, or kernel access-check path that gates the corresponding resource [@ms-learn-implementing]. The capability vocabulary is open-ended: standard Microsoft-defined capabilities such as `internetClient` coexist with developer-defined custom capabilities and Chromium&apos;s `Lpac*` synthetic constants.
&lt;p&gt;Microsoft Learn&apos;s AppContainer-implementation page formalises the dual-principal access rule for these fields with the same intersection wording quoted in §4.4: the granted access is the intersection of what the user / group SIDs allow and what the AppContainer SIDs allow [@ms-learn-implementing]. The capability-claim check fails closed: a capability the manifest did not declare is not in &lt;code&gt;Capabilities[]&lt;/code&gt;, the kernel callout does not find a matching claim, and the access fails.&lt;/p&gt;
&lt;p&gt;Selected capability SIDs and the resources they gate look like this.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;What it gates&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;internetClient&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Outbound TCP/UDP via WFP callout&lt;/td&gt;
&lt;td&gt;[@ms-learn-capabilities]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;internetClientServer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inbound + outbound network&lt;/td&gt;
&lt;td&gt;[@ms-learn-capabilities]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;picturesLibrary&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read access to user&apos;s Pictures library&lt;/td&gt;
&lt;td&gt;[@ms-learn-capabilities]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;documentsLibrary&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restricted; access to user Documents&lt;/td&gt;
&lt;td&gt;[@ms-learn-capabilities]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;enterpriseAuthentication&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restricted; Kerberos authentication&lt;/td&gt;
&lt;td&gt;[@ms-learn-capabilities]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;microphone&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Audio input device&lt;/td&gt;
&lt;td&gt;[@ms-learn-capabilities]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;webcam&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Video input device&lt;/td&gt;
&lt;td&gt;[@ms-learn-capabilities]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;location&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Geolocation API&lt;/td&gt;
&lt;td&gt;[@ms-learn-capabilities]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;runFullTrust&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Restricted; opt-out of AppContainer for mediumIL packages&lt;/td&gt;
&lt;td&gt;[@ms-learn-capabilities]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;WIN://NOALLAPPPKG&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Strip ALL_APP_PACKAGES from token (LPAC)&lt;/td&gt;
&lt;td&gt;[@ms-learn-implementing] [@chromium-lpac]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lpacCom&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;LPAC needs this to use COM&lt;/td&gt;
&lt;td&gt;[@ms-learn-implementing]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;registryRead&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;LPAC needs this to open registry keys&lt;/td&gt;
&lt;td&gt;[@ms-learn-implementing]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Several rows here have a &lt;em&gt;restricted capability&lt;/em&gt; designation in the manifest schema; Microsoft Learn&apos;s note on &lt;code&gt;runFullTrust&lt;/code&gt; is that &quot;a Medium IL app &lt;em&gt;needs&lt;/em&gt; to declare the &lt;strong&gt;runFullTrust&lt;/strong&gt; restricted capability&quot; before it can register out-of-process COM [@ms-learn-capabilities]. The same page gives the structural advice every developer should treat as default: &quot;be sure to declare only the capabilities that your app needs&quot; [@ms-learn-capabilities].&lt;/p&gt;
&lt;h3&gt;5.4 &lt;code&gt;_TOKEN.LowboxNumberEntry&lt;/code&gt; -- the per-instance number&lt;/h3&gt;
&lt;p&gt;Two simultaneous Microsoft Calculator processes both carry the same Package SID and the same Capabilities array. What makes them distinct principals to the kernel is the &lt;code&gt;AppContainerNumber&lt;/code&gt; -- a per-instance integer assigned by &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt; at token-creation time. The kernel maintains a session-scoped AVL tree keyed by this number. Ionescu named the corresponding kernel data structure in his 2015 reverse engineering [@ionescu-bh15]; the public-facing reference is Forshaw&apos;s 2024 book [@nostarch-wsi].&lt;/p&gt;

A per-instance integer assigned by `NtCreateLowBoxToken` to each LowBox token at creation time and used as the key into the kernel&apos;s session-scoped AVL trees that hold the per-instance named-object handles and the per-instance namespace state [@nostarch-wsi]. Two simultaneously running processes of the same package have the *same* Package SID but *different* `AppContainerNumber` values. This is the field that makes two co-tenanted calculator processes opaque to each other.
&lt;p&gt;The internal field names &lt;code&gt;_TOKEN.Flags.LowBoxToken&lt;/code&gt;, the AVL tree at &lt;code&gt;nt!SepLowBoxNumberTable&lt;/code&gt;, and the &lt;code&gt;AppContainerNumber&lt;/code&gt; are not in any official Microsoft document. The canonical primary sources for them are Ionescu&apos;s Black Hat 2015 reverse engineering [@ionescu-bh15] and Forshaw&apos;s 2024 book &lt;em&gt;Windows Security Internals&lt;/em&gt; [@nostarch-wsi]. Microsoft&apos;s own &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt; page documents the &lt;em&gt;behaviour&lt;/em&gt; but does not name the internal fields [@ms-learn-ntcreatelowboxtoken].&lt;/p&gt;
&lt;h3&gt;5.5 &lt;code&gt;_TOKEN.LowboxHandlesEntry&lt;/code&gt; -- the saved namespace handles&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;LowboxHandlesEntry&lt;/code&gt; points into a &lt;em&gt;second&lt;/em&gt; AVL tree, keyed by the same &lt;code&gt;AppContainerNumber&lt;/code&gt;. The entry carries kernel handles to the per-package directory at &lt;code&gt;\Sessions\&amp;lt;n&amp;gt;\AppContainerNamedObjects\&amp;lt;package-sid&amp;gt;\&lt;/code&gt; and to its &lt;code&gt;RPC Control&lt;/code&gt; sub-directory. The handles are captured by &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt; (the syscall&apos;s last two parameters are &lt;code&gt;HandleCount&lt;/code&gt; and &lt;code&gt;Handles[]&lt;/code&gt; [@ms-learn-ntcreatelowboxtoken]) and stored in the AVL tree for later lookups.&lt;/p&gt;
&lt;p&gt;When a LowBox process calls &lt;code&gt;NtCreateMutant(&quot;\\Global\\Foo&quot;)&lt;/code&gt;, the Object Manager&apos;s path-walker (&lt;code&gt;ObpLookupObjectName&lt;/code&gt;) consults &lt;code&gt;LowboxHandlesEntry&lt;/code&gt; to substitute the rewrite. The lookup proceeds against the per-package directory rather than the global &lt;code&gt;\BaseNamedObjects&lt;/code&gt;. The application&apos;s request for &quot;Global\Foo&quot; silently resolves to &quot;\Sessions\1\AppContainerNamedObjects\S-1-15-2-...\Foo&quot; -- which is invisible to any other package, because every other package has a different per-instance handle pointing to a different directory.&lt;/p&gt;
&lt;p&gt;This is the mechanism Forshaw&apos;s Project Zero Issue 1550 attacked in 2018. Forshaw&apos;s writeup names the surface: &quot;this API creates object directories under the user&apos;s AppContainerNamedObjects object directory to support redirecting BaseNamedObjects and RPC endpoints by the OS&quot; [@p0-1550]. The implementation bug was in the &lt;em&gt;helper&lt;/em&gt; (the AppInfo service&apos;s &lt;code&gt;CreateAppContainerToken&lt;/code&gt;); the primitive itself is structurally sound.&lt;/p&gt;
&lt;h3&gt;The five-step LowBox access check&lt;/h3&gt;
&lt;p&gt;Putting the five fields together, every access decision from a LowBox process runs through this five-step sequence.&lt;/p&gt;

flowchart TD
    A[Access requested by LowBox process] --&amp;gt; B[&quot;Step 1: Classic DACL walk&lt;br /&gt;SeAccessCheck against user + groups&quot;]
    B --&amp;gt; C[&quot;Step 2: MIC label check&lt;br /&gt;NO_WRITE_UP (default)&lt;br /&gt;against IL=Low&quot;]
    C --&amp;gt; D[&quot;Step 3: Package SID claim&lt;br /&gt;does object&apos;s DACL name PackageSid?&quot;]
    D --&amp;gt; E[&quot;Step 4: Capability claim&lt;br /&gt;WFP / broker / kernel callout&lt;br /&gt;checks Capabilities array&quot;]
    E --&amp;gt; F[&quot;Step 5: Namespace redirect&lt;br /&gt;ObpLookupObjectName rewrites&lt;br /&gt;BaseNamedObjects path&quot;]
    F --&amp;gt; G[Allow]
    F --&amp;gt; H[Deny]
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Classic DACL check.&lt;/strong&gt; &lt;code&gt;SeAccessCheck&lt;/code&gt; walks the object&apos;s DACL exactly as if the caller were a normal user. The user SID and group SIDs in the token contribute to the granted-access mask.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MIC label check.&lt;/strong&gt; The SACL&apos;s mandatory-integrity ACE is compared against the token&apos;s &lt;code&gt;IntegrityLevel&lt;/code&gt;. AppContainer tokens are &lt;em&gt;always&lt;/em&gt; Low [@ms-learn-legacy]; if the object&apos;s mandatory ACE says &lt;code&gt;NO_WRITE_UP&lt;/code&gt; and the request is a write, the access is denied here. The default &lt;code&gt;NO_WRITE_UP&lt;/code&gt;-only policy from Vista [@ms-learn-mic] means a Low-IL LowBox process still cannot write to a default-ACL Medium object.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Package SID claim check.&lt;/strong&gt; The object&apos;s DACL may grant access specifically to the Package SID. If matched, the package SID claim contributes to the granted mask. This is the channel by which per-package data directories are made accessible to the package and only the package.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Capability claim check.&lt;/strong&gt; A WFP callout, a broker, or a kernel callout checks whether the requested resource maps onto a declared capability. The Windows Filtering Platform compares the outbound socket&apos;s destination against the firewall callout&apos;s allow list; if the package did not declare &lt;code&gt;internetClient&lt;/code&gt;, no &lt;code&gt;S-1-15-3-*&lt;/code&gt; capability matches and the connection fails.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Named-object namespace redirect.&lt;/strong&gt; References to objects under &lt;code&gt;\BaseNamedObjects&lt;/code&gt; (mutants, events, sections, RPC endpoints) are rewritten by the Object Manager to &lt;code&gt;\Sessions\&amp;lt;n&amp;gt;\AppContainerNamedObjects\&amp;lt;package-sid&amp;gt;\&lt;/code&gt; before lookup. This is what makes &quot;Global\Foo&quot; mean a per-package object for the LowBox process.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Microsoft Learn&apos;s intersection rule is the canonical articulation: &quot;the permitted access is the intersection of that granted by the user/group SIDs and AppContainer SIDs&quot; [@ms-learn-implementing]. A user / group ACE that grants &lt;code&gt;FILE_READ_DATA&lt;/code&gt; and a package SID ACE that grants &lt;code&gt;FILE_READ_DATA | FILE_WRITE_DATA&lt;/code&gt; together produce &lt;code&gt;FILE_READ_DATA&lt;/code&gt;, because the intersection is the smaller set.&lt;/p&gt;

flowchart LR
    T[&quot;_TOKEN structure&quot;] --&amp;gt; U[&quot;User&lt;br /&gt;S-1-5-21-...&quot;]
    T --&amp;gt; G[&quot;Groups[]&quot;]
    T --&amp;gt; P[&quot;Privileges[]&quot;]
    T --&amp;gt; I[&quot;IntegrityLevel&lt;br /&gt;(always Low for LowBox)&quot;]
    T --&amp;gt; F[&quot;Flags&lt;br /&gt;(LowBoxToken bit)&quot;]
    T --&amp;gt; PK[&quot;PackageSid&lt;br /&gt;S-1-15-2-...&quot;]
    T --&amp;gt; CA[&quot;Capabilities[]&lt;br /&gt;SID_AND_ATTRIBUTES&quot;]
    T --&amp;gt; LN[&quot;LowboxNumberEntry&quot;]
    T --&amp;gt; LH[&quot;LowboxHandlesEntry&quot;]
    LN -.-&amp;gt;|&quot;keyed lookup&quot;| AVL1[&quot;nt!SepLowBoxNumberTable&lt;br /&gt;AVL tree by AppContainerNumber&quot;]
    LH -.-&amp;gt;|&quot;keyed lookup&quot;| AVL2[&quot;nt!SepLowBoxHandlesTable&lt;br /&gt;AVL tree of saved directory handles&quot;]
&lt;h3&gt;The Win32 launch surface&lt;/h3&gt;
&lt;p&gt;The minimum production launch sequence has four moving parts. &lt;code&gt;CreateAppContainerProfile&lt;/code&gt; derives the Package SID, lays down the per-package profile on disk, and returns the SID and the profile root [@ms-learn-createprofile]. &lt;code&gt;InitializeProcThreadAttributeList&lt;/code&gt; allocates a process-attribute list. &lt;code&gt;UpdateProcThreadAttribute&lt;/code&gt; installs the &lt;code&gt;PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES&lt;/code&gt; attribute, whose payload is a &lt;code&gt;SECURITY_CAPABILITIES&lt;/code&gt; struct carrying the AppContainer SID, the capability array, and a count [@ms-learn-updateprocthread] [@ms-learn-security-capabilities]. &lt;code&gt;CreateProcess&lt;/code&gt; (or &lt;code&gt;CreateProcessAsUser&lt;/code&gt;) with &lt;code&gt;STARTUPINFOEX&lt;/code&gt; carrying the attribute list launches the new process. Inside the kernel, &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt; does the work [@ms-learn-ntcreatelowboxtoken]: it sets &lt;code&gt;_TOKEN.Flags.LowBoxToken&lt;/code&gt;, stamps &lt;code&gt;PackageSid&lt;/code&gt;, populates &lt;code&gt;Capabilities[]&lt;/code&gt;, assigns the &lt;code&gt;AppContainerNumber&lt;/code&gt;, and saves the directory handles into &lt;code&gt;LowboxHandlesEntry&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;{`
// This sketches the &lt;em&gt;shape&lt;/em&gt; of the Win32 launch surface, not real P/Invoke.
// Real callers go through Win32 from C/C++/Rust or via interop.&lt;/p&gt;
&lt;p&gt;type PSID = string; // S-1-15-2-... package SIDs
type SECURITY_CAPABILITIES = {
  AppContainerSid: PSID;
  Capabilities: { Sid: PSID; Attributes: number }[];
};&lt;/p&gt;
&lt;p&gt;function createSandboxedProcess(packageName: string, exe: string) {
  // Step 1: derive Package SID, create per-package profile on disk.
  const packageSid = createAppContainerProfile(packageName, {
    displayName: packageName,
    description: &quot;Sandboxed worker&quot;,
    capabilities: [&quot;internetClient&quot;],
  });&lt;/p&gt;
&lt;p&gt;  // Step 2: build the capabilities payload.
  const caps: SECURITY_CAPABILITIES = {
    AppContainerSid: packageSid,
    Capabilities: [
      { Sid: &quot;S-1-15-3-1&quot;, Attributes: 0 }, // internetClient capability SID
    ],
  };&lt;/p&gt;
&lt;p&gt;  // Step 3: install PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES.
  const attrList = initializeProcThreadAttributeList(1);
  updateProcThreadAttribute(
    attrList,
    &quot;PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES&quot;,
    caps,
  );&lt;/p&gt;
&lt;p&gt;  // Step 4: launch. Kernel calls NtCreateLowBoxToken under the hood.
  return createProcess(exe, { startupInfoEx: { attributeList: attrList } });
}&lt;/p&gt;
&lt;p&gt;// Demonstration only:
const info = createSandboxedProcess(&quot;contoso.WidgetApp_8wekyb3d8bbwe&quot;, &quot;widget.exe&quot;);
console.log(&quot;Launched LowBox PID&quot;, info.pid);
console.log(&quot;Package SID:&quot;, info.packageSid);
console.log(&quot;Capabilities granted:&quot;, info.caps);
`}&lt;/p&gt;
&lt;h3&gt;LPAC: one synthetic capability&lt;/h3&gt;
&lt;p&gt;A LowBox token whose &lt;code&gt;Capabilities[]&lt;/code&gt; includes the synthetic &lt;code&gt;WIN://NOALLAPPPKG&lt;/code&gt; capability causes the kernel to &lt;em&gt;omit&lt;/em&gt; the &lt;code&gt;ALL_APP_PACKAGES&lt;/code&gt; group SID from the new token entirely. The token&apos;s groups become strictly the package&apos;s own derivations, plus the declared capabilities, plus the universal SIDs. Microsoft Learn&apos;s wording: &quot;Less Privileged AppContainers (LPAC) are even more isolated than regular AppContainers and require further capabilities to gain access to resources that regular AppContainers already have access to such as the registry, files, and others. For example, LPAC cannot open any keys in the registry unless it has the &lt;em&gt;registryRead&lt;/em&gt; capability and cannot use COM unless it has the &lt;em&gt;lpacCom&lt;/em&gt; capability&quot; [@ms-learn-implementing]. The Chromium source&apos;s &lt;code&gt;lpac_capability.h&lt;/code&gt; enumerates the full set Microsoft Edge inherits at renderer launch [@chromium-lpac]. Section 6.3 walks the practical consequences.&lt;/p&gt;
&lt;p&gt;These five fields and five steps are everything AppContainer is. The next section walks the four places they are exercised at production scale on a 2026 Windows 11 machine: every UWP app, every MSIX-packaged Win32 app, every Edge renderer, and every Windows Sandbox launch.&lt;/p&gt;
&lt;h2&gt;6. The Four 2026 Production Deployments&lt;/h2&gt;
&lt;p&gt;AppContainer is not a demonstration primitive. It is the principal mechanism behind every piece of trustworthy code execution on a modern Windows desktop. Four populations exercise it at production scale on a Windows 11 25H2 machine.&lt;/p&gt;
&lt;h3&gt;6.1 UWP apps&lt;/h3&gt;
&lt;p&gt;The Microsoft Calculator (&lt;code&gt;Microsoft.WindowsCalculator&lt;/code&gt;), the Mail and Calendar apps, Photos, Xbox, Settings, the modern Notepad, and the rest of the Windows-bundled Universal Windows Platform applications are MSIX packages whose manifest declares &lt;code&gt;uap10:TrustLevel=&quot;appContainer&quot;&lt;/code&gt; [@ms-learn-manifest-app]. Capabilities are declared explicitly in each manifest&apos;s &lt;code&gt;&amp;lt;Capabilities&amp;gt;&lt;/code&gt; element [@ms-learn-capabilities]. A typical UWP capabilities profile is small: &lt;code&gt;internetClient&lt;/code&gt; for a mail client; &lt;code&gt;picturesLibrary&lt;/code&gt; for Photos; &lt;code&gt;userAccountInformation&lt;/code&gt; for an app that wants the user&apos;s display name. The LowBox token is the only principal that ACLs in the per-package data directory at &lt;code&gt;%LOCALAPPDATA%\Packages\&amp;lt;PFN&amp;gt;\&lt;/code&gt; name. The six AppContainer isolation axes -- credential, device, file, network, process, and window isolation -- are listed on Microsoft Learn&apos;s AppContainer Isolation page [@ms-learn-isolation], and every UWP app exercises all six. Process isolation is AppContainer-only; UWP apps do not run inside Hyper-V.&lt;/p&gt;
&lt;h3&gt;6.2 MSIX-packaged desktop apps&lt;/h3&gt;
&lt;p&gt;The new File Explorer (in 25H2), the new Outlook, Power Automate Desktop, the modernised Snipping Tool, and a growing catalogue of third-party MSIX packages live in a different population. They are packaged using MSIX, and their manifest declares either &lt;code&gt;uap10:TrustLevel=&quot;mediumIL&quot;&lt;/code&gt; (with &lt;code&gt;&amp;lt;rescap:Capability Name=&quot;runFullTrust&quot; /&amp;gt;&lt;/code&gt;) or &lt;code&gt;uap10:TrustLevel=&quot;appContainer&quot;&lt;/code&gt; [@ms-learn-msix-container] [@ms-learn-manifest-app]. Many start at &lt;code&gt;mediumIL&lt;/code&gt; for legacy interop reasons and gradually migrate as the application drops dependencies on HKLM writes, on absolute file paths, and on registering arbitrary out-of-process COM. Microsoft Learn&apos;s MSIX page shows the migration shape: change the manifest from &lt;code&gt;&amp;lt;rescap:Capability Name=&quot;runFullTrust&quot; /&amp;gt;&lt;/code&gt; to &lt;code&gt;uap10:TrustLevel=&quot;appContainer&quot;&lt;/code&gt;, and the MSIX deployment stack does the LowBox plumbing on the application&apos;s behalf at install [@ms-learn-msix-container]. The &lt;code&gt;App Installer&lt;/code&gt; component is the surface that performs the install [@wiki-msix].&lt;/p&gt;
&lt;h3&gt;6.3 Microsoft Edge: LPAC renderers&lt;/h3&gt;
&lt;p&gt;Every Microsoft Edge renderer process on Windows 10 RS5 and later runs as a LowBox token with &lt;code&gt;WIN://NOALLAPPPKG&lt;/code&gt; set. The browser is Chromium-based -- &quot;A little over a year ago, we announced our intention to rebuild Microsoft Edge on the Chromium open source project&quot; said the Windows Experience Blog on January 15, 2020 [@edge-chromium-ga] -- and inherits Chromium&apos;s broker / target architecture and job-object lockdown [@chromium-sandbox]. The LPAC layer is the Windows-specific addition. Chromium&apos;s &lt;code&gt;lpac_capability.h&lt;/code&gt; defines the synthetic capabilities the renderer needs to function with &lt;code&gt;ALL_APP_PACKAGES&lt;/code&gt; stripped: &lt;code&gt;kLpacChromeInstallFiles&lt;/code&gt;, &lt;code&gt;kLpacCom&lt;/code&gt;, &lt;code&gt;kLpacCryptoServices&lt;/code&gt;, &lt;code&gt;kLpacIdentityServices&lt;/code&gt;, &lt;code&gt;kLpacMedia&lt;/code&gt;, &lt;code&gt;kRegistryRead&lt;/code&gt;, and the rest [@chromium-lpac]. Microsoft&apos;s &lt;code&gt;RendererAppContainerEnabled&lt;/code&gt; policy documents the customer-facing surface: &quot;Launches Renderer processes into an App Container for more security benefits&quot;; &quot;This policy will only take effect on Windows 10 RS5 and above&quot;; &quot;Windows: ≥ 96&quot;; &quot;If you enable this policy, Microsoft Edge launches the renderer process in an app container&quot; [@ms-learn-edge-lpac]. The policy is rolled out broadly on current Edge channels; the documentation itself notes the unconfigured default will move to in-app-container in a future update [@ms-learn-edge-lpac].&lt;/p&gt;

A renderer process has no reason to access the registry, no reason to open arbitrary COM classes, no reason to read most of the per-package directories under `%LOCALAPPDATA%\Packages\`. LPAC strips the ambient access by removing `ALL_APP_PACKAGES` from the token, and the renderer adds back only the synthetic capabilities Chromium needs to function (font cache reads, media foundation handles, the install-files capability). The combination -- *Chromium broker / target* outside the kernel plus *LPAC* inside the kernel -- is the most defended browser configuration on any current desktop OS. The Chromium sandbox document says the design intent: &quot;Sandbox operates at process-level granularity. Anything that needs to be sandboxed needs to live on a separate process&quot; [@chromium-sandbox]. LPAC is the Windows-side answer to *how* a separate process gets the smallest possible authority.
&lt;p&gt;This is the second insight to keep. Modern Microsoft Edge runs &lt;em&gt;all six post-Gen-0 mechanisms simultaneously&lt;/em&gt;: Medium IL host UI (Gen 0 baseline), MIC labels on objects (Gen 1), Chromium broker / target architecture (Gen 2), LowBox token on renderers (Gen 3), &lt;code&gt;WIN://NOALLAPPPKG&lt;/code&gt; LPAC (Gen 4), MSIX-packaged Edge (Gen 5), and optionally Windows Sandbox (Gen 6). Microsoft and Google reached the same browser-sandbox design from opposite directions, and the production browser uses &lt;em&gt;both&lt;/em&gt;. The right question is not &quot;AppContainer or Chromium sandbox?&quot; but &quot;where in the stack does each layer belong?&quot;&lt;/p&gt;
&lt;h3&gt;6.4 Windows Sandbox&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;WindowsSandbox.exe&lt;/code&gt; -- available on Pro, Enterprise, and Education editions of Windows 10 1903 and later [@ms-learn-sandbox-overview] -- launches the most isolated of the four populations. Three layers carry the design. The Hyper-V partition isolates a separate kernel from the host: &quot;It relies on the Microsoft hypervisor to run a separate kernel that isolates Windows Sandbox from the host&quot; [@ms-learn-sandbox-overview]. The Dynamic Base Image is the read-only file set the guest kernel boots from, stored as a 30 MB compressed package and unpacked to 500 MB on disk [@ms-learn-sandbox-arch]. Direct-map memory sharing means the host&apos;s read-only binaries (such as &lt;code&gt;ntdll.dll&lt;/code&gt;) &quot;use the same physical pages as those pages of the binary when loaded on the host&quot; [@ms-learn-sandbox-arch]. Security-research write-ups report that the host-side container manager that orchestrates this runs in an AppContainer; Microsoft&apos;s published architecture page does not document the host-side container-manager token type directly, so the load-bearing isolation property remains the Hyper-V partition itself.&lt;/p&gt;
&lt;p&gt;Starting with Windows 11 22H2, in-sandbox restarts persist data: &quot;data persists through restarts initiated within the sandbox&quot; [@ms-learn-sandbox-overview]. The persistence is bounded by the sandbox lifecycle; closing the sandbox window discards the guest entirely.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; On a running Windows 11 machine you can confirm the AppContainer status of any process two ways. Sysinternals Process Explorer&apos;s Security tab on a UWP process shows the package SID, the &lt;code&gt;AppContainer&lt;/code&gt; group, and the integrity level Low (&lt;code&gt;S-1-16-4096&lt;/code&gt;). James Forshaw&apos;s &lt;code&gt;NtObjectManager&lt;/code&gt; PowerShell module exposes &lt;code&gt;Get-NtToken&lt;/code&gt; and &lt;code&gt;Get-AccessibleObject -ProcessId &amp;lt;pid&amp;gt;&lt;/code&gt; which enumerate the kernel-visible token fields and the objects the process can reach [@sandboxsecuritytools]. Microsoft&apos;s own &lt;code&gt;SandboxSecurityTools&lt;/code&gt; repository on GitHub publishes &lt;code&gt;LaunchAppContainer&lt;/code&gt;: &quot;The LaunchAppContainer tool can be used to run applications in AppContainer or Less Privileged AppContainer (LPAC) sandboxes&quot; [@sandboxsecuritytools].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A comparison table for the four populations:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Deployment&lt;/th&gt;
&lt;th&gt;Year of ship&lt;/th&gt;
&lt;th&gt;Trust boundary&lt;/th&gt;
&lt;th&gt;Capabilities typical&lt;/th&gt;
&lt;th&gt;Kernel-exploit defence&lt;/th&gt;
&lt;th&gt;Hyper-V wrap&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;UWP apps&lt;/td&gt;
&lt;td&gt;2012&lt;/td&gt;
&lt;td&gt;AppContainer&lt;/td&gt;
&lt;td&gt;1-5, declared in manifest&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MSIX-packaged desktop apps&lt;/td&gt;
&lt;td&gt;2018&lt;/td&gt;
&lt;td&gt;AppContainer or mediumIL&lt;/td&gt;
&lt;td&gt;Varies; many declare runFullTrust&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microsoft Edge LPAC renderers&lt;/td&gt;
&lt;td&gt;2020 (Edge Chromium GA)&lt;/td&gt;
&lt;td&gt;LPAC inside Chromium target&lt;/td&gt;
&lt;td&gt;Synthetic Lpac* set&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Optional via Windows Sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows Sandbox&lt;/td&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;td&gt;Hyper-V partition + AppContainer&lt;/td&gt;
&lt;td&gt;Implicit&lt;/td&gt;
&lt;td&gt;Yes (separate kernel)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Microsoft Defender Application Guard, the per-tab Hyper-V isolation for Edge that shipped in Windows 10 1709, was retired in Windows 11 24H2 [@ms-learn-mdag]. The replacement is Microsoft Defender SmartScreen plus the Edge Management Service; the per-tab Hyper-V boundary is gone. Per-application hypervisor isolation is no longer the Microsoft direction; Windows Sandbox is the surviving Hyper-V-wrapped variant.&lt;/p&gt;
&lt;p&gt;On the same machine, the same kernel, the same &lt;code&gt;SeAccessCheck&lt;/code&gt; runs the same five-step LowBox path millions of times a second across these four populations. Yet the LowBox token is not what every OS does. The next section asks: how do macOS, iOS, Android, and Linux solve the same problem -- and what do the differences teach us about &lt;em&gt;kernel-token-side&lt;/em&gt; enforcement versus &lt;em&gt;user-mode-policy-side&lt;/em&gt; enforcement?&lt;/p&gt;
&lt;h2&gt;7. Competing Approaches Across Operating Systems&lt;/h2&gt;
&lt;p&gt;Apple, Google, and the Linux distributions solved the same problem -- &lt;em&gt;how does the kernel give two co-tenanted applications different verdicts?&lt;/em&gt; -- with four structurally different answers. Each trade-off is a different bet on where in the system the per-app principal should live.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;iOS and iPadOS application sandbox.&lt;/strong&gt; Apple ships a mandatory per-app sandbox on every device since iPhone OS 1 in 2007 [@apple-platform-security-app]. From iPhone OS 2.0 / the 2008 App Store launch onward, the developer-facing principal is two-valued: a Bundle ID (developer-chosen string) and a Team ID (signer identity). Per-app entitlements declared in the signed bundle are the capability vocabulary. Enforcement is &lt;em&gt;mandatory&lt;/em&gt; -- there is no opt-out. The per-app filesystem container at &lt;code&gt;Containers/Data/Application/&amp;lt;bundle-uuid&amp;gt;/&lt;/code&gt; is the iOS analogue of &lt;code&gt;%LOCALAPPDATA%\Packages\&amp;lt;PFN&amp;gt;\&lt;/code&gt;. Where Windows extends the kernel token, iOS keeps the per-app profile in a TrustedBSD-derived MAC framework and consults it from kernel mode at every relevant resource manager.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;macOS App Sandbox.&lt;/strong&gt; macOS uses the same entitlement primitive Apple developed for iOS, plus a user-mode policy daemon (&lt;code&gt;sandboxd&lt;/code&gt;) that holds the per-app profile [@apple-app-sandbox]. The trade-off is that more decisions are made in user mode than on iOS, with corresponding flexibility costs. App Sandbox is &lt;em&gt;optional&lt;/em&gt; on macOS for non-App-Store distribution, in contrast to iOS, where it is mandatory.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Android per-app UID plus SELinux plus seccomp-bpf.&lt;/strong&gt; Android&apos;s &lt;em&gt;Application Sandbox&lt;/em&gt; page on the AOSP documents the layered model: every app receives a distinct UNIX UID; classical UNIX discretionary access control enforces per-app isolation; SELinux mandatory access control is layered on top from Android 5.0 onward; per-physical-user partitions arrived in Android 6.0; the seccomp-bpf system-call filter is required from Android 8.0; per-app SELinux sandboxes apply for &lt;code&gt;targetSdkVersion &amp;gt;= 28&lt;/code&gt; (Android 9); and path-based file restrictions tightened in Android 10 [@android-app-sandbox]. The kernel-side per-app principal is the UNIX UID. The MAC layers above add additional confinement.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Linux unprivileged namespaces plus seccomp plus AppArmor / SELinux plus Flatpak / Snap.&lt;/strong&gt; Linux&apos;s per-application sandbox is &lt;em&gt;composable&lt;/em&gt;. No first-class per-app principal lives in the kernel. The application sandboxes are layered above the user-keyed DAC: Flatpak and Snap build on unprivileged user namespaces and seccomp filters; Chromium on Linux uses the same kit. The Chromium FAQ&apos;s kernel-bugs caveat from §3 applies in every cross-OS configuration: a user-mode sandbox cannot defend against bugs in the kernel it runs on [@chromium-sandbox-faq].&lt;/p&gt;
&lt;p&gt;The structural axis falls out cleanly. macOS keeps the per-app profile in user-mode &lt;em&gt;outside&lt;/em&gt; the kernel token; iOS keeps it in user-mode but enforces &lt;em&gt;mandatorily&lt;/em&gt;; Android allocates a real UNIX UID; Windows extends the kernel-token SID alphabet. &lt;strong&gt;Windows is the only one of the four that partitions the kernel data structure.&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operating system&lt;/th&gt;
&lt;th&gt;Primitive&lt;/th&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Where the principal lives&lt;/th&gt;
&lt;th&gt;Per-call cost model&lt;/th&gt;
&lt;th&gt;Kernel-exploit containment&lt;/th&gt;
&lt;th&gt;Mandatory?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Windows 8+&lt;/td&gt;
&lt;td&gt;LowBox token (AppContainer)&lt;/td&gt;
&lt;td&gt;2012&lt;/td&gt;
&lt;td&gt;Inside the access token (kernel)&lt;/td&gt;
&lt;td&gt;Inline in &lt;code&gt;SeAccessCheck&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Opt-in via manifest / SDK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;td&gt;App Sandbox + entitlements&lt;/td&gt;
&lt;td&gt;2011 (Mac App Store)&lt;/td&gt;
&lt;td&gt;User-mode &lt;code&gt;sandboxd&lt;/code&gt; profile&lt;/td&gt;
&lt;td&gt;IPC for some decisions&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Opt-in (Mac App Store: yes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iOS / iPadOS&lt;/td&gt;
&lt;td&gt;App Sandbox + entitlements&lt;/td&gt;
&lt;td&gt;2007&lt;/td&gt;
&lt;td&gt;TrustedBSD MAC framework&lt;/td&gt;
&lt;td&gt;Inline kernel check&lt;/td&gt;
&lt;td&gt;No (Pegasus class proves)&lt;/td&gt;
&lt;td&gt;Yes, mandatory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Android&lt;/td&gt;
&lt;td&gt;UID + SELinux + seccomp&lt;/td&gt;
&lt;td&gt;2008&lt;/td&gt;
&lt;td&gt;UID space (kernel) + MAC&lt;/td&gt;
&lt;td&gt;Four checks per syscall&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes, mandatory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux&lt;/td&gt;
&lt;td&gt;Namespaces + seccomp + Flatpak / Snap&lt;/td&gt;
&lt;td&gt;2014 (Flatpak)&lt;/td&gt;
&lt;td&gt;Composable user-mode + kernel&lt;/td&gt;
&lt;td&gt;Variable per layer&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Opt-in per app&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

The sandbox is not a security silver bullet, but it is a strong last defense against nasty exploits. -- Chromium sandbox FAQ [@chromium-sandbox-faq]
&lt;p&gt;The Chromium project itself ports its broker / target sandbox across all four families. On Windows it sits &lt;em&gt;on top of&lt;/em&gt; AppContainer and LPAC. On macOS it uses seatbelt. On Linux it uses seccomp filters plus unprivileged user namespaces. On ChromeOS the same code talks to ChromeOS&apos;s own kernel-side enforcement. The broker / target pattern is operating-system-independent; what differs is the per-OS capability vocabulary the broker negotiates with [@chromium-sandbox].&lt;/p&gt;
&lt;p&gt;Putting the principal in the kernel access token is fast and elegant. The amortised cost of the LowBox path is one extra DACL pass plus the capability-claim check, all running inline in &lt;code&gt;SeAccessCheck&lt;/code&gt; with no IPC round-trip. But the kernel-side bet has a brittle edge: it is brittle exactly where putting it in the kernel is not enough. The next section asks what the LowBox token &lt;em&gt;cannot&lt;/em&gt; prove.&lt;/p&gt;
&lt;h2&gt;8. Theoretical Limits: What the LowBox Token Cannot Prove&lt;/h2&gt;
&lt;p&gt;Every security primitive has a perimeter beyond which it can no longer reason. The LowBox token has five.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Limit 1: Kernel exploits.&lt;/strong&gt; Once the attacker is in kernel mode, they rewrite their own token. The LowBox bit becomes irrelevant. The Chromium sandbox FAQ states the principle plainly: &quot;the sandbox cannot provide any protection against bugs in system components such as the kernel it is running on&quot; [@chromium-sandbox-faq]. This is the load-bearing argument for Windows Sandbox&apos;s Hyper-V wrap (§4.7, §6.4). It is the third insight to keep.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The LowBox token is a runtime sandbox, not a privilege ceiling. To the rest of user mode a LowBox process is opaque and bounded; to the kernel it is a small extension of the access-check pipeline. A kernel-mode arbitrary write -- of which Windows has had a steady but small supply across its history -- gives the attacker the ability to set or clear &lt;code&gt;_TOKEN.Flags.LowBoxToken&lt;/code&gt;, swap out &lt;code&gt;PackageSid&lt;/code&gt;, append entries to &lt;code&gt;Capabilities[]&lt;/code&gt;, or simply impersonate a different token. The structural answer is hypervisor isolation, not better in-token checks. Windows Sandbox is the production implementation [@ms-learn-sandbox-overview].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Limit 2: Confused-deputy brokers.&lt;/strong&gt; A broker process outside the AppContainer is, by design, the bridge back to user-scoped resources. If the broker honours requests it should not have honoured, the AppContainer&apos;s caller succeeds in doing something it could not have done directly. The canonical class on Windows is RuntimeBroker, AppInfo (the UAC consent broker), Print, WebAuthn, voice activation, and the &lt;em&gt;Microsoft (R) Diagnostics Hub Standard Collector Service&lt;/em&gt; that Forshaw called &quot;DiagHub for short&quot; in his 2018 arbitrary-file-write writeup [@p0-arbitrary-write-2018]. The 1988 &lt;em&gt;Confused Deputy&lt;/em&gt; paper from the capability-systems literature [@hardy-confused-deputy] is the literature anchor for this whole class.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Limit 3: Token-stealing.&lt;/strong&gt; An attacker who can duplicate a higher-privilege token via &lt;code&gt;NtDuplicateToken&lt;/code&gt;, &lt;code&gt;NtOpenProcessTokenEx&lt;/code&gt;, or any of the SeImpersonatePrivilege-stealing Potato-family attacks steps out of the AppContainer by impersonating a non-LowBox user. &lt;em&gt;Siloscape&lt;/em&gt; is the most-publicised Windows container escape: Daniel Prizmant&apos;s Unit 42 writeup characterises it as &quot;the first known malware targeting Windows containers&quot;, and records Microsoft&apos;s initial position that &quot;Windows Server containers are not a security boundary&quot; before later reclassifying that &quot;an escape from a Windows container to the host, when executed without administrator permissions inside the container, will in fact be considered a vulnerability&quot; [@siloscape]. Token-stealing is the same class one rung down: an AppContainer escape via impersonation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Limit 4: Capability granularity.&lt;/strong&gt; &lt;code&gt;internetClient&lt;/code&gt; is &quot;the application can speak to the public internet&quot; with no per-host granularity. Each Windows capability is approximately as fine-grained as a Linux &lt;code&gt;CAP_*&lt;/code&gt;, not as fine-grained as an iOS entitlement key. There is no AppContainer-native equivalent of &quot;this app can connect to api.example.com:443 but nowhere else.&quot; The Microsoft Learn capabilities page enumerates the capability vocabulary [@ms-learn-capabilities]; the cardinality is in the dozens of standard capabilities plus custom ones, not in the millions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Limit 5: In-process side channels.&lt;/strong&gt; Once data is in the AppContainer process&apos;s address space, the AppContainer model has nothing to say about it. Hardware side channels (Spectre, MDS, Downfall), explicit channels (clipboard, WebRTC, browser-tab side channels in Edge), and out-of-band exfiltration via broker calls are all out of model. AppContainer is a &lt;em&gt;boundary&lt;/em&gt; primitive; once you are inside the boundary, the primitive does not constrain in-process behaviour.&lt;/p&gt;

The Windows Sandbox architecture page is explicit about the chain of reasoning [@ms-learn-sandbox-arch]. The host&apos;s read-only OS files can be shared with the sandbox without compromise (immutable; &quot;Most OS files are immutable and can be freely shared&quot;). The host&apos;s writable state cannot be shared (mutable; &quot;A small subset of operating system files are mutable and can&apos;t be shared&quot;). The remaining isolation question -- can a kernel exploit inside the guest reach the host? -- is answered structurally: the guest runs on a separate Microsoft hypervisor partition with a separate kernel. A guest-side kernel exploit gives the attacker control of the guest&apos;s `_TOKEN.Flags.LowBoxToken` field, but the host&apos;s kernel is untouched on the other side of the partition.
&lt;p&gt;These five limits define the research surface post-2024. The next section catalogues what is still open.&lt;/p&gt;
&lt;h2&gt;9. Open Problems&lt;/h2&gt;
&lt;p&gt;Six open problem classes are actively investigated in 2024-2026 research.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The &lt;code&gt;ALL_APP_PACKAGES&lt;/code&gt; ambient-access surface.&lt;/strong&gt; Even with LPAC, many default-installed resources are ACL&apos;d to the &lt;code&gt;ALL_APP_PACKAGES&lt;/code&gt; group; non-LPAC AppContainers inherit access to all of them. The research class is to enumerate the union of all &lt;code&gt;ALL_APP_PACKAGES&lt;/code&gt;-readable surfaces on a default Windows install and classify which leak cross-package data.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Capability-SID collisions and over-grant.&lt;/strong&gt; Third-party custom capability SIDs are derived from a string in the manifest; the collision resistance of the hash construction under adversarial choice of input string is an open question. A malicious manifest that produces a capability SID colliding with a privileged Microsoft-defined capability would be a structural break.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;LPAC capability-injection at process creation.&lt;/strong&gt; The capability list is supplied by the &lt;em&gt;creator&lt;/em&gt; of the AppContainer process; tightening which caller can ask for which capability has been a recurring servicing-bulletin theme. Forshaw&apos;s &lt;em&gt;Raising the Dead&lt;/em&gt; writeup (P0 Issue 483, fixed in MS15-111 as CVE-2015-2554 [@p0-raising-dead] [@nvd-cve-2015-2554]) and the 2018 &lt;em&gt;Arbitrary Object Directory Creation&lt;/em&gt; writeup (P0 Issue 1550 [@p0-1550]) are the canonical examples of bugs in this class.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hyper-V-per-AppContainer (&quot;HVCI for AppContainers&quot;).&lt;/strong&gt; WDAG&apos;s 2024 retirement [@ms-learn-mdag] leaves a gap: there is no production direction for per-application hypervisor isolation. Windows Sandbox is the surviving Hyper-V-wrapped sandbox; per-tab or per-app HV isolation in Edge is not coming back. The research question is whether per-app Hyper-V can be reintroduced in a form that does not have WDAG&apos;s adoption problems.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cross-package AppContainer-to-AppContainer attacks.&lt;/strong&gt; As third-party MSIX adoption grows, the cross-package attack surface (one third-party package abusing another&apos;s &lt;code&gt;\AppContainerNamedObjects\&amp;lt;other-package-sid&amp;gt;\&lt;/code&gt; ACL) grows. The 2018 P0 1550 writeup [@p0-1550] is the canonical example of a kernel-side variant; user-mode variants in third-party MSIX brokers are the next class.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Post-CrowdStrike user-mode EDR runtime.&lt;/strong&gt; After the July 2024 incident, Microsoft convened a multi-vendor endpoint-security summit and committed to a more isolated EDR / AV runtime in user mode. AppContainer or LPAC is one of the candidate runtimes for the new platform; architectural details are still being published.For Problem 1 specifically, the standard tool for enumerating the &lt;code&gt;ALL_APP_PACKAGES&lt;/code&gt;-readable surface is Forshaw&apos;s &lt;code&gt;NtObjectManager&lt;/code&gt; PowerShell module&apos;s &lt;code&gt;Get-AccessibleObject -ProcessId &amp;lt;pid&amp;gt;&lt;/code&gt; cmdlet -- it enumerates every kernel object a given process can reach, which is the right primitive for asking &quot;what does an &lt;code&gt;ALL_APP_PACKAGES&lt;/code&gt; member actually see?&quot; The companion Microsoft tool is &lt;code&gt;LaunchAppContainer&lt;/code&gt; from the official &lt;code&gt;SandboxSecurityTools&lt;/code&gt; repository on GitHub [@sandboxsecuritytools].&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Microsoft&apos;s decision to retire WDAG in Windows 11 24H2 [@ms-learn-mdag] is the proximate signal that per-application hypervisor isolation is &lt;em&gt;not&lt;/em&gt; the Microsoft direction. The 2024+ answer is &quot;harden the in-token AppContainer surface&quot; and &quot;reduce the broker attack surface&quot; rather than &quot;wrap every app in Hyper-V.&quot; The single shipping Hyper-V-wrapped sandbox is Windows Sandbox, and it is a user-launched primitive, not an OS-default for browsers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The unresolved problems share a structural property: they sit &lt;em&gt;between&lt;/em&gt; the token-level guarantee AppContainer makes and the resource managers that implement the consequences. The next section gives practical guidance for developers and practitioners working within these limits today.&lt;/p&gt;
&lt;h2&gt;10. Practical Guide&lt;/h2&gt;
&lt;p&gt;Two audiences need different answers. Developers need to know how to opt their code in. Practitioners need to know how to audit the deployment.&lt;/p&gt;
&lt;h3&gt;For developers&lt;/h3&gt;
&lt;p&gt;Package your Win32 app with MSIX and set &lt;code&gt;uap10:TrustLevel=&quot;appContainer&quot;&lt;/code&gt; in the manifest&apos;s &lt;code&gt;&amp;lt;Application&amp;gt;&lt;/code&gt; element [@ms-learn-manifest-app]. Stop adding &lt;code&gt;runFullTrust&lt;/code&gt; as a restricted capability by default; the manifest is a &lt;em&gt;budget&lt;/em&gt;, and every restricted capability is a lifetime ACL on your package SID [@ms-learn-capabilities] [@ms-learn-msix-container].&lt;/p&gt;
&lt;p&gt;Declare &lt;em&gt;only&lt;/em&gt; the capabilities your app needs in &lt;code&gt;&amp;lt;Capabilities&amp;gt;&lt;/code&gt;. Microsoft Learn&apos;s wording is exactly this: &quot;be sure to declare only the capabilities that your app needs&quot; [@ms-learn-capabilities]. Each extra &lt;code&gt;internetClient&lt;/code&gt; or &lt;code&gt;picturesLibrary&lt;/code&gt; is a lifetime allow ACE on the corresponding Capability SID.&lt;/p&gt;
&lt;p&gt;Use &lt;strong&gt;LPAC&lt;/strong&gt; for any process that does not need cross-package or cross-user resource access. The mechanism is the synthetic &lt;code&gt;WIN://NOALLAPPPKG&lt;/code&gt; capability in &lt;code&gt;SECURITY_CAPABILITIES.Capabilities[]&lt;/code&gt; at process creation [@ms-learn-implementing] [@chromium-lpac]. The Chromium tree&apos;s &lt;code&gt;lpac_capability.h&lt;/code&gt; is the canonical reference for which LPAC capabilities each kind of worker process needs [@chromium-lpac].&lt;/p&gt;
&lt;p&gt;Test your sandbox with Forshaw&apos;s &lt;code&gt;NtObjectManager&lt;/code&gt; PowerShell module&apos;s &lt;code&gt;Get-AccessibleObject -ProcessId &amp;lt;pid&amp;gt;&lt;/code&gt; cmdlet, which enumerates every kernel object your process can actually reach [@sandboxsecuritytools]. Tighten capabilities until the enumerated list matches your expectations. Microsoft&apos;s own &lt;code&gt;SandboxSecurityTools&lt;/code&gt; repository ships &lt;code&gt;LaunchAppContainer&lt;/code&gt; for the same kind of testing in the Insider bounty programme [@sandboxsecuritytools].&lt;/p&gt;
&lt;h3&gt;For practitioners&lt;/h3&gt;
&lt;p&gt;Identify which high-risk applications on your managed fleet can be MSIX-packaged and migrated to &lt;code&gt;uap10:TrustLevel=&quot;appContainer&quot;&lt;/code&gt;. Browsers, PDF viewers, and email clients are highest payoff because hostile content reaches them most often.&lt;/p&gt;
&lt;p&gt;Audit the per-package directory permissions in &lt;code&gt;%LOCALAPPDATA%\Packages\&amp;lt;PFN&amp;gt;\&lt;/code&gt;. The &lt;code&gt;LocalState&lt;/code&gt;, &lt;code&gt;RoamingState&lt;/code&gt;, and &lt;code&gt;AC&lt;/code&gt; subdirectories are the load-bearing entries; each should grant access to the package SID, not to broad groups.&lt;/p&gt;
&lt;p&gt;Use Windows Sandbox (&lt;code&gt;WindowsSandbox.exe&lt;/code&gt;) for Hyper-V-isolated execution of untrusted programs on Pro, Enterprise, or Education editions [@ms-learn-sandbox-overview]. Configurations are described in a &lt;code&gt;.wsb&lt;/code&gt; file: a 30 MB Dynamic Base Image launches in a few seconds, and Windows 11 22H2 onward preserves data through in-sandbox restarts.&lt;/p&gt;
&lt;p&gt;Monitor the &lt;code&gt;Microsoft-Windows-AppxPackaging/Operational&lt;/code&gt; and &lt;code&gt;Microsoft-Windows-AppXDeploymentServer/Operational&lt;/code&gt; event logs for unexpected &lt;code&gt;CreateAppContainerToken&lt;/code&gt; failures and capability mismatches. Microsoft Learn&apos;s AppX troubleshooting page names these two channels verbatim and walks the diagnostic surface they expose [@ms-learn-appx-troubleshooting].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Five commands a defender can run today to inventory AppContainer adoption on a managed endpoint: 1. &lt;code&gt;Get-AppxPackage -AllUsers | Select-Object Name, PackageFullName, PackageFamilyName&lt;/code&gt; -- enumerate installed MSIX packages. 2. &lt;code&gt;(Get-AppxPackage -Name Microsoft.MicrosoftEdge.Stable).Manifest&lt;/code&gt; -- inspect a specific package&apos;s manifest. 3. &lt;code&gt;Get-Process | Where-Object &amp;amp;#123; $_.Path -like &quot;*WindowsApps*&quot; &amp;amp;#125; | Select-Object Id, ProcessName, Path&lt;/code&gt; -- find running UWP / packaged processes. 4. From an elevated PowerShell with &lt;code&gt;NtObjectManager&lt;/code&gt; installed: &lt;code&gt;Get-NtToken -ProcessId &amp;lt;pid&amp;gt;&lt;/code&gt; to see the &lt;code&gt;LowBoxToken&lt;/code&gt; flag, Package SID, and capability list. 5. &lt;code&gt;Get-AppxLog | Where-Object &amp;amp;#123; $_.LevelDisplayName -eq &quot;Error&quot; &amp;amp;#125;&lt;/code&gt; -- review AppX deployment errors for clues to broken AppContainer launches.&lt;/p&gt;
&lt;/blockquote&gt;

Once `NtObjectManager` is installed (from PowerShell Gallery), a defender can dump the *complete* reachable kernel-object set for a running LowBox process:&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;Install-Module NtObjectManager -Scope CurrentUser
$pid = (Get-Process -Name calc).Id
Get-AccessibleObject -ProcessId $pid -TypeFilter Mutant,Section,Event,Directory |
  Sort-Object Path | Format-Table -Property TypeName, Path, GrantedAccess
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output enumerates every named kernel object the LowBox token can open, with the access mask the kernel would grant on each request. Any path that resolves outside &lt;code&gt;\Sessions\&amp;lt;n&amp;gt;\AppContainerNamedObjects\&amp;lt;package-sid&amp;gt;\&lt;/code&gt; is a candidate for cross-package leakage or an over-permissive capability. This is the same surface §9 problem 1 catalogues [@sandboxsecuritytools].
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;

Converting a `mediumIL` packaged Win32 app to `appContainer` is structurally easier than it sounds and operationally harder than it sounds. Structurally, the manifest change is one line: drop `` and set `uap10:TrustLevel=&quot;appContainer&quot;` [@ms-learn-msix-container]. Operationally, you must remove every HKLM write the app does, every assumption that absolute file paths outside the per-package directory will succeed, and every dependency on registering out-of-process COM. Legacy desktop apps tend to violate all three in plumbing that nobody documented. This is why `runFullTrust` is so widespread in third-party MSIX packages despite Microsoft&apos;s clear guidance to stop adding it.
&lt;p&gt;The practical guidance only matters if the deployment is correctly understood. The next section addresses the most common misconceptions.&lt;/p&gt;
&lt;h2&gt;11. Frequently Asked Questions&lt;/h2&gt;

No. AppContainer and integrity level are orthogonal axes. AppContainer is a per-application principal in the access token; integrity level is a mandatory label. Microsoft Learn states the relationship plainly: &quot;if you _are_ in an app container, then the integrity level (IL) is always _low_&quot; -- but the converse is *not* true. Plenty of Low-IL tokens are not AppContainer tokens (the IE 7 Protected Mode renderer was the historical example; the LocalLow workspace and the Low-IL Chromium target are modern examples) [@ms-learn-legacy].

Because the LowBox process&apos;s Object Manager namespace is partitioned at `\Sessions\\AppContainerNamedObjects\\`, and the WinRT API the UWP app uses for `Windows.Storage.TempFolder` resolves a per-package temporary directory under `%LOCALAPPDATA%\Packages\\AC\Temp\` instead of the global `\BaseNamedObjects`-anchored `C:\Windows\Temp`. The kernel handles for the rewrite live in the `LowboxHandlesEntry` AVL-tree slot keyed by your process&apos;s `AppContainerNumber`.

*LowBox* is the kernel-internal name preserved in `NtCreateLowBoxToken` and the `_TOKEN.Flags.LowBoxToken` bit [@ms-learn-legacy] [@ms-learn-ntcreatelowboxtoken]. *AppContainer* is the public Win32 / WinRT API name for the same kernel object. *LPAC* (*Less Privileged AppContainer*) is the variant where the synthetic `WIN://NOALLAPPPKG` capability strips the implicit `ALL_APP_PACKAGES` group SID from the new token, requiring the package to declare each access (`registryRead`, `lpacCom`, and others) explicitly [@ms-learn-implementing].

Because the Package SID is a deterministic hash of the Package Family Name (which is itself a hash of the publisher distinguished name plus the package moniker). Each of the eight 32-bit sub-authorities is a 4-byte slice of the underlying SHA-256 hash (8 × 4 bytes = 32 bytes = 256 bits). The point of the construction is that every Windows 8 or later machine derives the *same* Package SID for the same Package Family Name, so per-package directory DACLs can be ACL&apos;d at install time to a known SID [@ms-learn-derivesid] [@nostarch-wsi].

No. The kernel&apos;s `AppContainerNumber` per-instance counter keys each instance into its own slot in `nt!SepLowBoxHandlesTable`, and the Object Manager rewrites `\BaseNamedObjects` references against the per-instance saved-handle directory before name lookup [@nostarch-wsi]. Two Calculator processes share a Package SID and a Capabilities array but have *different* AppContainerNumbers; the named objects each one creates land in different directories.

Yes -- unless the AppContainer has `WIN://NOALLAPPPKG`, in which case it is LPAC and `ALL_APP_PACKAGES` is omitted from its token [@ms-learn-implementing] [@chromium-lpac]. The `ALL_APP_PACKAGES` ambient-access surface is the §9 problem-1 research class. The standard tool for enumerating what an `ALL_APP_PACKAGES` member can reach on a given system is the `Get-AccessibleObject` cmdlet in Forshaw&apos;s `NtObjectManager` PowerShell module, and Microsoft publishes its own `LaunchAppContainer` testing tool in `SandboxSecurityTools` [@sandboxsecuritytools].

No. Windows Sandbox combines a *separate* Hyper-V partition running a pristine Windows kernel from the Dynamic Base Image [@ms-learn-sandbox-arch] with -- according to security-research reverse-engineering write-ups -- an AppContainer wrap on the host-side container manager. The Hyper-V layer is the only documented structural defence against kernel exploits inside the sandbox; pure AppContainer cannot defend against a kernel exploit. Inside the sandbox you still see the LowBox plumbing (it is a Windows guest, after all), but the meaningful trust boundary is the hypervisor partition.
&lt;h2&gt;12. Where This Plane Connects to the Others&lt;/h2&gt;
&lt;p&gt;AppContainer is the &lt;em&gt;principal layer&lt;/em&gt; of the modern Windows sandbox stack. It is not the whole stack. A correct modern Windows app launch crosses four single-purpose layers in the kernel and in the deployment pipeline, and each layer carries a separate guarantee.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Code identity.&lt;/strong&gt; Who is this code? &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; signatures, kernel-mode code signing, the MSIX signing pipeline, and the Package Family Name derivation answer that question [@ms-learn-derivesid].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AppContainer / LowBox token.&lt;/strong&gt; What principal is the running instance? &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt; sets the LowBox flag, stamps the Package SID, and populates &lt;code&gt;Capabilities[]&lt;/code&gt; [@ms-learn-ntcreatelowboxtoken].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Object Manager namespace partition.&lt;/strong&gt; What names can the principal resolve? &lt;code&gt;LowboxHandlesEntry&lt;/code&gt; directs the path-walker to the per-package &lt;code&gt;\AppContainerNamedObjects\&amp;lt;package-sid&amp;gt;\&lt;/code&gt; directory [@nostarch-wsi].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://paragmali.com/blog/process-mitigation-policies-cfg-acg-cig-and-the-layer-betwee/&quot; rel=&quot;noopener&quot;&gt;Process mitigation policies&lt;/a&gt;.&lt;/strong&gt; What is the principal allowed to do inside its own address space? Arbitrary Code Guard, Code Integrity Guard, Control Flow Guard, Extended Flow Guard, Hardware-enforced Stack Protection (CET), and ImageLoadPolicy are the in-process mitigations Windows applies on top of the per-app principal, exposed via the &lt;code&gt;PROCESS_MITIGATION_POLICY&lt;/code&gt; enumeration that &lt;code&gt;SetProcessMitigationPolicy&lt;/code&gt; accepts [@ms-learn-process-mitigation-policy].&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Walk a modern Windows app boot. Authenticode and the MSIX signing pipeline verify the binaries. The MSIX deployment stack derives the Package SID from the Package Family Name and writes the per-package profile under &lt;code&gt;%LOCALAPPDATA%\Packages\&amp;lt;PFN&amp;gt;\&lt;/code&gt;. The launcher calls &lt;code&gt;CreateProcess&lt;/code&gt; with a &lt;code&gt;SECURITY_CAPABILITIES&lt;/code&gt; payload, which calls &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt; in the kernel. &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt; sets the LowBox flag and the per-instance fields. From that point every later &lt;code&gt;SeAccessCheck&lt;/code&gt; against any object the process opens consults the new token&apos;s per-app fields alongside the classic user / groups walk. The Object Manager namespace gets partitioned in the kernel&apos;s hash table by the &lt;code&gt;AppContainerNumber&lt;/code&gt;. The in-process mitigation policies decide what code may execute and which DLLs may load.&lt;/p&gt;

flowchart LR
    A[Authenticode / KMCS&lt;br /&gt;code identity] --&amp;gt; B[NtCreateLowBoxToken&lt;br /&gt;per-app principal]
    B --&amp;gt; C[Object Manager&lt;br /&gt;namespace partition]
    C --&amp;gt; D[Mitigation policies&lt;br /&gt;ACG / CIG / CFG / XFG / CET]
    D --&amp;gt; E[Running LowBox process]
&lt;p&gt;Each layer is single-purpose. Together they cover the four-axis system the classic NT token model could not.&lt;/p&gt;

The LowBox bit is the answer to the question Windows NT 3.1 couldn&apos;t ask. Everything else is bookkeeping.
&lt;p&gt;Two calculators on the same Windows 11 machine give the kernel a single question with two verdicts. The bit in &lt;code&gt;_TOKEN.Flags.LowBoxToken&lt;/code&gt; is the one-line answer Windows took fourteen years to ship -- and the work Microsoft did in the years between January 30, 2007 and October 26, 2012 is what made it possible to ask the question at all.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;appcontainer-and-lowbox-tokens-windowss-capability-sandbox&quot; keyTerms={[
  { term: &quot;LowBox token&quot;, definition: &quot;An access token whose _TOKEN.Flags.LowBoxToken bit is set, marking the process as an AppContainer process whose access checks consult the package SID, capability SIDs, and per-instance namespace fields.&quot; },
  { term: &quot;Package SID&quot;, definition: &quot;A deterministic SHA-2-derived SID with prefix S-1-15-2-* that names a specific MSIX/UWP package.&quot; },
  { term: &quot;Capability SID&quot;, definition: &quot;A SID with prefix S-1-15-3-* representing a per-package permission declared in the application manifest.&quot; },
  { term: &quot;AppContainerNumber&quot;, definition: &quot;A per-instance integer assigned by NtCreateLowBoxToken that keys per-instance state in the kernel&apos;s session-scoped AVL trees.&quot; },
  { term: &quot;LPAC&quot;, definition: &quot;Less Privileged AppContainer -- a LowBox token whose WIN://NOALLAPPPKG capability strips the ALL_APP_PACKAGES group SID for stricter deny-by-default.&quot; },
  { term: &quot;Mandatory Integrity Control&quot;, definition: &quot;Vista-era mechanism adding an integrity level (Low/Medium/High/System) to every process and securable object; AppContainer tokens are always Low IL.&quot; },
  { term: &quot;Dynamic Base Image&quot;, definition: &quot;Windows Sandbox&apos;s read-only file set, stored as a 30 MB compressed package and unpacked to 500 MB, that a fresh Hyper-V partition boots from.&quot; }
]} questions={[
  { q: &quot;Which kernel data-structure field distinguishes a UWP calc.exe from a legacy win32calc.exe even when every classic NT token field is identical?&quot;, a: &quot;_TOKEN.Flags.LowBoxToken, the bit set by NtCreateLowBoxToken and consulted by every AppContainer-aware access-check path.&quot; },
  { q: &quot;Why are two co-tenanted instances of the same package different principals to the kernel?&quot;, a: &quot;Because NtCreateLowBoxToken assigns a fresh AppContainerNumber per instance and the Object Manager keys the per-instance namespace handles on that number.&quot; },
  { q: &quot;Name the synthetic capability that turns an AppContainer into an LPAC.&quot;, a: &quot;WIN://NOALLAPPPKG -- its presence in Capabilities[] causes NtCreateLowBoxToken to omit the ALL_APP_PACKAGES group SID from the new token.&quot; },
  { q: &quot;What is the only structural defence against kernel exploits inside an AppContainer?&quot;, a: &quot;Hypervisor isolation. Windows Sandbox wraps an AppContainer-managed guest in a separate Hyper-V partition with its own kernel.&quot; },
  { q: &quot;Cite the Microsoft Learn intersection rule for AppContainer access checks.&quot;, a: &quot;&apos;the permitted access is the intersection of that granted by the user/group SIDs and AppContainer SIDs&apos; -- the dual-principal rule that runs on every LowBox access decision.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>windows-security</category><category>appcontainer</category><category>lowbox-token</category><category>sandbox</category><category>capability-security</category><category>msix</category><category>uwp</category><category>windows-sandbox</category><author>noreply@paragmali.com (Parag Mali)</author></item><item><title>The Object Manager Namespace: The Hierarchical Filesystem Underneath Every Windows Security Boundary</title><link>https://paragmali.com/blog/the-object-manager-namespace/</link><guid isPermaLink="true">https://paragmali.com/blog/the-object-manager-namespace/</guid><description>A bottom-up tour of the Windows Object Manager namespace, the 1993 Cutler-era kernel data structure that every Windows security boundary quietly assumes.</description><pubDate>Mon, 11 May 2026 00:00:00 GMT</pubDate><content:encoded>
**The Windows Object Manager namespace is the kernel-resident, filesystem-shaped tree that every Windows security boundary quietly assumes.** Every named kernel object -- processes, threads, sections, files, registry keys, tokens, mutants, semaphores, ALPC ports, devices, drivers, jobs, silos -- lives somewhere under `\`. Six generations of isolation primitives (Session 0 isolation, AppContainer lowbox, integrity levels, VBS trustlets, Server Silos, and the `ObRegisterCallbacks` EDR sensor surface) are all path rewrites, per-directory ACLs, or kernel callbacks layered on the same 1993 Cutler-era four-piece structure. This article builds the namespace bottom-up -- `OBJECT_HEADER`, `OBJECT_TYPE`, `ParseProcedure`, `OBJECT_DIRECTORY` -- walks the 2026 top-level directory atlas on Windows 11 25H2, surveys the exploit tradition (symbolic-link redirection, namespace squatting, bait-and-switch on `\??` and `\Device`, arbitrary directory creation), and closes on the EDR pivot in `ObRegisterCallbacks`.
&lt;h2&gt;1. The path that isn&apos;t a path&lt;/h2&gt;
&lt;p&gt;Open &lt;code&gt;WinObj.exe&lt;/code&gt; as administrator on any Windows 11 25H2 machine (&lt;a href=&quot;https://en.wikipedia.org/wiki/Windows_11_version_history&quot; rel=&quot;noopener&quot;&gt;Windows 11 version history&lt;/a&gt;). For about ten seconds the screen looks like a filesystem. The root is named &lt;code&gt;\&lt;/code&gt;. Below it sit folders called &lt;code&gt;\Device&lt;/code&gt;, &lt;code&gt;\BaseNamedObjects&lt;/code&gt;, &lt;code&gt;\Sessions&lt;/code&gt;, &lt;code&gt;\RPC Control&lt;/code&gt;, &lt;code&gt;\KnownDlls&lt;/code&gt;, and &lt;code&gt;\ObjectTypes&lt;/code&gt;. Double-click any of them and you see children. Right-click any node and you can read a security descriptor. This is essentially the same UI a 1996 SysAdmin would have recognised; the tool first shipped that year as part of Mark Russinovich and Bryce Cogswell&apos;s Winternals [@en-wikipedia-mark-russinovich], and the current build is a Microsoft-signed Sysinternals binary whose navigation surface has not been redesigned in three decades [@ms-winobj].&lt;/p&gt;
&lt;p&gt;Navigate to &lt;code&gt;\Sessions\1\AppContainerNamedObjects&lt;/code&gt; and the picture starts to fracture. Inside that directory you will find one subdirectory per running AppContainer-sandboxed app, each named after a long Security Identifier of the form &lt;code&gt;S-1-15-2-...&lt;/code&gt;. Pick the one belonging to the Microsoft Edge renderer process you are reading this article in. Every named mutant, event, section, semaphore, and ALPC port the renderer can ever name lives inside that one subdirectory. The renderer cannot escape it. Not because of a permission check that comes second, but because the kernel rewrites every name the renderer asks for, transparently, before path resolution begins. Microsoft&apos;s AppContainer Isolation documentation [@ms-appcontainer-isolation] calls this &quot;sandboxing the application kernel objects.&quot;&lt;/p&gt;
&lt;p&gt;This tree is not a filesystem. There is no disk persistence; nothing under &lt;code&gt;\&lt;/code&gt; survives a reboot. It is not the Windows registry either; the registry is a separate subsystem with its own hive format that hangs off the namespace only through a parse procedure on the &lt;code&gt;Key&lt;/code&gt; object type. What this tree is, instead, is the Object Manager namespace: the in-memory, kernel-resident, hierarchical name service that the Windows kernel uses to locate every nameable kernel object [@ms-managing-kernel-objects]. Its top-level directories are catalogued in the driver kit&apos;s Object Directories reference [@ms-object-directories].&lt;/p&gt;

The Windows Object Manager, internally called `Ob`, is a kernel-mode subsystem of the Windows Executive that manages the lifetime, naming, security, and accounting of every resource the kernel exposes to user mode as a named object. Wikipedia summarises it as a &quot;subsystem implemented as part of the Windows Executive which manages Windows resources... each [resource] reside[s] in a namespace for categorization&quot; [@en-wikipedia-object-manager].
&lt;p&gt;Here is the thesis the rest of this article spends nine thousand words unpacking. Every Windows security boundary you have read about -- Session 0 isolation, Mandatory Integrity Control, AppContainer, the Virtualization-Based Security trustlets, Server Silos and Windows containers, the EDR sensor surface that fires when something opens a handle to &lt;code&gt;lsass.exe&lt;/code&gt; -- is &lt;em&gt;physically realised&lt;/em&gt; in this tree. Each boundary is either a path rewrite at lookup time, a per-directory ACL, a token-keyed name substitution, or a kernel callback registered against an &lt;code&gt;OBJECT_TYPE&lt;/code&gt;. The boundaries you read about elsewhere are the &lt;em&gt;policies&lt;/em&gt;; this tree is the &lt;em&gt;mechanism&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The Object Manager has shipped without architectural change for thirty-three years. Whose decision was that? And why did a 1993 data structure survive untouched while the GUI, the driver model, the security subsystem, and the boot path around it were rewritten more than once?&lt;/p&gt;
&lt;h2&gt;2. Where the namespace came from&lt;/h2&gt;
&lt;p&gt;The decision belongs to Dave Cutler. In 1988 Microsoft hired Cutler away from Digital Equipment Corporation. The Wikipedia biography records the line of operating systems Cutler had developed at DEC: &quot;RSX-11M, VAXELN, VMS, and MICA&quot; [@en-wikipedia-dave-cutler]. Three of those shipped commercially; the fourth, MICA, was cancelled with the Prism RISC program. Cutler walked out, and Microsoft signed him with a charter from Bill Gates to build a portable next-generation kernel that could host the existing Windows API on top of a 32-bit, multi-architecture base [@en-wikipedia-architecture-of-windows-nt]. Cutler brought a small team of DEC veterans with him.&lt;/p&gt;
&lt;p&gt;The Object Manager is one of that team&apos;s earliest design decisions. The architectural bet was to &lt;em&gt;unify every named kernel object&lt;/em&gt; under one filesystem-shaped tree, with each type carrying a parse procedure so a single family of syscalls (&lt;code&gt;NtCreateFile&lt;/code&gt;, &lt;code&gt;NtOpenSection&lt;/code&gt;, &lt;code&gt;NtOpenProcess&lt;/code&gt;, and so on) could address files, registry keys, processes, ports, sections, drivers, devices, jobs, and synchronization primitives using the same path-walk algorithm. That was an unusual choice in 1989. VMS had a more typed, less unified resource broker. Mach treated kernel objects as capability-style port rights and never gave them a hierarchical name. Cutler&apos;s choice was, at heart, a Plan-9-style &quot;every named resource is a filesystem path&quot; idea, imported into a Windows shell.Plan 9 from Bell Labs (Pike, Thompson, et al.) was the academic articulation of the &quot;everything is a path&quot; property: every kernel-named resource, including processes and network connections, surfaced as a file under a 9P-served namespace. Plan 9 never reached commercial scale, but its design idea reached production through NT, and through Linux&apos;s /proc, /sys, and FUSE.&lt;/p&gt;
&lt;p&gt;Windows NT 3.1 shipped on July 27, 1993. It was &quot;Microsoft&apos;s first 32-bit operating system,&quot; supported on IA-32, DEC Alpha, and MIPS [@en-wikipedia-windows-nt-3-1]. The Object Manager was already one of its executive subsystems, sitting alongside the I/O Manager, the Memory Manager, the Process Manager, the Security Reference Monitor, and the Local Procedure Call subsystem [@en-wikipedia-architecture-of-windows-nt]. The four pieces this article will rebuild from scratch -- the &lt;code&gt;OBJECT_HEADER&lt;/code&gt; that prefixes every object in memory, the &lt;code&gt;OBJECT_TYPE&lt;/code&gt; singleton that owns each type&apos;s method table, the &lt;code&gt;ParseProcedure&lt;/code&gt; that delegates path resolution to the owning subsystem, and the &lt;code&gt;OBJECT_DIRECTORY&lt;/code&gt; hash table that maps names to objects -- were all in the NT 3.1 kernel. None of them has been rearchitected since.&lt;/p&gt;
&lt;p&gt;That same year, Microsoft Press published &lt;em&gt;Inside Windows NT&lt;/em&gt;, written by technical writer Helen Custer with a Foreword by Cutler himself. The book&apos;s Object Manager chapter is the canonical pre-2000 description of the namespace, cited on the Sysinternals WinObj page [@ms-winobj] as &quot;Helen Custer&apos;s &lt;em&gt;Inside Windows NT&lt;/em&gt; provides a good overview of the Object Manager namespace.&quot; Custer&apos;s book has been out of print for two decades, but the citation chain through Russinovich&apos;s tool is durable.&lt;/p&gt;
&lt;p&gt;Three years later, in 1996, Russinovich and Cogswell co-founded Winternals and released WinObj 1.0 [@en-wikipedia-mark-russinovich]. WinObj was the first publicly distributed tool to walk &lt;code&gt;\&lt;/code&gt; from user mode, using the native &lt;code&gt;NtOpenDirectoryObject&lt;/code&gt; and &lt;code&gt;NtQueryDirectoryObject&lt;/code&gt; syscalls that the Object Manager exposed through NTDLL [@ms-winobj]. The following year, Russinovich&apos;s October 1997 &lt;em&gt;Windows IT Pro&lt;/em&gt; column &quot;Inside the Object Manager&quot; gave the namespace its first treatment in the trade press. The original URL did not survive changes to TechTarget&apos;s web property portfolio in 2025 (TechTarget was acquired by Informa PLC in 2025), but the WinObj page still cites the column by name as &quot;Mark&apos;s October 1997 [WindowsITPro Magazine] column, &apos;Inside the Object Manager&apos;.&quot;The Russinovich 1997 column has no surviving direct URL because the URL did not survive changes to TechTarget&apos;s web property portfolio in 2025. The most accessible surviving citation is through the WinObj page itself. The same archive failure also explains why Helen Custer&apos;s 1993 biography returns HTTP 404 on Wikipedia in 2026; the book (ISBN 1-55615-481-X) survives in used-book channels only.&lt;/p&gt;
&lt;p&gt;The line of book-length internals references that began with Custer continued through &lt;em&gt;Inside Windows 2000&lt;/em&gt; (third edition) and the &lt;em&gt;Windows Internals&lt;/em&gt; series that succeeded it. The 7th edition Part 1 was published by Microsoft Press in May 2017, authored by Russinovich, Alex Ionescu, and David A. Solomon [@microsoftpressstore-wininternals7-part1]; its Chapter 8 is the current canonical reference for the Object Manager. James Forshaw&apos;s April 2024 &lt;em&gt;Windows Security Internals&lt;/em&gt; [@nostarch-windows-security-internals] is the contemporary companion that ties the namespace into the access-check pipeline.&lt;/p&gt;
&lt;p&gt;The 1993 design assumed a single global namespace. One process tree, one &lt;code&gt;\BaseNamedObjects&lt;/code&gt;, one &lt;code&gt;\Windows\WindowStations\WinSta0&lt;/code&gt;, one &lt;code&gt;\??&lt;/code&gt; view of DOS device letters. Everyone shared everything. Did that assumption survive the Internet?&lt;/p&gt;
&lt;h2&gt;3. The pre-Vista namespace and how it broke&lt;/h2&gt;
&lt;p&gt;It did not. By the late 1990s every interactive Windows user was sharing a name service with every running service. The single-global-namespace assumption produced three distinct exploit classes, each rediscovered repeatedly between 1996 and 2007, and each ultimately closed only by architectural change.&lt;/p&gt;
&lt;p&gt;The most public failure was the &lt;em&gt;shatter attack&lt;/em&gt;. In August 2002 a researcher named Chris Paget published a paper titled &quot;Exploiting design flaws in the Win32 API for privilege escalation.&quot; Wikipedia&apos;s article on the disclosure preserves the chronology: &quot;Shatter attacks became a topic of intense conversation in the security community in August 2002 after the publication of Chris Paget&apos;s paper&quot; [@en-wikipedia-shatter-attack]. The proof-of-concept was about thirty lines. As an unprivileged interactive user, Paget sent a &lt;code&gt;WM_TIMER&lt;/code&gt; window message to a service&apos;s hidden window in the same &lt;code&gt;\Windows\WindowStations\WinSta0&lt;/code&gt; (which all services and all interactive users shared in pre-Vista Windows), with a callback parameter pointing to attacker-placed shellcode. The shellcode ran as SYSTEM.&lt;/p&gt;
&lt;p&gt;Microsoft&apos;s initial response, preserved in the Wikipedia article, was that &quot;the flaw lies in the specific, highly privileged service&quot;: a per-service bug, patch the services. That stance did not survive the structural-class argument. The exploit was not a bug in one service. It was a &lt;em&gt;property of the namespace&lt;/em&gt;: as long as services and users shared a window station and a &lt;code&gt;\BaseNamedObjects&lt;/code&gt;, any service that ever called a Windows API processing a message from its message queue was reachable from any logged-in user.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A second class of pre-Vista failure was &lt;em&gt;named-object squatting&lt;/em&gt;. A low-privilege user pre-creates &lt;code&gt;\BaseNamedObjects\Some_Global_Event&lt;/code&gt; with a permissive DACL. A privileged service later calls &lt;code&gt;CreateEvent(&quot;Some_Global_Event&quot;)&lt;/code&gt; with default open-or-create semantics and ends up inheriting the squatter&apos;s object, security descriptor and all. This is not one service-author&apos;s bug; it is the consequence of every service-author trusting that names in a shared namespace would resolve to objects they themselves created. The pattern has been rediscovered approximately once a year for two decades. James Forshaw documents the contemporary named-pipe analog in his 2017 &quot;Named Pipe Secure Prefixes&quot; post [@tiraniddo-named-pipe-secure-prefixes], where the SMSS-created prefixes &lt;code&gt;\Device\NamedPipe\ProtectedPrefix\Administrators&lt;/code&gt;, &lt;code&gt;\Device\NamedPipe\ProtectedPrefix\LocalService&lt;/code&gt;, and &lt;code&gt;\Device\NamedPipe\ProtectedPrefix\NetworkService&lt;/code&gt; are TCB-privilege-gated -- only &lt;code&gt;smss.exe&lt;/code&gt; can create sibling protected prefixes, so a service that publishes its pipe below one of these prefixes inherits a DACL that low-privilege squatters cannot reach.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The third class was &lt;em&gt;symbolic-link redirection&lt;/em&gt;. The pre-Vista Object Manager exposed two kinds of user-creatable symbolic link: object-manager symbolic links inside &lt;code&gt;\??&lt;/code&gt; (the per-session DOS-devices view) and NTFS mount points on disk. The attack pattern was the same in both. A privileged process is asked to open a path the user controls part of. The user has pre-planted a symbolic link partway through the path that redirects the residual walk into a target the user could not otherwise write. The privileged process opens the redirected file and treats it as if it were the original.&lt;/p&gt;
&lt;p&gt;Forshaw&apos;s 2015 Project Zero post on the symbolic-link hardening generation is the canonical taxonomy: &quot;There are three types of symbolic links you can access from a low privileged user, Object Manager Symbolic Links, Registry Key Symbolic Links and NTFS Mount Points&quot; [@p0-symlink-mitigations]. His worked example for the Internet Explorer 11 EPM sandbox is CVE-2015-0055 [@nvd-cve-2015-0055], described in the post as &quot;an information disclosure issue in the IE EPM sandbox which abused symbolic links to bypass a security check.&quot;&lt;/p&gt;
&lt;p&gt;The aha moment from this section is the one Microsoft eventually conceded. The pre-Vista failure mode was not three independent bug families. It was &lt;em&gt;one&lt;/em&gt; structural problem -- a single global namespace shared by every principal -- with three faces. No amount of per-service patching could close it. The fix had to be architectural: the namespace itself had to be partitioned.The Interactive Services Detection Service (ISDS) was Vista&apos;s backward-compatibility hack for legacy services that drew GUIs into Session 0. ISDS displayed a &quot;An interactive service has requested attention&quot; prompt that let the user switch to Session 0 long enough to dismiss the dialog. It was deprecated in Windows 10 1803 and is the historical artifact of just how much pre-Vista code assumed services and users would share a window station.&lt;/p&gt;
&lt;p&gt;That fix took five years to ship. Windows Vista RTM was released on November 8, 2006 and General Availability arrived on January 30, 2007 [@en-wikipedia-windows-vista]. Vista did not ship one fix; it shipped three independent partition mechanisms in the same release window, because the structural failure had three faces and each face needed its own mechanism. The next section catalogues those mechanisms and the four additional generations of additive isolation that have built on them since.&lt;/p&gt;
&lt;h2&gt;4. Six generations of namespace isolation&lt;/h2&gt;
&lt;p&gt;The namespace itself has not been rearchitected since 1993. What has evolved, in six discrete generations between 1993 and 2026, is the set of &lt;em&gt;partition primitives&lt;/em&gt; layered on top: the mechanisms that let the kernel hide subtrees from particular callers, rewrite paths transparently for particular tokens, or invoke a registered watcher when a particular handle is created. Each generation closes a structural class. None has rendered its predecessor obsolete. On 2026 Windows 11 25H2 all six are simultaneously load-bearing.&lt;/p&gt;

flowchart LR
    G1[&quot;Gen 1&lt;br /&gt;NT 3.1, Jul 1993&lt;br /&gt;Single global namespace&quot;] --&amp;gt; G2
    G2[&quot;Gen 2&lt;br /&gt;Vista, Jan 2007 / SP1, Feb 2008&lt;br /&gt;Session 0 + MIC + ObRegisterCallbacks&quot;] --&amp;gt; G3
    G3[&quot;Gen 3&lt;br /&gt;Windows 8, Oct 2012&lt;br /&gt;AppContainer / Lowbox / per-package directory&quot;] --&amp;gt; G4
    G4[&quot;Gen 4&lt;br /&gt;Windows 10 RTM, Jul 2015&lt;br /&gt;VBS / IUM secure-kernel namespace&quot;] --&amp;gt; G5
    G5[&quot;Gen 5&lt;br /&gt;Windows Server 2016, Oct 2016&lt;br /&gt;Server Silos / silo-scoped views&quot;] --&amp;gt; G6
    G6[&quot;Gen 6&lt;br /&gt;MS15-090, Aug 2015 -&amp;gt;&lt;br /&gt;symbolic-link class hardening&quot;]
&lt;p&gt;Generation numbering is thematic (by isolation capability introduced) rather than strictly chronological. Gen 6 (MS15-090, August 11, 2015) predates Gen 5 (Windows Server 2016, October 12, 2016) by 14 months; the numbering reflects the logical layering of isolation mechanisms, not their calendar sequence.&lt;/p&gt;
&lt;h3&gt;4.1 Generation 2 -- Session 0 isolation, integrity levels, ObRegisterCallbacks&lt;/h3&gt;
&lt;p&gt;Vista shipped three mechanisms in one release window because the structural failure had three faces.&lt;/p&gt;
&lt;p&gt;The first was &lt;em&gt;Session 0 isolation&lt;/em&gt;. From Vista forward, services run in Session 0 alone; the first interactive logon starts at Session 1. Each session gets its own subtree at &lt;code&gt;\Sessions\&amp;lt;n&amp;gt;\BaseNamedObjects&lt;/code&gt;, &lt;code&gt;\Sessions\&amp;lt;n&amp;gt;\Windows\WindowStations&lt;/code&gt;, and &lt;code&gt;\Sessions\&amp;lt;n&amp;gt;\DosDevices&lt;/code&gt;. The Win32 &lt;code&gt;Local\&lt;/code&gt; prefix routes through &lt;code&gt;kernel32!BaseGetNamedObjectDirectory&lt;/code&gt; into the per-session BNO; &lt;code&gt;Global\&lt;/code&gt; routes into the shared &lt;code&gt;\BaseNamedObjects&lt;/code&gt; [@ms-termserv-kernel-object-namespaces]. The Wikipedia Shatter article preserves the architectural fix verbatim: &quot;Local user logins were moved from Session 0 to Session 1, thus separating the user&apos;s processes from system services that could be vulnerable&quot; [@en-wikipedia-shatter-attack]. After Vista an interactive user could no longer &lt;code&gt;SendMessage(WM_TIMER)&lt;/code&gt; into a service&apos;s hidden window because the user and the service no longer shared a window station.&lt;/p&gt;
&lt;p&gt;The second mechanism was &lt;em&gt;Mandatory Integrity Control&lt;/em&gt;. Vista introduced a new ACE type, &lt;code&gt;SYSTEM_MANDATORY_LABEL_ACE&lt;/code&gt;, attached to every object&apos;s security descriptor. Each token carries one of four integrity levels (Low S-1-16-4096, Medium S-1-16-8192, High S-1-16-12288, or System S-1-16-16384), and the Security Reference Monitor compares the requester&apos;s level against the object&apos;s level &lt;em&gt;after&lt;/em&gt; path resolution succeeds [@en-wikipedia-mandatory-integrity-control]. MIC is not a namespace partition. A Low-IL process and a Medium-IL process resolve the same &lt;code&gt;\BaseNamedObjects&lt;/code&gt; directory; only the open is denied at the leaf. The structural property MIC adds is that the leaf check is &lt;em&gt;unbypassable from user mode&lt;/em&gt;; the check fires regardless of which DACL the object carries.&lt;/p&gt;
&lt;p&gt;The third mechanism was &lt;code&gt;ObRegisterCallbacks&lt;/code&gt;. Microsoft&apos;s wdm.h documentation records the API&apos;s first ship date verbatim: &quot;Available starting with Windows Vista with Service Pack 1 (SP1) and Windows Server 2008&quot; [@ms-obregistercallbacks]. The API lets a KMCS-signed driver intercept handle creation and handle duplication on &lt;code&gt;PsProcessType&lt;/code&gt;, &lt;code&gt;PsThreadType&lt;/code&gt;, and the desktop object type. The registration carries an Altitude (a FltMgr-style collision key) and an array of &lt;code&gt;OB_OPERATION_REGISTRATION&lt;/code&gt; records [@ms-ob-callback-registration]. Pre-operation callbacks can strip access-mask bits before the handle is granted; post-operation callbacks fire for logging. The parallel API &lt;code&gt;PsSetCreateProcessNotifyRoutineEx&lt;/code&gt; [@ms-pssetcreateprocessnotifyroutineex] covers process creation. Together, these are the kernel-mode primitives every modern EDR product depends on; they ship inside the Object Manager itself and they are the reason an EDR knows when something opens a handle to &lt;code&gt;lsass.exe&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;4.2 Generation 3 -- AppContainer and the lowbox token&lt;/h3&gt;
&lt;p&gt;Windows 8 shipped on October 26, 2012 [@en-wikipedia-windows-8]. Modern / UWP apps downloaded from the Microsoft Store needed a sandbox finer-grained than per-session BNO. The Vista path rewriting in &lt;code&gt;kernel32!BaseGetNamedObjectDirectory&lt;/code&gt; happened in user mode, which made it the wrong layer for a sandbox: a hostile renderer could in principle bypass the user-mode rewrite. The new layer moved into the kernel.&lt;/p&gt;
&lt;p&gt;Each UWP / MSIX process runs under a special token type, the &lt;em&gt;AppContainer / LowBox token&lt;/em&gt; (referred to in kernel code as the &lt;em&gt;lowbox token&lt;/em&gt;), created by &lt;code&gt;NtCreateLowBoxToken&lt;/code&gt;. The token carries a &lt;code&gt;TOKEN_APPCONTAINER_INFORMATION&lt;/code&gt; block that names the process&apos;s package SID (&lt;code&gt;S-1-15-2-...&lt;/code&gt;) and an &lt;code&gt;AppContainerNumber&lt;/code&gt;. Inside &lt;code&gt;ObpLookupObjectName&lt;/code&gt;, &lt;em&gt;before&lt;/em&gt; the path is walked, the kernel checks whether the caller&apos;s token is a lowbox token; if it is, lookups of &lt;code&gt;\BaseNamedObjects\X&lt;/code&gt;, &lt;code&gt;\RPC Control\X&lt;/code&gt;, and other rewriteable paths get redirected into &lt;code&gt;\Sessions\&amp;lt;n&amp;gt;\AppContainerNamedObjects\&amp;lt;package-sid&amp;gt;\X&lt;/code&gt;. The user-mode caller never sees the rewrite. The package-SID directory is created by SYSTEM at process-creation time with a security descriptor that grants the package SID, and only the package SID, full access. Microsoft&apos;s wording is precise: AppContainer works by &quot;sandboxing the application kernel objects, the AppContainer environment prevents the application from influencing, or being influenced by, other application processes&quot; [@ms-appcontainer-isolation].&lt;/p&gt;

The AppInfo service, which is responsible for creating the new application, calls the undocumented API CreateAppContainerToken to do some internal housekeeping. Unfortunately this API creates object directories under the user&apos;s AppContainerNamedObjects object directory to support redirecting BaseNamedObjects and RPC endpoints by the OS. -- James Forshaw, Project Zero Issue 1550 [@p0-issue1550]
&lt;p&gt;The residual class the AppContainer model has not closed is the one Forshaw&apos;s August 30, 2018 Project Zero post [@p0-issue1550] documents: because the SYSTEM-side AppInfo service has to write into the user&apos;s AppContainerNamedObjects subtree to set up redirection, an unprivileged caller can race the directory creation and end up planting a symbolic link the SYSTEM service then follows. The class -- &quot;SYSTEM-privileged directory creation in user-controllable territory&quot; -- is the worked example of why &quot;the kernel rewrites the name&quot; is an isolation property only when the SYSTEM helpers also use the rewrite.&lt;/p&gt;
&lt;h3&gt;4.3 Generation 4 -- VBS trustlets and the IUM secure-kernel namespace&lt;/h3&gt;
&lt;p&gt;Windows 10 RTM shipped on July 29, 2015 [@en-wikipedia-windows-10-version-history]. The Virtualization-Based Security (VBS) feature set introduced a parallel object-manager-shaped namespace that lives in Virtual Trust Level 1 (VTL1) and is inaccessible to the VTL0 NT kernel. Inside VTL1 the Secure Kernel (&lt;code&gt;securekernel.exe&lt;/code&gt;) maintains its own root, its own type registry, and its own handle-table machinery. The VTL0 NT kernel can see &lt;em&gt;trustlet processes&lt;/em&gt; -- the per-trustlet user-mode containers running in Isolated User Mode (IUM) -- but it cannot reach into their secure-side state.&lt;/p&gt;
&lt;p&gt;Alex Ionescu&apos;s Black Hat USA 2015 talk Battle of SKM and IUM [@ionescu-bh2015-pdf] is the canonical inventory of the inbox Trustlet IDs at ship: Trustlet 0 is the Secure Kernel Process hosting Device Guard; Trustlet 1 is LSAISO.EXE for Credential Guard; Trustlet 2 is VMSP.EXE hosting the virtual TPM; Trustlet 3 is the vTPM provisioning trustlet. Each is identified by a Trustlet ID and reachable only through narrow Secure Kernel ALPC ports. The VBS Trustlets piece in this series unpacks the threat model.&lt;/p&gt;
&lt;h3&gt;4.4 Generation 5 -- Server Silos and the silo-scoped namespace&lt;/h3&gt;
&lt;p&gt;Windows Server 2016 shipped on October 12, 2016 [@en-wikipedia-windows-server-2016]. Microsoft needed a Linux-namespaces equivalent so that container runtimes -- Docker, containerd, and the Azure Kubernetes Service Windows-node pods that followed -- could host adjacent workloads on one kernel. The answer was &lt;em&gt;Server Silo&lt;/em&gt;: a new &lt;code&gt;OBJECT_TYPE&lt;/code&gt; registered alongside &lt;code&gt;Job&lt;/code&gt;, &lt;code&gt;Process&lt;/code&gt;, and &lt;code&gt;Thread&lt;/code&gt;, that carries its own &lt;code&gt;RootDirectory&lt;/code&gt;, &lt;code&gt;DosDevicesDirectory&lt;/code&gt;, and &lt;code&gt;ServerSiloGlobals&lt;/code&gt;. A process attached to a silo via &lt;code&gt;PsAttachSiloToCurrentThread&lt;/code&gt; sees the silo&apos;s namespace as its root; the silo&apos;s &lt;code&gt;\GLOBAL??\C:&lt;/code&gt; resolves to the silo&apos;s &lt;code&gt;\Device\HarddiskVolume*&lt;/code&gt;, which is a different &lt;code&gt;Device&lt;/code&gt; object from the host&apos;s. Job objects [@ms-job-objects] provide the cgroups-equivalent resource-accounting dimension; the Silo type builds on top.&lt;/p&gt;
&lt;p&gt;The canonical reverse-engineering reference is Daniel Prizmant&apos;s July 2020 Unit 42 writeup, which spells out the architecture: &quot;job objects are used in a similar way control groups (cgroups) are used in Linux, and... server silo objects were used as a replacement for namespaces support in the kernel&quot; [@unit42-rev-eng-windows-containers].&lt;/p&gt;
&lt;p&gt;The companion piece, Prizmant&apos;s June 2021 &lt;em&gt;Siloscape&lt;/em&gt; [@unit42-siloscape], is the first known malware family that escapes the silo boundary: Prizmant named the malware &quot;Siloscape (sounds like silo escape) because its primary goal is to escape the container, and in Windows this is implemented mainly by a server silo.&quot; James Forshaw&apos;s April 2021 Project Zero post &lt;em&gt;Who Contains the Containers?&lt;/em&gt; [@p0-who-contains-containers] is the four-LPE companion disclosure. Microsoft&apos;s standing position is that Server Silo is not a security boundary; the Hyper-V Container, which adds a Hyper-V VM around the container&apos;s silo, is the security-boundary product.&lt;/p&gt;
&lt;h3&gt;4.5 Generation 6 -- the symbolic-link hardening continuum&lt;/h3&gt;
&lt;p&gt;The cross-cutting hardening generation closes the symlink subclass that recurred in Generations 1, 3, and 5. MS15-090 shipped on August 11, 2015 [@ms-ms15-090] and &quot;corrects how Windows Object Manager handles object symbolic links created by a sandbox process, by preventing improper interaction with the registry by sandboxed applications, and by preventing improper interaction with the filesystem by sandboxed applications.&quot; The bulletin&apos;s canonical Object Manager CVE is CVE-2015-2428 [@nvd-cve-2015-2428], described verbatim as the case where the &quot;Object Manager in Microsoft Windows... does not properly constrain impersonation levels during interaction with object symbolic links that originated in a sandboxed process.&quot; Subsequent Windows 10 builds added &lt;code&gt;OBJ_DONT_REPARSE&lt;/code&gt;, an open-time flag that disables symbolic-link substitution for callers willing to opt in, and post-Siloscape patches in 2021 closed &lt;code&gt;NtSetInformationSymbolicLink&lt;/code&gt; retargeting from inside a silo.&lt;/p&gt;
&lt;p&gt;The scope document for this article originally attributed MS15-090 to CVE-2015-2528 and CVE-2015-1463. Independent NVD verification confirmed neither is correct: CVE-2015-2528 [@nvd-cve-2015-2528] is the MS15-102 Task Management EoP, and CVE-2015-1463 [@nvd-cve-2015-1463] is a ClamAV denial-of-service crash. The canonical MS15-090 OM-symlink CVE is CVE-2015-2428. Separately, CVE-2018-0824 [@nvd-cve-2018-0824] is a CWE-502 COM deserialization issue that joined the CISA KEV catalog on 2024-08-05, not a namespace-squatting CVE.&lt;/p&gt;
&lt;p&gt;The residual subclass MS15-090 did not close was the per-session &lt;code&gt;\??&lt;/code&gt; DosDevices remapping path under impersonation. A low-privileged process whose token is impersonated by a SYSTEM service can plant a &lt;code&gt;DefineDosDevice&lt;/code&gt; remapping that survives into the impersonation-time &lt;code&gt;\??&lt;/code&gt; view, and the SYSTEM-side activation-context resolver then opens the redirected path while running with elevated privileges. The canonical 2023 worked example is HackSys&apos;s &lt;em&gt;Activation Context Hell -- DosDevices Remapping Attack under Impersonation&lt;/em&gt; [@hacksys-activation-context-hell], which targets the CSRSS / SxS activation-context resolver and shipped as CVE-2023-35359 [@nvd-cve-2023-35359], with the closely-related CVE-2022-22047 [@nvd-cve-2022-22047] covering the underlying CSRSS surface. The mitigation has to live inside the impersonation-aware &lt;code&gt;\??&lt;/code&gt; resolver in the SYSTEM caller, not at the symlink-creation gate.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Every generation since Generation 1 has &lt;em&gt;layered&lt;/em&gt; a new isolation primitive on top of the prior generation. None has rendered its predecessor obsolete. On 2026 Windows 11 25H2 all six generations coexist simultaneously: a UWP / MSIX app inside a Server Silo on a VBS-enabled host is session-partitioned, lowbox-rewritten, silo-scoped, VTL0-confined, integrity-gated, and watched by every loaded EDR&apos;s &lt;code&gt;ObRegisterCallbacks&lt;/code&gt; filter. Each layer adds an independent enforcement point at &lt;code&gt;ObpLookupObjectName&lt;/code&gt; time.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Six generations of isolation primitives is a tidy story, but it has glossed the most important question. What is the actual kernel data structure all six generations parameterize? What does the path-walk algorithm look like, what is the type registry, and where does the hash table live?&lt;/p&gt;
&lt;h2&gt;5. The four load-bearing primitives&lt;/h2&gt;
&lt;p&gt;If you remember one paragraph from this article, make it this one. The Object Manager namespace is built out of four kernel data structures: an &lt;code&gt;OBJECT_HEADER&lt;/code&gt; that prefixes every named object in memory, an &lt;code&gt;OBJECT_TYPE&lt;/code&gt; singleton that owns each type&apos;s method table, a &lt;code&gt;ParseProcedure&lt;/code&gt; that delegates path resolution to the owning subsystem when needed, and an &lt;code&gt;OBJECT_DIRECTORY&lt;/code&gt; hash table that maps names to objects. Every Windows security boundary you have read about is a parameter to one of these four pieces. The next eight subsections rebuild them one at a time.&lt;/p&gt;

flowchart TB
    OD[&quot;OBJECT_DIRECTORY&lt;br /&gt;(37-bucket hash table)&quot;] --&amp;gt;|&quot;hash(name) % 37&quot;| OH
    OH[&quot;OBJECT_HEADER&lt;br /&gt;(PointerCount, HandleCount,&lt;br /&gt;TypeIndex, InfoMask,&lt;br /&gt;SecurityDescriptor, Body offset)&quot;] --&amp;gt;|&quot;TypeIndex XOR&lt;br /&gt;ObHeaderCookie&quot;| OT
    OT[&quot;OBJECT_TYPE singleton&lt;br /&gt;(in nt!ObTypeIndexTable)&quot;] --&amp;gt;|&quot;TypeInfo&quot;| TI
    TI[&quot;TYPE_INFO method table&lt;br /&gt;(Dump, Open, Close, Delete,&lt;br /&gt;ParseProcedure,&lt;br /&gt;Security, QueryName, ...)&quot;]
    OH --&amp;gt;|&quot;Body[]&quot;| BODY[&quot;Type-specific body&lt;br /&gt;(EPROCESS, FILE_OBJECT,&lt;br /&gt;SECTION_OBJECT, ...)&quot;]
&lt;h3&gt;5.1 OBJECT_HEADER&lt;/h3&gt;
&lt;p&gt;Every named kernel object lives in non-paged pool. Immediately &lt;em&gt;before&lt;/em&gt; each object&apos;s typed body sits an &lt;code&gt;OBJECT_HEADER&lt;/code&gt;, a 0x30-byte (48-byte on x64) structure that the Object Manager owns. &lt;code&gt;PointerCount&lt;/code&gt; and &lt;code&gt;HandleCount&lt;/code&gt; are the two reference counts: the former tracks raw kernel-mode pointer references, the latter tracks user-mode handles. &lt;code&gt;TypeIndex&lt;/code&gt; is a single byte that indexes into the &lt;code&gt;nt!ObTypeIndexTable&lt;/code&gt; to find the object&apos;s type singleton; since Windows 10 1709, the byte is XOR-obfuscated against the per-boot &lt;code&gt;nt!ObHeaderCookie&lt;/code&gt; so that simple type confusion is non-trivial.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;InfoMask&lt;/code&gt; is a bitmap of optional sub-headers that may precede the main header: &lt;code&gt;OBJECT_HEADER_NAME_INFO&lt;/code&gt; for named objects, &lt;code&gt;OBJECT_HEADER_QUOTA_INFO&lt;/code&gt; for objects that charge a quota block, &lt;code&gt;OBJECT_HEADER_HANDLE_INFO&lt;/code&gt; for objects that need per-process handle accounting. &lt;code&gt;SecurityDescriptor&lt;/code&gt; is a tagged pointer to the object&apos;s DACL/SACL. &lt;code&gt;Body[]&lt;/code&gt; is the offset at which the type-specific payload begins; for a process object that payload is an &lt;code&gt;EPROCESS&lt;/code&gt;, for a file it is a &lt;code&gt;FILE_OBJECT&lt;/code&gt;, and so on. The canonical reference is Chapter 8 of &lt;em&gt;Windows Internals 7th Edition Part 1&lt;/em&gt; [@microsoftpressstore-wininternals7-part1].&lt;/p&gt;

The per-object header (`nt!_OBJECT_HEADER`) that precedes every named kernel object in non-paged pool. Carries reference counts (`PointerCount`, `HandleCount`), a `TypeIndex` byte that points into `nt!ObTypeIndexTable` (XOR-obfuscated against `nt!ObHeaderCookie` since Windows 10 1709), an `InfoMask` describing optional sub-headers, a `SecurityDescriptor` pointer, and the offset to the typed `Body[]`.
&lt;p&gt;The &lt;code&gt;TypeIndex&lt;/code&gt; XOR-with-cookie is one of the smallest kernel hardening changes Microsoft has shipped: a single byte that prevents a poisoned &lt;code&gt;OBJECT_HEADER&lt;/code&gt; from naming an arbitrary type after a heap-corruption primitive. The cookie is per-boot and lives in &lt;code&gt;nt!ObHeaderCookie&lt;/code&gt;. The hardening is documented in &lt;em&gt;Windows Internals 7th Edition&lt;/em&gt; Chapter 8 [@microsoftpressstore-wininternals7-part1] and in Geoff Chappell&apos;s reverse-engineering studies; Microsoft has not, as of 2026, published a Learn-hosted reference for the cookie itself.&lt;/p&gt;
&lt;h3&gt;5.2 OBJECT_TYPE&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;OBJECT_TYPE&lt;/code&gt; is the per-type singleton. There is exactly one &lt;code&gt;OBJECT_TYPE&lt;/code&gt; per registered kernel type, and they live in &lt;code&gt;\ObjectTypes&lt;/code&gt;. On Windows 11 25H2 the count sits at roughly seventy-five: &lt;code&gt;Type&lt;/code&gt;, &lt;code&gt;Directory&lt;/code&gt;, &lt;code&gt;SymbolicLink&lt;/code&gt;, &lt;code&gt;Token&lt;/code&gt;, &lt;code&gt;Job&lt;/code&gt;, &lt;code&gt;Process&lt;/code&gt;, &lt;code&gt;Thread&lt;/code&gt;, &lt;code&gt;Section&lt;/code&gt;, &lt;code&gt;Key&lt;/code&gt;, &lt;code&gt;File&lt;/code&gt;, &lt;code&gt;Event&lt;/code&gt;, &lt;code&gt;Mutant&lt;/code&gt;, &lt;code&gt;Semaphore&lt;/code&gt;, &lt;code&gt;Timer&lt;/code&gt;, &lt;code&gt;WindowStation&lt;/code&gt;, &lt;code&gt;Desktop&lt;/code&gt;, &lt;code&gt;Device&lt;/code&gt;, &lt;code&gt;Driver&lt;/code&gt;, &lt;code&gt;IoCompletion&lt;/code&gt;, &lt;code&gt;ALPC Port&lt;/code&gt;, &lt;code&gt;EtwRegistration&lt;/code&gt;, &lt;code&gt;Silo&lt;/code&gt;, and dozens more.&lt;/p&gt;

The per-type singleton (`nt!_OBJECT_TYPE`) that owns each kernel type&apos;s method table. The `TypeInfo` field carries eight procedure pointers and one offset field (WaitObjectFlagOffset): `DumpProcedure`, `OpenProcedure`, `CloseProcedure`, `DeleteProcedure`, `ParseProcedure` (the path-resolution callback), `SecurityProcedure`, `QueryNameProcedure`, `OkayToCloseProcedure`, and a `WaitObjectFlagOffset` offset for waitable types. Every `OBJECT_TYPE` instance is reachable through `\ObjectTypes`.
&lt;p&gt;The &lt;code&gt;TypeInfo&lt;/code&gt; field on each &lt;code&gt;OBJECT_TYPE&lt;/code&gt; carries eight procedure pointers and one offset field (WaitObjectFlagOffset). The most consequential is the &lt;code&gt;ParseProcedure&lt;/code&gt;. When &lt;code&gt;ObpLookupObjectName&lt;/code&gt; is walking a path component-by-component, and a step lands on an object whose &lt;code&gt;OBJECT_TYPE&lt;/code&gt; defines a &lt;code&gt;ParseProcedure&lt;/code&gt;, the OM hands the &lt;em&gt;residual&lt;/em&gt; path and the desired access to that procedure, which becomes the namespace authority below that point. That is how the registry&apos;s &lt;code&gt;Key&lt;/code&gt; type, the I/O Manager&apos;s &lt;code&gt;Device&lt;/code&gt; type, and the various WMI / Volume-Manager subsystems insert themselves into the namespace without the Object Manager having to know any of their internal structure [@en-wikipedia-object-manager].&lt;/p&gt;
&lt;h3&gt;5.3 The parse procedure&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ObpLookupObjectName&lt;/code&gt; walks &lt;code&gt;\Foo\Bar\Baz\...\Leaf&lt;/code&gt; left-to-right. At each component the walker does one of three things. The common case is a hash-table lookup in the current &lt;code&gt;OBJECT_DIRECTORY&lt;/code&gt;&apos;s 37 buckets to find the child object by name. The second case is &lt;code&gt;SymbolicLink&lt;/code&gt; substitution: if the child object&apos;s type is &lt;code&gt;SymbolicLink&lt;/code&gt;, the walker substitutes the link target and re-enters the walk at the substitution. The third and most consequential case is &lt;em&gt;parse-procedure handoff&lt;/em&gt;. If the child object&apos;s &lt;code&gt;OBJECT_TYPE&lt;/code&gt; has a non-null &lt;code&gt;ParseProcedure&lt;/code&gt;, the walker stops, hands the residual path string to that procedure, and lets it decide what to do.&lt;/p&gt;

The load-bearing method pointer on each `OBJECT_TYPE`&apos;s `TypeInfo` field. When `ObpLookupObjectName` encounters an object whose type defines a `ParseProcedure`, the residual path is handed to that procedure for resolution. The two canonical parse procedures are `IopParseDevice` (for the `Device` type, which delegates further resolution to the device&apos;s owning driver via `IRP_MJ_CREATE`) and `CmpParseKey` (for the `Key` type, which walks the registry hive).
&lt;p&gt;&lt;code&gt;IopParseDevice&lt;/code&gt; is the parse procedure for the &lt;code&gt;Device&lt;/code&gt; type. When the walker reaches &lt;code&gt;\Device\HarddiskVolume1&lt;/code&gt; and is asked to continue with &lt;code&gt;\Users\me\file.txt&lt;/code&gt;, the I/O Manager builds an &lt;code&gt;IRP_MJ_CREATE&lt;/code&gt; packet, dispatches it to the filesystem driver that owns the volume (NTFS, ReFS, ExFAT, FAT32, or one of several others), and lets that driver walk the rest of the path inside its own on-disk structures. The driver returns a &lt;code&gt;FILE_OBJECT&lt;/code&gt;, which the Object Manager packages into a handle.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;CmpParseKey&lt;/code&gt; is the parse procedure for the &lt;code&gt;Key&lt;/code&gt; type. When the walker reaches &lt;code&gt;\REGISTRY&lt;/code&gt; and is asked to continue with &lt;code&gt;\MACHINE\Software\Microsoft\Windows&lt;/code&gt;, the Configuration Manager takes over and walks the in-memory hive structures.&lt;/p&gt;
&lt;p&gt;The structural consequence is profound. Every named file in Windows is, technically, a leaf in the Object Manager namespace. NTFS, ReFS, ExFAT, and the registry are not separate naming systems; they are parse-procedure callbacks that hand &lt;code&gt;FILE_OBJECT&lt;/code&gt; or &lt;code&gt;KEY&lt;/code&gt; bodies back to the OM.&lt;/p&gt;

sequenceDiagram
    participant User as User Process
    participant OM as ObpLookupObjectName
    participant Dir as \GLOBAL?? OBJECT_DIRECTORY
    participant Dev as \Device\HarddiskVolume1 (Device type)
    participant Drv as NTFS Driver
    User-&amp;gt;&amp;gt;OM: NtCreateFile(&quot;\??\C:\Users\me\file.txt&quot;)
    OM-&amp;gt;&amp;gt;OM: rewrite \??\ -&amp;gt; \Sessions\\DosDevices\
    OM-&amp;gt;&amp;gt;Dir: lookup &quot;C:&quot;
    Dir--&amp;gt;&amp;gt;OM: SymbolicLink -&amp;gt; \Device\HarddiskVolume1
    OM-&amp;gt;&amp;gt;OM: substitute, re-enter walk
    OM-&amp;gt;&amp;gt;Dev: lookup \Device\HarddiskVolume1
    Dev--&amp;gt;&amp;gt;OM: type=Device, has ParseProcedure
    OM-&amp;gt;&amp;gt;Drv: IopParseDevice with &quot;\Users\me\file.txt&quot;
    Drv-&amp;gt;&amp;gt;Drv: IRP_MJ_CREATE: walk MFT, find file
    Drv--&amp;gt;&amp;gt;OM: FILE_OBJECT
    OM--&amp;gt;&amp;gt;User: HANDLE
&lt;h3&gt;5.4 The 37-bucket directory hash&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;OBJECT_DIRECTORY&lt;/code&gt; is a 37-bucket open-hash table. The hash function is &lt;code&gt;RtlHashUnicodeString&lt;/code&gt;, applied to each component name. Thirty-seven was the prime Cutler picked in 1993; the constant has not changed in thirty-three years. The folk-knowledge corroboration is in Chapter 8 of &lt;em&gt;Windows Internals 7th Edition Part 1&lt;/em&gt; and in Forshaw&apos;s &lt;em&gt;Windows Security Internals&lt;/em&gt; Chapter 8; Microsoft has never published a Learn-hosted spec for the constant [@nostarch-windows-security-internals].&lt;/p&gt;

The 37-bucket open-hash table (`nt!_OBJECT_DIRECTORY`) that lives at every interior node of the Object Manager tree. Keys are `UNICODE_STRING` component names; the hash is `RtlHashUnicodeString` modulo 37. Each bucket is a linked list of `OBJECT_DIRECTORY_ENTRY` records that point at the next-level `OBJECT_HEADER`. Reading the tree requires `Directory`-`TRAVERSE` rights on the parent.
&lt;p&gt;The 37-bucket constant from 1993 has not changed in thirty-three years. On a 2026 Windows 11 25H2 box with several hundred MSIX packages each owning an &lt;code&gt;\AppContainerNamedObjects\&amp;lt;package-sid&amp;gt;\&lt;/code&gt; subtree, average bucket chains run several entries deep. Collision pressure on the constant is the open problem returned to in Section 9.&lt;/p&gt;
&lt;h3&gt;5.5 The lowbox redirect inside ObpLookupObjectName&lt;/h3&gt;
&lt;p&gt;This is the subsection that earns the second aha moment of the article.&lt;/p&gt;
&lt;p&gt;When the calling thread&apos;s primary token is a lowbox token, &lt;code&gt;ObpLookupObjectName&lt;/code&gt; consults the token&apos;s &lt;code&gt;AppContainerNumber&lt;/code&gt; and package SID &lt;em&gt;before&lt;/em&gt; it begins the walk. Lookups that would otherwise resolve into &lt;code&gt;\BaseNamedObjects&lt;/code&gt; or &lt;code&gt;\RPC Control&lt;/code&gt; are rewritten into &lt;code&gt;\Sessions\&amp;lt;n&amp;gt;\AppContainerNamedObjects\&amp;lt;package-sid&amp;gt;\&lt;/code&gt;. The rewrite happens transparently to the user-mode Win32 caller, which still thinks it asked for &lt;code&gt;\BaseNamedObjects\X&lt;/code&gt;.&lt;/p&gt;

A specialised token type produced by `NtCreateLowBoxToken` that carries a `TOKEN_APPCONTAINER_INFORMATION` block (with a package SID `S-1-15-2-...` and an `AppContainerNumber`). When a process runs under a lowbox token, `ObpLookupObjectName` rewrites every named-object lookup into the per-package directory `\Sessions\\AppContainerNamedObjects\\` before path walking begins.

The user-facing brand for the lowbox-token mechanism. Every UWP / MSIX / Windows Store app runs in an AppContainer. The Windows API surface is unchanged for the app; the Object Manager rewrites every named-object name into a per-package subtree, gating cross-package coordination at the namespace layer. The Microsoft Learn page describes this as &quot;Sandboxing the application kernel objects, the AppContainer environment prevents the application from influencing, or being influenced by, other application processes&quot; [@ms-appcontainer-isolation].
&lt;p&gt;The aha moment is structural. AppContainer is not a &lt;em&gt;containment&lt;/em&gt; mechanism the way you might first picture it. It is a &lt;em&gt;name-translation&lt;/em&gt; mechanism. The lowbox token tells the kernel which directory to rewrite every name into; the sandbox is, at root, a hash-table indirection inside the kernel&apos;s path-walk function. The Edge renderer process cannot name &lt;code&gt;\BaseNamedObjects\GlobalEvent_Foo&lt;/code&gt; because the kernel rewrites that name into &lt;code&gt;\Sessions\1\AppContainerNamedObjects\S-1-15-2-...\Global\GlobalEvent_Foo&lt;/code&gt; before lookup even begins. The &quot;sandbox&quot; is a hash-table redirect.&lt;/p&gt;
&lt;h3&gt;5.6 The Silo OBJECT_TYPE and silo-scoped views&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Silo&lt;/code&gt; is itself a registered &lt;code&gt;OBJECT_TYPE&lt;/code&gt;. Each silo instance carries a silo-scoped &lt;code&gt;RootDirectory&lt;/code&gt;, &lt;code&gt;DosDevicesDirectory&lt;/code&gt;, and &lt;code&gt;ServerSiloGlobals&lt;/code&gt; (with the silo&apos;s own registry-hive root and per-silo &lt;code&gt;BaseNamedObjects&lt;/code&gt; root). &lt;code&gt;PsAttachSiloToCurrentThread&lt;/code&gt; switches the thread&apos;s namespace view; once attached, every Object Manager lookup runs through the silo&apos;s roots instead of the host&apos;s. Job objects, which provide the cgroups-equivalent resource-accounting substrate, are the underlying primitive the Silo type extends [@ms-job-objects]. The structural design history is in Prizmant&apos;s reverse-engineering writeup [@unit42-rev-eng-windows-containers].&lt;/p&gt;

A specialised `Job`-derived kernel object (`OBJECT_TYPE` Silo) introduced in Windows Server 2016 that carries silo-scoped `RootDirectory`, `DosDevicesDirectory`, and `ServerSiloGlobals` fields. A thread attached to a silo via `PsAttachSiloToCurrentThread` sees the silo&apos;s namespace as its root; the silo&apos;s `\GLOBAL??\C:` resolves to the silo&apos;s `\Device\HarddiskVolume*`, which is a different `Device` object from the host&apos;s. Server Silo is the substrate underneath Windows Server Containers and WSL1.
&lt;h3&gt;5.7 The Secure Kernel&apos;s parallel namespace&lt;/h3&gt;
&lt;p&gt;Inside VTL1, the Secure Kernel maintains a separate Object Manager tree with its own root, its own type registry, and its own handle-table machinery. The VTL0 NT kernel cannot enumerate this tree; the only cross-VTL traffic is the narrow ALPC interface each trustlet publishes. Ionescu&apos;s BH2015 inventory (Trustlet IDs 0 through 3 at ship, growing in subsequent releases) is the canonical primary [@ionescu-bh2015-pdf].&lt;/p&gt;

A user-mode process running in Isolated User Mode under the VTL1 Secure Kernel. Each trustlet is signed with both the Windows System Component Verification EKU (1.3.6.1.4.1.311.10.3.6) and the IUM EKU (1.3.6.1.4.1.311.10.3.37), runs at Signature Level 12, and is reachable from VTL0 only through narrow ALPC ports. LSAISO.EXE (Credential Guard), VMSP.EXE (virtual TPM host), and the vTPM provisioning trustlet are the inbox examples.
&lt;h3&gt;5.8 The handle table&lt;/h3&gt;
&lt;p&gt;The namespace is the &lt;em&gt;name&lt;/em&gt; side; the per-process &lt;code&gt;HANDLE_TABLE&lt;/code&gt; is the &lt;em&gt;access&lt;/em&gt; side. Once a handle exists in a process, no name lookup happens on subsequent use; the kernel dereferences the handle through a three-level radix tree indexed by the 32-bit handle value, lands on an &lt;code&gt;OBJECT_HEADER&lt;/code&gt;, and operates on the body. This is why &lt;code&gt;ObRegisterCallbacks&lt;/code&gt; fires on handle &lt;em&gt;creation&lt;/em&gt; and &lt;em&gt;duplication&lt;/em&gt; rather than on every use, and why an inherited handle bypasses the callback entirely. The structural consequence -- that the Object Manager is the gate at name resolution but not at every operation -- comes back in Section 8.&lt;/p&gt;
&lt;p&gt;Now you know the data structure. But what does the actual tree look like in 2026? What does &lt;code&gt;\&lt;/code&gt; contain on a Windows 11 25H2 box, and which security boundary lives in each top-level directory?&lt;/p&gt;
&lt;h2&gt;6. The 2026 top-level directory atlas&lt;/h2&gt;
&lt;p&gt;Open &lt;code&gt;WinObj.exe&lt;/code&gt; as administrator on a Windows 11 25H2 machine and the root directory at &lt;code&gt;\&lt;/code&gt; carries roughly twenty entries. The table below catalogues the load-bearing ones. Each row names the directory, the security boundary it physically realises, and a representative exploit class that has been thrown at it. The driver kit&apos;s Object Directories reference [@ms-object-directories] is Microsoft&apos;s canonical inventory.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Top-level directory&lt;/th&gt;
&lt;th&gt;What it contains&lt;/th&gt;
&lt;th&gt;Which boundary it enforces&lt;/th&gt;
&lt;th&gt;Exploit class&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\ObjectTypes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The ~75 &lt;code&gt;OBJECT_TYPE&lt;/code&gt; singletons (&lt;code&gt;Process&lt;/code&gt;, &lt;code&gt;Thread&lt;/code&gt;, &lt;code&gt;Section&lt;/code&gt;, &lt;code&gt;Key&lt;/code&gt;, &lt;code&gt;File&lt;/code&gt;, &lt;code&gt;Token&lt;/code&gt;, &lt;code&gt;Job&lt;/code&gt;, &lt;code&gt;Silo&lt;/code&gt;, etc.)&lt;/td&gt;
&lt;td&gt;Meta -- the type registry the rest of the namespace depends on&lt;/td&gt;
&lt;td&gt;Type confusion (mitigated by &lt;code&gt;ObHeaderCookie&lt;/code&gt; since Windows 10 1709)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\Device&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Driver-published device objects (&lt;code&gt;\Device\HarddiskVolume*&lt;/code&gt;, &lt;code&gt;\Device\Tcp&lt;/code&gt;, &lt;code&gt;\Device\Tpm&lt;/code&gt;, &lt;code&gt;\Device\NamedPipe&lt;/code&gt;, &lt;code&gt;\Device\Mailslot&lt;/code&gt;, &lt;code&gt;\Device\Vmbus&lt;/code&gt;, &lt;code&gt;\Device\KsecDD&lt;/code&gt;, &lt;code&gt;\Device\CNG&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;The I/O Manager&apos;s surface; each driver&apos;s parse procedure consumes residual paths&lt;/td&gt;
&lt;td&gt;Bait-and-switch on &lt;code&gt;\Device&lt;/code&gt; (a low-privilege user redirects a privileged opener through a planted symbolic link)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\Driver&lt;/code&gt;, &lt;code&gt;\FileSystem&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Loaded &lt;code&gt;DRIVER_OBJECT&lt;/code&gt; registries&lt;/td&gt;
&lt;td&gt;KMCS / HVCI driver-load gate&lt;/td&gt;
&lt;td&gt;Vulnerable signed-driver class (BYOVD)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\GLOBAL??&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The machine-wide DosDevices view -- where &lt;code&gt;C:&lt;/code&gt; and &lt;code&gt;D:&lt;/code&gt; are symlinks to &lt;code&gt;\Device\HarddiskVolume*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cross-session drive-letter map&lt;/td&gt;
&lt;td&gt;Symlink redirect across session boundary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\??&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The per-session DosDevices alias, falling through to &lt;code&gt;\GLOBAL??&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Session-scoped drive-letter map&lt;/td&gt;
&lt;td&gt;The HackSys / CVE-2023-35359 worked example: a low-privilege caller plants a &lt;code&gt;DefineDosDevice&lt;/code&gt; remapping that survives into the impersonation-time &lt;code&gt;\??&lt;/code&gt; view, and the SYSTEM-side activation-context resolver opens the redirected path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\BaseNamedObjects&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The global / &lt;code&gt;Global\&lt;/code&gt;-prefixed-only BNO&lt;/td&gt;
&lt;td&gt;Cross-session named-object visibility&lt;/td&gt;
&lt;td&gt;Pre-Vista squatting class (closed by Generation 2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\Sessions\&amp;lt;n&amp;gt;\&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Per-session subtrees (BNO, DosDevices, WindowStations, AppContainerNamedObjects)&lt;/td&gt;
&lt;td&gt;Session boundary (Generation 2)&lt;/td&gt;
&lt;td&gt;Shatter attacks (closed by Generation 2)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\Sessions\&amp;lt;n&amp;gt;\AppContainerNamedObjects\&amp;lt;package-sid&amp;gt;\&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Per-package UWP / MSIX lowbox namespace&lt;/td&gt;
&lt;td&gt;AppContainer / lowbox boundary (Generation 3)&lt;/td&gt;
&lt;td&gt;Forshaw P0 Issue 1550 arbitrary-directory creation race&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\RPC Control&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Every named LRPC ALPC port (every COM call lands here)&lt;/td&gt;
&lt;td&gt;RPC endpoint visibility&lt;/td&gt;
&lt;td&gt;Endpoint squatting against named LRPC ports&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\KnownDlls&lt;/code&gt;, &lt;code&gt;\KnownDlls32&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pre-mapped &lt;code&gt;Section&lt;/code&gt; objects for system DLLs&lt;/td&gt;
&lt;td&gt;Loader supply-chain&lt;/td&gt;
&lt;td&gt;&lt;code&gt;DefineDosDevice&lt;/code&gt; + &lt;code&gt;\??&lt;/code&gt; symlink-plant trick (closed in NTDLL July 2022, build 19044.1826)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\KernelObjects&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;System-defined events (&lt;code&gt;LowMemoryCondition&lt;/code&gt;, &lt;code&gt;HighMemoryCondition&lt;/code&gt;, etc.)&lt;/td&gt;
&lt;td&gt;Kernel-internal visibility&lt;/td&gt;
&lt;td&gt;None public&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\Callback&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;System-defined &lt;code&gt;Callback&lt;/code&gt; objects (&lt;code&gt;ExCallback&lt;/code&gt; slots drivers register against)&lt;/td&gt;
&lt;td&gt;Kernel API extension surface&lt;/td&gt;
&lt;td&gt;Driver-callback abuse&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\Security&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;LSA-private endpoints&lt;/td&gt;
&lt;td&gt;LSA / authentication isolation&lt;/td&gt;
&lt;td&gt;Credential-theft (the LSAISO trustlet via Generation 4)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\Windows&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;BNO-redirect surface and &lt;code&gt;SharedSection&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Win32 subsystem shared state&lt;/td&gt;
&lt;td&gt;Cross-session Win32 state leakage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\Silos\&amp;lt;id&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Per-container silo subroots on Server SKUs&lt;/td&gt;
&lt;td&gt;Server Silo boundary (Generation 5)&lt;/td&gt;
&lt;td&gt;Siloscape -- symlink retarget out of the silo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;\BNOLINKS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The boundary-keyed private-namespace index&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CreatePrivateNamespace&lt;/code&gt; cross-session/cross-package IPC&lt;/td&gt;
&lt;td&gt;None public; the directory itself is RE-derived&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

flowchart LR
    subgraph EdgeRenderer[&quot;Microsoft Edge Renderer (lowbox token)&quot;]
        K32[&quot;CreateMutexW(L&apos;Global\\Foo&apos;)&quot;]
    end
    K32 --&amp;gt;|&quot;NtCreateMutant, OBJECT_ATTRIBUTES&quot;| OB
    subgraph KernelOb[&quot;ObpLookupObjectName&quot;]
        OB[&quot;Read caller token&lt;br /&gt;token.AppContainerNumber&lt;br /&gt;token.PackageSid&quot;]
        OB --&amp;gt;|&quot;rewrite name&quot;| RW[&quot;Rewrite &apos;\\BaseNamedObjects\\Global\\Foo&apos;&lt;br /&gt;to&lt;br /&gt;&apos;\\Sessions\\1\\AppContainerNamedObjects\\&lt;br /&gt;S-1-15-2-...\\Global\\Foo&apos;&quot;]
        RW --&amp;gt; WALK[&quot;walk the rewritten path&quot;]
    end
    WALK --&amp;gt; Dir[&quot;\\Sessions\\1\\AppContainerNamedObjects\\&lt;br /&gt;S-1-15-2-...\\Global\\&lt;br /&gt;(per-package OBJECT_DIRECTORY,&lt;br /&gt;DACL allows only package SID)&quot;]
&lt;p&gt;The &lt;code&gt;\BNOLINKS&lt;/code&gt; directory deserves a separate paragraph because it is not on Microsoft Learn. &lt;code&gt;NtCreatePrivateNamespace&lt;/code&gt; is the kernel-side syscall behind the Win32 &lt;code&gt;CreatePrivateNamespace&lt;/code&gt; API [@ms-createprivatenamespacew]; the caller passes a boundary descriptor built by &lt;code&gt;CreateBoundaryDescriptor&lt;/code&gt; [@ms-createboundarydescriptorw] plus one or more SIDs added via &lt;code&gt;AddSIDToBoundaryDescriptor&lt;/code&gt; [@ms-addsidtoboundarydescriptor]. The kernel materialises one &lt;code&gt;\BNOLINKS&lt;/code&gt; entry per &lt;code&gt;(alias_prefix, boundary_descriptor_hash)&lt;/code&gt; tuple; two callers that pass the same &lt;code&gt;lpAliasPrefix&lt;/code&gt; but different boundary descriptors land on different directories. The native signature is documented in the PHNT-derived NtDoc mirror [@ntdoc-ntcreateprivatenamespace], and the &lt;code&gt;OBJECT_BOUNDARY_DESCRIPTOR&lt;/code&gt; structure layout is at ntdoc.m417z.com/object_boundary_descriptor [@ntdoc-object-boundary-descriptor]. The Win32 Object Namespaces overview [@ms-object-namespaces] is Microsoft&apos;s only published user-mode reference; the &lt;code&gt;\BNOLINKS&lt;/code&gt; directory name itself is reverse-engineering-derived.The &lt;code&gt;\BNOLINKS&lt;/code&gt; directory is documented only through reverse engineering of &lt;code&gt;ntoskrnl.exe&lt;/code&gt; -- via Forshaw&apos;s NtObjectManager and System Informer&apos;s PHNT headers -- not on Microsoft Learn. The user-mode API surface (&lt;code&gt;CreatePrivateNamespace&lt;/code&gt;, &lt;code&gt;CreateBoundaryDescriptor&lt;/code&gt;, &lt;code&gt;AddSIDToBoundaryDescriptor&lt;/code&gt;) is fully documented. The provenance gap is worth flagging when you cite the directory by name.The &lt;code&gt;\KnownDlls&lt;/code&gt; LPE class was, for a decade, the canonical example of how a DACL plus loader-side validation could lock down a supply-chain anchor. Forshaw&apos;s August 2018 P0 post first sketched a &lt;code&gt;DefineDosDevice&lt;/code&gt; + &lt;code&gt;\??&lt;/code&gt; symlink-plant chain that could land a forged &lt;code&gt;Section&lt;/code&gt; object into &lt;code&gt;\KnownDlls&lt;/code&gt;; Clement Labro (itm4n) implemented the attack as the PPLdump tool and wrote companion posts on both itm4n.github.io [@itm4n-lsass-runasppl] and the SCRT team blog [@blog-scrt-bypassing-lsa-protection-in-userland]. The class was closed in NTDLL by Windows 10 21H2 build 19044.1826; itm4n confirms the patch in &lt;em&gt;The End of PPLdump&lt;/em&gt; [@itm4n-the-end-of-ppldump]: &quot;A patch in NTDLL now prevents PPLs from loading Known DLLs.&quot;&lt;/p&gt;
&lt;p&gt;{`
const MAX_DIRECTORY_BUCKETS = 37;&lt;/p&gt;
&lt;p&gt;function rtlHashUnicodeString(name) {
  let h = 0;
  for (const ch of name.toUpperCase()) {
    h = (h * 31 + ch.charCodeAt(0)) &amp;gt;&amp;gt;&amp;gt; 0;
  }
  return h % MAX_DIRECTORY_BUCKETS;
}&lt;/p&gt;
&lt;p&gt;function makeDir() {
  return { buckets: Array(MAX_DIRECTORY_BUCKETS).fill(null).map(() =&amp;gt; []) };
}&lt;/p&gt;
&lt;p&gt;function addChild(dir, name, child) {
  dir.buckets[rtlHashUnicodeString(name)].push({ name, child });
}&lt;/p&gt;
&lt;p&gt;function lookupObjectName(path, root) {
  const components = path.split(&apos;\\&apos;).filter(Boolean);
  let cursor = root;
  for (const comp of components) {
    const bucket = rtlHashUnicodeString(comp);
    const chain = cursor.buckets[bucket];
    const hit = chain.find(e =&amp;gt; e.name.toUpperCase() === comp.toUpperCase());
    console.log(`lookup &apos;${comp}&apos; -&amp;gt; bucket ${bucket}, chain length ${chain.length}, ${hit ? &apos;HIT&apos; : &apos;MISS&apos;}`);
    if (!hit) return null;
    if (hit.child.parseProcedure) {
      const rest = &apos;\\&apos; + components.slice(components.indexOf(comp) + 1).join(&apos;\\&apos;);
      console.log(`  parse-procedure handoff for type &apos;${hit.child.type}&apos;, residual=&apos;${rest}&apos;`);
      return { handedOff: hit.child, residual: rest };
    }
    cursor = hit.child;
  }
  return cursor;
}&lt;/p&gt;
&lt;p&gt;const root = makeDir();
const device = makeDir();
device.parseProcedure = true; device.type = &apos;Device&apos;;
const sessions = makeDir();
addChild(root, &apos;Device&apos;, device);
addChild(root, &apos;Sessions&apos;, sessions);
addChild(root, &apos;BaseNamedObjects&apos;, makeDir());&lt;/p&gt;
&lt;p&gt;lookupObjectName(&apos;\\Device\\HarddiskVolume1\\Users\\me\\file.txt&apos;, root);
`}&lt;/p&gt;
&lt;p&gt;The walk is the algorithm. The 37 is the bucket count Cutler picked in 1993. The parse-procedure handoff is where the I/O Manager and the Configuration Manager and dozens of other subsystems insert themselves into the tree. Now turn the question around: Windows bet on one tree. What did the kernels that did not bet on one tree do, and why?&lt;/p&gt;
&lt;h2&gt;7. How other kernels name kernel objects&lt;/h2&gt;
&lt;p&gt;Three kernels, three different bets. Linux took the namespace and &lt;em&gt;split it into per-resource-class clones&lt;/em&gt; -- one for mounts, one for PIDs, one for IPC, one for the network stack, one for users, one for hostnames, one for cgroups, one for time -- and never built a unified tree. macOS / Darwin gave each task its own &lt;em&gt;Mach port-right namespace&lt;/em&gt; and let &lt;code&gt;launchd&lt;/code&gt; broker named-service lookups. Plan 9 from Bell Labs was the academic ancestor of &quot;every named OS resource is a filesystem path,&quot; and the design Cutler imported into NT.&lt;/p&gt;
&lt;h3&gt;7.1 Linux: per-resource namespaces&lt;/h3&gt;
&lt;p&gt;Linux ships eight namespace types, each governed by a &lt;code&gt;CLONE_NEW*&lt;/code&gt; flag passed to &lt;code&gt;clone()&lt;/code&gt;, &lt;code&gt;unshare()&lt;/code&gt;, or &lt;code&gt;setns()&lt;/code&gt;: mount, PID, network, IPC, user, UTS, cgroup, and time. The &lt;code&gt;namespaces(7)&lt;/code&gt; man page is precise: &quot;A namespace wraps a global system resource in an abstraction that makes it appear to the processes within the namespace that they have their own isolated instance of the global resource&quot; [@man7-namespaces]. Docker, containerd, runc, Kubernetes pods, LXC, and systemd-nspawn all compose these eight flags into a Linux container.&lt;/p&gt;
&lt;p&gt;The strength of the Linux design is per-class composability. A process can be in a fresh mount namespace, a fresh PID namespace, and the host&apos;s network namespace, all at once. The weakness is the absence of a unified type registry: Linux has no equivalent of &lt;code&gt;\ObjectTypes&lt;/code&gt;, no equivalent of the &lt;code&gt;OBJECT_HEADER&lt;/code&gt; reference counting that the kernel applies uniformly to every named object. Each resource class has its own lookup function, its own permission model, and its own ownership story. A bug in any one of them is bounded to that one resource class but is also not shared mitigation across the others.&lt;/p&gt;
&lt;h3&gt;7.2 macOS / Darwin: Mach ports and the bootstrap server&lt;/h3&gt;
&lt;p&gt;Darwin&apos;s kernel-object naming is capability-style. Apple&apos;s archive documentation describes the model directly: &quot;each task consists of a virtual address space, a port right namespace, and one or more threads&quot; [@apple-mach-kernel]. Tasks send messages by holding a &lt;em&gt;port right&lt;/em&gt; -- a per-task index into a kernel-managed table of Mach ports. There is no single hierarchical namespace; ports are sent over Mach messages, and &lt;code&gt;launchd&lt;/code&gt; operates as the bootstrap-server name broker for services that need a stable rendezvous. A separate I/O Registry tree carries device objects.&lt;/p&gt;
&lt;p&gt;The strength of the Mach design is that capabilities cannot be forged; you cannot synthesise a port right out of a string the way you can synthesise a path string under Windows. The weakness is the split namespace: device objects live in the I/O Registry, services live behind &lt;code&gt;launchd&lt;/code&gt;, and the kernel itself has no equivalent of &lt;code&gt;\BaseNamedObjects&lt;/code&gt; as a one-stop shop.&lt;/p&gt;
&lt;h3&gt;7.3 Plan 9 from Bell Labs&lt;/h3&gt;
&lt;p&gt;Plan 9 is the design lineage Cutler imported. In Plan 9, every named operating-system resource -- including processes, network connections, devices, and the window system -- surfaces as a path served over 9P. The single hierarchical namespace was the central claim. Plan 9 never reached commercial scale, but its design idea reached production in three places: NT (1993, via Cutler), Linux&apos;s /proc, /sys, and FUSE (the 1990s onward), and the various capability-OS research projects (KeyKOS, EROS, seL4) that took the lessons in a different direction.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Primitive&lt;/th&gt;
&lt;th&gt;Granularity&lt;/th&gt;
&lt;th&gt;Enforcement point&lt;/th&gt;
&lt;th&gt;Structural / opt-in&lt;/th&gt;
&lt;th&gt;Bypass by privilege&lt;/th&gt;
&lt;th&gt;Inheritance gap&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Per-Session (NT)&lt;/td&gt;
&lt;td&gt;Logon session&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ObpLookupObjectName&lt;/code&gt; + DACL&lt;/td&gt;
&lt;td&gt;Structural&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SeDebugPrivilege&lt;/code&gt; short-circuit&lt;/td&gt;
&lt;td&gt;Inherited handles cross sessions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AppContainer Lowbox (NT)&lt;/td&gt;
&lt;td&gt;Package SID&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ObpLookupObjectName&lt;/code&gt; rewrite&lt;/td&gt;
&lt;td&gt;Structural&lt;/td&gt;
&lt;td&gt;TCB privileges only&lt;/td&gt;
&lt;td&gt;Brokered handles enter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server Silo (NT)&lt;/td&gt;
&lt;td&gt;Container&lt;/td&gt;
&lt;td&gt;Process-&amp;gt;Silo indirection&lt;/td&gt;
&lt;td&gt;Structural&lt;/td&gt;
&lt;td&gt;KMCS-signed driver&lt;/td&gt;
&lt;td&gt;Host handles cross silos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VBS / IUM Trustlet (NT)&lt;/td&gt;
&lt;td&gt;Trust level (VTL)&lt;/td&gt;
&lt;td&gt;Hypervisor&lt;/td&gt;
&lt;td&gt;Structural&lt;/td&gt;
&lt;td&gt;Hypervisor compromise&lt;/td&gt;
&lt;td&gt;Cross-VTL ALPC only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mandatory Integrity Control (NT)&lt;/td&gt;
&lt;td&gt;IL band&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SeAccessCheckByType&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Opt-in (per-object SACL)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SeRelabelPrivilege&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inherited handles bypass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ObRegisterCallbacks&lt;/code&gt; (NT)&lt;/td&gt;
&lt;td&gt;Per-type, per-driver&lt;/td&gt;
&lt;td&gt;Object Manager pre-op callback&lt;/td&gt;
&lt;td&gt;Mediation, not partition&lt;/td&gt;
&lt;td&gt;KMCS-signed driver&lt;/td&gt;
&lt;td&gt;Inheritance bypasses callback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Private Namespace (NT)&lt;/td&gt;
&lt;td&gt;Boundary SID-list&lt;/td&gt;
&lt;td&gt;&lt;code&gt;NtCreatePrivateNamespace&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Structural&lt;/td&gt;
&lt;td&gt;All SIDs in caller&apos;s token&lt;/td&gt;
&lt;td&gt;Boundary-keyed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux Namespace&lt;/td&gt;
&lt;td&gt;Per-resource clone&lt;/td&gt;
&lt;td&gt;&lt;code&gt;setns&lt;/code&gt;/&lt;code&gt;unshare&lt;/code&gt;/&lt;code&gt;clone&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Structural&lt;/td&gt;
&lt;td&gt;&lt;code&gt;CAP_SYS_ADMIN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fork inherits namespace set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mach Port Right&lt;/td&gt;
&lt;td&gt;Per-task&lt;/td&gt;
&lt;td&gt;Capability check on send&lt;/td&gt;
&lt;td&gt;Structural (capabilities)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;host_priv&lt;/code&gt; / kernel&lt;/td&gt;
&lt;td&gt;Inherited rights on fork&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

The Object Manager namespace is not a filesystem. There is no disk persistence, no journal, no FAT or MFT, no inode allocator, no per-file DACL in the filesystem sense. Nothing under `\` survives a reboot. Files-on-disk, registry-keys-in-a-hive, and named pipes are leaves in the OM tree, but the actual filesystem implementation lives in NTFS / ReFS / ExFAT drivers reached through the `Device` type&apos;s parse procedure.&lt;p&gt;What the OM namespace &lt;em&gt;shares&lt;/em&gt; with filesystems is exactly three things: the path-walk algorithm (left-to-right, component-by-component, with one hash-table lookup per component), the per-directory hash table (analogous to the directory-entry hash filesystems use), and the per-object security descriptor (which the SRM enforces at the same point a filesystem would enforce its DACL).&lt;/p&gt;
&lt;p&gt;When you read or write the phrase &quot;Object Manager namespace,&quot; the metaphor that is doing real work is &quot;in-memory directory tree the kernel uses to find named objects,&quot; not &quot;filesystem in the disk-format sense.&quot;
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;

The Windows 2000-era `CreateRestrictedToken` primitive was the wrong layer in 2000 as a standalone sandboxing mechanism -- it could not partition the namespace; it only filtered the caller&apos;s SID set against per-object DACLs. Chromium revived it in 2008 as one of four cooperating layers, and that pattern is the canonical 2026 production sandbox shape. The Chromium design document captures the constraints: &quot;The Windows sandbox is a user-mode only sandbox. There are no special kernel mode drivers... The sandbox is provided as a static library that must be linked to both the broker and the target executables&quot; (Chromium Sandbox Design [@chromium-sandbox-md], FAQ [@chromium-sandbox-faq]).&lt;p&gt;The four layers compose pairwise-orthogonally. The token gates &lt;em&gt;which DACLs&lt;/em&gt; the renderer can satisfy at &lt;code&gt;SeAccessCheck&lt;/code&gt; time; the job object gates &lt;em&gt;which kernel API surface&lt;/em&gt; the renderer can call (UI exceptions, process creation, etc.); the integrity level gates &lt;em&gt;which writes&lt;/em&gt; the renderer can perform across MIC label boundaries; the AppContainer lowbox-rewrites &lt;em&gt;every named-object lookup&lt;/em&gt; into the per-package directory inside &lt;code&gt;ObpLookupObjectName&lt;/code&gt;. A handle that survives all four checks is the only object the renderer can usefully touch. The load-bearing header is &lt;code&gt;sandbox_policy.h&lt;/code&gt;, which declares &lt;code&gt;TargetConfig::SetTokenLevel(TokenLevel initial, TokenLevel lockdown)&lt;/code&gt;, &lt;code&gt;SetJobLevel&lt;/code&gt;, &lt;code&gt;SetIntegrityLevel&lt;/code&gt;, &lt;code&gt;SetDelayedIntegrityLevel&lt;/code&gt;, and &lt;code&gt;SetAppContainerSid&lt;/code&gt;, with one verbatim mutual-exclusion note: &quot;Using an initial token is not compatible with AppContainer&quot; [@chromium-sandbox-policy-h].&lt;/p&gt;
&lt;p&gt;This is the 2026 production sandbox shape every Chromium-based browser inherits (Edge, Chrome, Brave, Vivaldi, Opera), as do Electron-based apps like Visual Studio Code&apos;s renderer processes.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;

The cross-VTL ALPC ports through which a VTL0 process talks to a VTL1 trustlet are still located in VTL0&apos;s `\RPC Control`. An attacker who controls VTL0 can *send* messages to LsaIso even though they cannot *read* LsaIso&apos;s internal state. Oliver Lyak&apos;s December 2022 *Pass-the-Challenge* result is the canonical worked example ([GitHub: ly4k/PassTheChallenge](https://github.com/ly4k/PassTheChallenge)): the trustlet&apos;s pages are never read, but the trustlet&apos;s RPC output exfiltrates the secret. The lesson is that VTL1 isolation is a *page-level* read barrier, not a *protocol-level* containment property. The VBS Trustlets piece in this corpus carries the deeper walkthrough.
&lt;p&gt;Windows bet on one tree; Linux bet on eight clone-flag dimensions; Darwin bet on capability-style port-right tables. Each bet has theoretical limits. What are they?&lt;/p&gt;
&lt;h2&gt;8. What the namespace cannot do&lt;/h2&gt;
&lt;p&gt;The frame for this section comes from James P. Anderson&apos;s 1972 USAF technical report &lt;em&gt;Computer Security Technology Planning Study&lt;/em&gt; (ESD-TR-73-51), Section 4.1.1. Anderson is the named originator of the reference-monitor concept and of the four properties such a monitor must satisfy. Wikipedia preserves the modern acronym verbatim: the reference-validation mechanism must be &quot;&lt;strong&gt;N&lt;/strong&gt;on-bypassable... &lt;strong&gt;E&lt;/strong&gt;valuable... &lt;strong&gt;A&lt;/strong&gt;lways invoked... &lt;strong&gt;T&lt;/strong&gt;amper-proof,&quot; and &quot;according to Ross Anderson, the reference monitor concept was introduced by James Anderson in an influential 1972 paper&quot; [@wikipedia-reference-monitor]. The NIST CSRC mirror hosts the original PDF [@csrc-nist-ande72].&lt;/p&gt;
&lt;p&gt;Saltzer and Schroeder&apos;s 1975 paper &lt;em&gt;The Protection of Information in Computer Systems&lt;/em&gt; [@cs-virginia-saltzer-schroeder] added the &lt;em&gt;complete-mediation principle&lt;/em&gt; -- &quot;every access to every object must be checked for authority&quot; -- and seven other design principles the reference-validation mechanism must satisfy (economy of mechanism, fail-safe defaults, open design, separation of privilege, least privilege, least common mechanism, psychological acceptability).&lt;/p&gt;
&lt;p&gt;Map the Windows Object Manager against the four NEAT properties and the answer is uncomfortable. The namespace partially achieves two (Always-invoked and Tamper-proof), fails Non-bypassable outright, and falls one to two orders of magnitude short of Evaluable.&lt;/p&gt;
&lt;h3&gt;8.1 Always-invoked: provably gapped&lt;/h3&gt;
&lt;p&gt;The namespace achieves always-invoked for &lt;em&gt;name-based opens&lt;/em&gt;. Every &lt;code&gt;Nt*OpenObject*&lt;/code&gt; syscall walks &lt;code&gt;ObpLookupObjectName&lt;/code&gt;; there is no path that returns a handle to a named object without going through the lookup. But the namespace cannot achieve always-invoked for &lt;em&gt;handle inheritance&lt;/em&gt;. A child process inherits handles from &lt;code&gt;CreateProcess(bInheritHandles=TRUE)&lt;/code&gt; without going through the OM at all. The handles already exist in the parent&apos;s &lt;code&gt;HANDLE_TABLE&lt;/code&gt;; the kernel walks the parent&apos;s table, duplicates the entries into the child&apos;s table, and the child has live access. No name-lookup, no &lt;code&gt;ObRegisterCallbacks&lt;/code&gt; callback, no SRM check. As long as the OS API exposes handle inheritance -- and it is too deeply embedded in 33 years of shipping Windows code to remove -- the Object Manager cannot be the &lt;em&gt;sole&lt;/em&gt; reference monitor.&lt;/p&gt;
&lt;h3&gt;8.2 Tamper-proof: bounded, not absolute&lt;/h3&gt;
&lt;p&gt;The Object Manager runs in ring 0, under Kernel-Mode Code Signing (KMCS), and -- on machines with Virtualization-Based Security and Hypervisor-protected Code Integrity (HVCI) enabled -- inside a Hyper-V-enforced code-integrity policy. Any kernel-mode adversary who can load a driver bypasses the OM. KMCS and HVCI raise the cost; they do not eliminate the surface. The Bring-Your-Own-Vulnerable-Driver class of attacks (signed but exploitable drivers) is the running residual class, and the historical pattern is that one or two new vulnerable signed drivers surface every quarter.&lt;/p&gt;
&lt;h3&gt;8.3 Evaluable: provably above threshold&lt;/h3&gt;
&lt;p&gt;A small enough TCB can be machine-verified. The seL4 microkernel is the canonical demonstration: roughly 9,000 lines of C verified end-to-end against a formal specification (~11 person-years for initial functional correctness per Klein et al. SOSP 2009, and approximately 25 person-years for the full suite of subsequent proofs including information-flow and binary verification) [@sel4-project]. The Object Manager subsystem, the Security Reference Monitor, and the parse procedures the Object Manager delegates to (file-system drivers via &lt;code&gt;IopParseDevice&lt;/code&gt;; the registry via &lt;code&gt;CmpParseKey&lt;/code&gt;; ALPC; the I/O manager itself) collectively comprise tens of thousands of lines of C, putting the TCB for &quot;open a named object&quot; at one to two orders of magnitude above the verification threshold any current proof system can handle. The Object Manager is &lt;em&gt;not&lt;/em&gt; evaluable in the formal sense Anderson required.&lt;/p&gt;
&lt;h3&gt;8.4 Non-bypassable: the privilege short-circuit&lt;/h3&gt;
&lt;p&gt;A process holding &lt;code&gt;SeDebugPrivilege&lt;/code&gt; (or any privilege that grants &lt;code&gt;PROCESS_VM_*&lt;/code&gt; rights) can short-circuit per-directory ACLs. The privilege evaluation happens at &lt;code&gt;SeAccessCheck&lt;/code&gt; time, &lt;em&gt;after&lt;/em&gt; &lt;code&gt;ObpLookupObjectName&lt;/code&gt; has resolved the name. The Object Manager will resolve any path the privileged caller asks for; the gate fires, but it lets the call through. The namespace cannot defend against the holder of &lt;code&gt;SeDebugPrivilege&lt;/code&gt;. This is by design -- you want a debugger to be able to attach to anything -- but it is also the structural reason why &quot;lock down the namespace&quot; is not by itself a containment story.&lt;/p&gt;
&lt;h3&gt;8.5 What else the namespace cannot do&lt;/h3&gt;
&lt;p&gt;It cannot prevent in-process memory disclosure -- the Pass-the-Challenge limit covered in the Section 7 aside. It cannot defend against a malicious driver -- KMCS, HVCI, and WDAC gate driver load; the namespace itself trusts already-loaded drivers. It cannot eliminate time-of-check / time-of-use racing during a path walk; the walker walks components one at a time, and any reentrant call into the walker is a TOCTOU surface. The mitigation is per-call -- callers pass &lt;code&gt;OBJ_DONT_REPARSE&lt;/code&gt; on object-attributes, &lt;code&gt;FILE_FLAG_OPEN_REPARSE_POINT&lt;/code&gt; on file opens, or otherwise instruct the path-walker to refuse symbolic-link substitution -- not a structural property of the namespace.&lt;/p&gt;
&lt;h3&gt;8.6 The honest accounting&lt;/h3&gt;
&lt;p&gt;The Object Manager namespace is a &lt;em&gt;coordination&lt;/em&gt; mechanism, not a &lt;em&gt;containment&lt;/em&gt; mechanism. Containment is in the layers above: the session ID, the package SID, the integrity level, the silo ID, the VTL split. The namespace&apos;s job is to make those layers &lt;em&gt;enforceable&lt;/em&gt; by partitioning the path space so the bad open &lt;em&gt;cannot resolve to the privileged object&apos;s name&lt;/em&gt;. The layers above decide which partition the caller is in; the namespace&apos;s only job is &quot;given a path and a caller, find the object.&quot; Anderson 1972 names the &lt;em&gt;kernel mechanism&lt;/em&gt; (the reference-validation mechanism with NEAT properties); Saltzer-Schroeder 1975 names the &lt;em&gt;design principles&lt;/em&gt; the mechanism must satisfy. The Object Manager is the Windows realisation; it inherits both the strengths and the limits.&lt;/p&gt;

The namespace is a coordination mechanism, not a containment mechanism. The containment is in the layers above.
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The Object Manager is the coordination layer; the containment is in the partition primitives stacked on top (session ID, package SID, integrity level, silo ID, VTL). The namespace&apos;s only job is &quot;given a path and a caller, find the object.&quot; Every Windows security boundary is a parameter to that one job: a per-directory ACL, a token-keyed name rewrite, or a kernel callback registered against an &lt;code&gt;OBJECT_TYPE&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The provable gaps are real. What is the active research direction in 2026 -- where do attackers and defenders actually meet inside the namespace today?&lt;/p&gt;
&lt;h2&gt;9. Open problems in 2026&lt;/h2&gt;
&lt;p&gt;Five open problems sit in active research as of 2026.&lt;/p&gt;
&lt;h3&gt;9.1 Hash-bucket collision pressure&lt;/h3&gt;
&lt;p&gt;The 37-bucket constant has not changed since 1993. On a 2026 Windows 11 25H2 machine with several hundred MSIX packages, each owning an &lt;code&gt;\AppContainerNamedObjects\&amp;lt;package-sid&amp;gt;\&lt;/code&gt; subtree, average chain lengths inside &lt;code&gt;\Sessions\1\AppContainerNamedObjects&lt;/code&gt; exceed two and routinely run higher under load. The structural impact is small per-lookup (O(chain length) at each component), but it compounds across deep path walks and across the per-VM hot loops in &lt;code&gt;ObpLookupObjectName&lt;/code&gt;. Microsoft has not committed to a larger table or a different structure; the constant remains.&lt;/p&gt;
&lt;h3&gt;9.2 Cross-AppContainer object-directory privacy&lt;/h3&gt;
&lt;p&gt;Per-AppContainer isolation is the AppContainer model&apos;s promise; residual cross-package reads erode it. Forshaw&apos;s Project Zero work between 2017 and 2020 documents specific classes; Windows 11 25H2 DACLs are tighter than Windows 10 RTM, but the impersonation-mediated cases survive. The HackSys / CVE-2023-35359 family covered in Section 4.5 is the current realisation of the cross-AppContainer-plus-impersonation surface, and the same broader resource-planting taxonomy Forshaw described in the 2017 Named Pipe Secure Prefixes post [@tiraniddo-named-pipe-secure-prefixes] is still rediscovered every year.&lt;/p&gt;
&lt;h3&gt;9.3 Silo-escape via routines that ignore silo attachment&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;Siloscape&lt;/em&gt; (June 7, 2021) showed that &lt;code&gt;NtSetInformationSymbolicLink&lt;/code&gt; could retarget a silo-scoped symbolic link at a host-scoped path. Microsoft patched the specific function; the &lt;em&gt;class&lt;/em&gt; -- kernel routines whose path resolution does not honour &lt;code&gt;Process-&amp;gt;Silo-&amp;gt;RootDirectory&lt;/code&gt; -- remains open. Microsoft&apos;s long-standing position is that Server Silo is not a security boundary; Hyper-V Container is the security-boundary product. Container runtimes that depend on Server Silo for tenant isolation are knowingly running outside the supported boundary.&lt;/p&gt;
&lt;h3&gt;9.4 ObRegisterCallbacks erosion under HVCI&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ObRegisterCallbacks&lt;/code&gt; requires a KMCS-signed driver, and on HVCI-enabled machines the binary must additionally be HVCI-compatible. Microsoft has progressively raised the compatibility bar -- preventing unsigned drivers, banning common runtime-patching idioms, and tightening the W^X policy. EDR vendors depend on the surface staying open; if HVCI&apos;s compatibility bar ever excludes the EDR kernel driver pattern, the in-kernel callback layer is at risk. The CrowdStrike Falcon Sensor outage of July 2024 made the brittleness of in-kernel EDR a public conversation. Microsoft&apos;s &lt;em&gt;Defender for Endpoint&lt;/em&gt; and &lt;em&gt;EDR-on-Linux eBPF&lt;/em&gt; projects point at alternative-mediation futures, but in-kernel &lt;code&gt;ObRegisterCallbacks&lt;/code&gt; is still the primary credential-theft sensor.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; As attackers ship Hell&apos;s Gate / Halo&apos;s Gate / direct-syscall stubs to bypass userland EDR hooks, the kernel callback fires regardless. The arms race accordingly shifts to the &lt;em&gt;access-mask-strip vs. impersonate-trusted-parent-PID&lt;/em&gt; layer inside the kernel callback itself, with both sides racing to define the right pre-operation policy for &lt;code&gt;lsass.exe&lt;/code&gt; handle opens. Watch the Microsoft Security Response Center advisories and the EDR-vendor incident postmortems for the bleeding edge.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;9.5 Public benchmark vacuum&lt;/h3&gt;
&lt;p&gt;No peer-reviewed benchmark compares per-call namespace-lookup cost across the Windows Object Manager, Linux namespaces, and Mach ports. Choice of namespace design at the OS level is a multi-decade commitment; the absence of an empirical comparison forces architecture decisions on theoretical-only grounds. The Linux Kernel Test Robot, the Phoronix Test Suite, and various academic systems-conference benchmarks measure adjacent properties (filesystem-call latency, system-call vector cost), but none publishes head-to-head numbers on the named-object-lookup hot path. This is an open invitation to systems researchers.&lt;/p&gt;
&lt;p&gt;Five open problems is a research agenda, not a how-to. How do you actually look at this thing on your own machine?&lt;/p&gt;
&lt;h2&gt;10. Reading the namespace from a live system&lt;/h2&gt;
&lt;p&gt;Three tools cover the operational practice: Sysinternals WinObj, Forshaw&apos;s NtObjectManager PowerShell module, and WinDbg in kernel mode.&lt;/p&gt;
&lt;h3&gt;10.1 WinObj on a live system&lt;/h3&gt;
&lt;p&gt;Download &lt;code&gt;winobj.exe&lt;/code&gt; from Sysinternals [@ms-winobj] and run it as administrator. The left pane is the directory tree; the right pane shows the children of the selected directory with their object types. Navigate to &lt;code&gt;\Sessions\1\BaseNamedObjects&lt;/code&gt; and read off the named events and mutants every Win32 app in your interactive session has created. Navigate to &lt;code&gt;\Sessions\1\AppContainerNamedObjects&lt;/code&gt; and pick an &lt;code&gt;S-1-15-2-...&lt;/code&gt; directory; right-click, choose Properties, and read the security descriptor. You will see a single allow-ACE granting full access only to the package SID itself. That ACE is the entire AppContainer sandbox at the namespace layer.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; WinObj cannot traverse &lt;code&gt;\ObjectTypes&lt;/code&gt;, &lt;code&gt;\Security&lt;/code&gt;, or &lt;code&gt;\Sessions\0\&lt;/code&gt; without administrator rights. Without traversal, the enumerate fails silently and the tree looks empty. Always run elevated, and accept that the tool will show the kernel view, not a per-process view.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;10.2 NtObjectManager PowerShell&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;NtObjectManager&lt;/code&gt; is Forshaw&apos;s PowerShell module that exposes the Object Manager namespace through cmdlets (PowerShell Gallery [@powershellgallery-ntobjectmanager]; GitHub [@p0-sandbox-attacksurface-analysis-tools]). Install with &lt;code&gt;Install-Module NtObjectManager&lt;/code&gt;. Useful commands: &lt;code&gt;Get-ChildItem NtObject:\&lt;/code&gt; walks the root; &lt;code&gt;Get-NtType&lt;/code&gt; lists the registered &lt;code&gt;OBJECT_TYPE&lt;/code&gt; singletons; &lt;code&gt;Get-NtObject \BaseNamedObjects&lt;/code&gt; enumerates the global BNO; &lt;code&gt;Get-NtAlpcPort &apos;\RPC Control&apos;&lt;/code&gt; lists every LRPC endpoint on the machine. The module wraps the same NTDLL syscalls WinObj uses, but in a scripting surface that composes into automation.&lt;/p&gt;
&lt;h3&gt;10.3 WinDbg kernel session&lt;/h3&gt;
&lt;p&gt;In a kernel-mode WinDbg session attached to a target machine (or to a live local kernel via Microsoft&apos;s local-kernel debug mode), &lt;code&gt;!object \&lt;/code&gt; dumps the root directory and its children. &lt;code&gt;dt nt!_OBJECT_HEADER &amp;lt;addr&amp;gt;-30&lt;/code&gt; reads the header preceding any object&apos;s body (the offset 0x30 is the size of &lt;code&gt;OBJECT_HEADER&lt;/code&gt; on x64; subtract that from the body pointer to land on the header -- the field layout is documented in &lt;em&gt;Windows Internals 7th Edition* Chapter 8, Microsoft Press Store [@microsoftpressstore-wininternals7-part1]). `dx -r1 ((nt!_OBJECT_TYPE&lt;/em&gt;)nt!PsProcessType[0]).TypeInfo` walks the Process type&apos;s method table and lists all eight procedure pointers and the WaitObjectFlagOffset, including the parse procedure.&lt;/p&gt;
&lt;h3&gt;10.4 The EDR primitive: an ObRegisterCallbacks driver template&lt;/h3&gt;
&lt;p&gt;The minimal sketch of an in-kernel EDR sensor is four steps. Register an &lt;code&gt;OB_CALLBACK_REGISTRATION&lt;/code&gt; for &lt;code&gt;PsProcessType&lt;/code&gt; with &lt;code&gt;OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE&lt;/code&gt; [@ms-obregistercallbacks]. In the pre-operation callback, examine &lt;code&gt;OperationInformation-&amp;gt;Object&lt;/code&gt;, derive the target process&apos;s PID, and compare it against &lt;code&gt;lsass.exe&lt;/code&gt;. If it matches, strip credential-relevant access bits from &lt;code&gt;OperationInformation-&amp;gt;Parameters-&amp;gt;CreateHandleInformation.DesiredAccess&lt;/code&gt; (or duplicate-handle equivalent). The kernel grants the handle with the reduced rights, the attacker&apos;s &lt;code&gt;PROCESS_VM_READ&lt;/code&gt; is gone before the call returns, and the post-operation callback logs the attempt. The parallel API &lt;code&gt;PsSetCreateProcessNotifyRoutineEx&lt;/code&gt; [@ms-pssetcreateprocessnotifyroutineex] covers process creation, which is the other half of the EDR sensor surface.&lt;/p&gt;

sequenceDiagram
    participant A as Attacker process
    participant NT as nt!NtOpenProcess
    participant OM as Object Manager
    participant EDR as EDR Pre-Op Callback
    participant LSASS as lsass.exe (target)
    A-&amp;gt;&amp;gt;NT: NtOpenProcess(lsass PID, PROCESS_VM_READ | PROCESS_QUERY_INFORMATION)
    NT-&amp;gt;&amp;gt;OM: lookup PsProcessType, target by PID
    OM-&amp;gt;&amp;gt;EDR: fire pre-op callback (handle create)
    EDR-&amp;gt;&amp;gt;EDR: target == lsass.exe?
    EDR-&amp;gt;&amp;gt;EDR: strip PROCESS_VM_READ from DesiredAccess
    EDR--&amp;gt;&amp;gt;OM: granted = PROCESS_QUERY_LIMITED_INFORMATION
    OM--&amp;gt;&amp;gt;NT: HANDLE with reduced access
    NT--&amp;gt;&amp;gt;A: open succeeded (but useless rights)
&lt;p&gt;{`
const PROCESS_VM_READ                   = 0x0010;
const PROCESS_VM_WRITE                  = 0x0020;
const PROCESS_VM_OPERATION              = 0x0008;
const PROCESS_QUERY_INFORMATION         = 0x0400;
const PROCESS_QUERY_LIMITED_INFORMATION = 0x1000;
const PROCESS_CREATE_THREAD             = 0x0002;
const PROCESS_DUP_HANDLE                = 0x0040;&lt;/p&gt;
&lt;p&gt;function stripForLsass(desired) {
  const STRIPPED =
      PROCESS_VM_READ |
      PROCESS_VM_WRITE |
      PROCESS_VM_OPERATION |
      PROCESS_CREATE_THREAD |
      PROCESS_DUP_HANDLE |
      PROCESS_QUERY_INFORMATION;
  return desired &amp;amp; ~STRIPPED;
}&lt;/p&gt;
&lt;p&gt;const desired = PROCESS_VM_READ | PROCESS_QUERY_INFORMATION | PROCESS_DUP_HANDLE;
console.log(&apos;attacker asked for:&apos;, &apos;0x&apos; + desired.toString(16));
const granted = stripForLsass(desired) | PROCESS_QUERY_LIMITED_INFORMATION;
console.log(&apos;EDR pre-op granted:&apos;, &apos;0x&apos; + granted.toString(16));
`}&lt;/p&gt;

```c
OB_OPERATION_REGISTRATION op = {
    .ObjectType = PsProcessType,
    .Operations = OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE,
    .PreOperation = MyPreOp,
    .PostOperation = MyPostOp,
};
OB_CALLBACK_REGISTRATION reg = {
    .Version = OB_FLT_REGISTRATION_VERSION,
    .OperationRegistrationCount = 1,
    .Altitude = RTL_CONSTANT_STRING(L&quot;123456&quot;),
    .OperationRegistration = &amp;amp;op,
};
ObRegisterCallbacks(®, &amp;amp;g_handle);
```
The driver must be KMCS-signed (`IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY`) per the wdm.h documentation; an unsigned image returns `STATUS_ACCESS_DENIED` from `ObRegisterCallbacks`. Two drivers cannot pick the same Altitude; collisions return `STATUS_FLT_INSTANCE_ALTITUDE_COLLISION`.
&lt;p&gt;You can now read the namespace, register an EDR-style callback, and dump the type registry. What are the questions readers ask after they finish reading?&lt;/p&gt;
&lt;h2&gt;11. Frequently asked questions&lt;/h2&gt;


No. The registry is a separate Windows Executive subsystem implemented in `nt!Cm*`, with its own hive on-disk format and its own in-memory hive structures. It hooks into the Object Manager namespace through one and only one mechanism: the `Key` `OBJECT_TYPE` registers a `ParseProcedure` (`CmpParseKey`) that takes over path walking when the namespace walker reaches `\REGISTRY`. The registry is therefore a *consumer* of the Object Manager, but not part of the Object Manager.

Because `\BaseNamedObjects` is the *global* / `Global\`-prefixed-only view, distinct from the per-session BNO at `\Sessions\\BaseNamedObjects`. The Win32 `Local\` prefix routes through `kernel32!BaseGetNamedObjectDirectory` into the per-session BNO; `Global\` routes into the global one [@ms-termserv-kernel-object-namespaces]. Cross-session named-object coordination still needs the global view; per-session isolation lives in the per-session subtree.

Because the lowbox token attached to the UWP app&apos;s process tells `ObpLookupObjectName` to rewrite the path to `\Sessions\\AppContainerNamedObjects\\Global\Foo` before path walking. Two different UWP apps have two different package SIDs and therefore land on two different directories. The Win32 names look the same; the kernel resolves them to different objects.

`\??\C:` is the per-session DosDevices alias; if `C:` is not defined in the current session&apos;s `\??`, the walker falls through to `\GLOBAL??\C:`. `\GLOBAL??\C:` is the machine-wide DosDevices symbolic link to `\Device\HarddiskVolume*` -- the real on-disk volume object. The split matters because the per-session `\??` is where per-session drive-letter remappings (`net use X: \\server\share`, `subst Z: C:\foo`, `DefineDosDevice`) live, and the activation-context resolver class covered in Section 4.5 is the exploit family that lives at this boundary.

Several top-level directories have `Directory`-`TRAVERSE` ACLs that restrict to SYSTEM and the local Administrators group. Without traversal, the directory enumeration silently fails. `\ObjectTypes`, `\Security`, and `\Sessions\0\` are the directories users most often notice as &quot;missing&quot; when running unelevated.

By DACL plus loader-side validation. The directory grants `Directory`-`READ` to everyone but `Directory`-`WRITE` only to SYSTEM and TrustedInstaller. The `Section` objects inside are Authenticode-signed by Microsoft and validated at boot by `smss.exe`. The historical `DefineDosDevice` + `\??` symlink-plant bypass class survived until Windows 10 21H2 build 19044.1826 (July 2022), when an NTDLL patch closed it [@itm4n-the-end-of-ppldump].

`ObRegisterCallbacks` [@ms-obregistercallbacks] and `PsSetCreateProcessNotifyRoutineEx` [@ms-pssetcreateprocessnotifyroutineex] are both fully documented. The HVCI compatibility requirements, the KMCS attestation flow, and the exact policy interactions with Defender for Endpoint&apos;s tamper-protection layer are partly implementation-defined; EDR vendor engineering teams maintain private regression suites against successive Windows feature updates.

When two or more processes that don&apos;t share a session or package must coordinate over a securable directory keyed by a SID-list they agree on at design time. The boundary descriptor is the *agreement primitive*: the kernel requires every SID in the boundary to be in the caller&apos;s token. The namespace&apos;s `OBJECT_DIRECTORY` lives in `\BNOLINKS`, keyed by the alias-prefix string plus a hash of the boundary descriptor&apos;s SID-list (CreatePrivateNamespaceW [@ms-createprivatenamespacew]; Object Namespaces overview [@ms-object-namespaces]; native NtCreatePrivateNamespace [@ntdoc-ntcreateprivatenamespace] and OBJECT_BOUNDARY_DESCRIPTOR [@ntdoc-object-boundary-descriptor] signatures). From inside an AppContainer process the lookup is rewritten into the per-package subtree, so private namespaces are not a substitute for the `windows.applicationModel.*` brokered APIs when cross-package coordination is the goal.


A user-mode structure produced by `CreateBoundaryDescriptor` and populated with `AddSIDToBoundaryDescriptor` (plus the optional `CREATE_BOUNDARY_DESCRIPTOR_ADD_APPCONTAINER_SID` flag). Conceptually the descriptor is a SID-list that the caller and every other participant must share via their tokens. Kernel-side the structure is `OBJECT_BOUNDARY_DESCRIPTOR` (Version, Items, TotalSize, Flags). `NtCreatePrivateNamespace` materialises a directory in `\BNOLINKS` keyed by the `lpAliasPrefix` plus a hash of the boundary descriptor&apos;s SIDs.
&lt;h2&gt;12. Coming back to the WinObj screen&lt;/h2&gt;
&lt;p&gt;Open WinObj one more time. Navigate back to &lt;code&gt;\Sessions\1\AppContainerNamedObjects&lt;/code&gt; and pick the Edge renderer&apos;s &lt;code&gt;S-1-15-2-...&lt;/code&gt; directory. You can now name everything you are looking at. The directory is an &lt;code&gt;_OBJECT_DIRECTORY&lt;/code&gt; instance with 37 hash buckets. You reach it through a token-keyed rewrite that the kernel applies inside &lt;code&gt;ObpLookupObjectName&lt;/code&gt; before path walking begins. Its security descriptor grants &lt;code&gt;GenericAll&lt;/code&gt; only to the package SID. Every EDR loaded on this machine has registered an &lt;code&gt;ObRegisterCallbacks&lt;/code&gt; filter on &lt;code&gt;PsProcessType&lt;/code&gt;, watching for handle creations against &lt;code&gt;lsass.exe&lt;/code&gt;. If you are running on a Server SKU with Windows Server Containers, the directory might also be silo-scoped, with &lt;code&gt;Process-&amp;gt;Silo-&amp;gt;RootDirectory&lt;/code&gt; indirecting your view of the rest of &lt;code&gt;\&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The four pieces of the 1993 Cutler design have shipped without architectural change for thirty-three years. The six generations of partition primitives stacked on top are all simultaneously load-bearing on Windows 11 25H2. The namespace itself is a coordination mechanism, in Anderson 1972&apos;s sense of the reference-validation mechanism, with Saltzer-Schroeder 1975&apos;s complete-mediation principle as the design constraint it must satisfy. Containment lives in the partition layers above it: the session, the package, the integrity level, the silo, and the VTL split. Every other article in this corpus -- the Credential Guard piece, the AppContainer piece, the VBS Trustlets piece, the Hyper-V piece, the App Identity piece, the TPM piece -- quietly assumes this tree underneath them.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Every Windows security boundary is a path rewrite, a per-directory ACL, a token-keyed name substitution, or a kernel callback against an &lt;code&gt;OBJECT_TYPE&lt;/code&gt;. The Object Manager is the data structure underneath them all.&lt;/p&gt;
&lt;/blockquote&gt;

**Key terms.** Object Manager (`Ob`), `OBJECT_HEADER`, `OBJECT_TYPE`, `ParseProcedure`, `OBJECT_DIRECTORY`, Lowbox token, AppContainer, Server Silo, Trustlet / IUM, Boundary descriptor, Session 0 isolation, Mandatory Integrity Control, `ObRegisterCallbacks`, KMCS, HVCI, `\BaseNamedObjects`, `\Sessions\\AppContainerNamedObjects`, `\RPC Control`, `\KnownDlls`, `\BNOLINKS`, `\GLOBAL??`, `\??`.&lt;p&gt;&lt;strong&gt;Review questions.&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Why does AppContainer isolation work even when the calling UWP app explicitly asks for &lt;code&gt;Global\X&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;What is the relationship between &lt;code&gt;IopParseDevice&lt;/code&gt;, &lt;code&gt;\Device\HarddiskVolume1&lt;/code&gt;, and &lt;code&gt;IRP_MJ_CREATE&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Which of Anderson 1972&apos;s four NEAT properties does the Object Manager achieve cleanly, and which does it provably fail?&lt;/li&gt;
&lt;li&gt;Why is &lt;code&gt;ObRegisterCallbacks&lt;/code&gt; an enforcement gate only against handle creation and duplication, not against handle use?&lt;/li&gt;
&lt;li&gt;Why does the canonical MS15-090 OM-symlink CVE point at CVE-2015-2428 [@nvd-cve-2015-2428] rather than CVE-2015-2528 or CVE-2015-1463?&lt;/li&gt;
&lt;li&gt;What is the structural difference between &lt;code&gt;\??\C:&lt;/code&gt; and &lt;code&gt;\GLOBAL??\C:&lt;/code&gt;, and which one does the HackSys / CVE-2023-35359 worked example abuse?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Recommended reading.&lt;/strong&gt; Russinovich, Ionescu, and Solomon, &lt;em&gt;Windows Internals, Part 1&lt;/em&gt; (7th edition, Microsoft Press, 2017), Chapter 8 [@microsoftpressstore-wininternals7-part1]. James Forshaw, &lt;em&gt;Windows Security Internals&lt;/em&gt; (No Starch Press, 2024), Chapter 8 [@nostarch-windows-security-internals]. Alex Ionescu, &lt;em&gt;Battle of SKM and IUM&lt;/em&gt;, Black Hat USA 2015 [@ionescu-bh2015-pdf]. The Google Project Zero blog&apos;s symlink mitigations [@p0-symlink-mitigations], arbitrary directory creation [@p0-issue1550], and who contains the containers [@p0-who-contains-containers] posts. James P. Anderson, &lt;em&gt;Computer Security Technology Planning Study&lt;/em&gt; [@csrc-nist-ande72].
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
</content:encoded><category>windows-internals</category><category>object-manager</category><category>kernel</category><category>sandbox</category><category>appcontainer</category><category>security-boundaries</category><category>edr</category><category>vbs</category><author>noreply@paragmali.com (Parag Mali)</author></item></channel></rss>