Levels/Security & authentication/Protected Processes & PPL

Level 2 · Chapter 11

Protected Processes & PPL

A process-launch-time trust label that blocks even SYSTEM-level handle access — orthogonal to being an administrator, not an extension of it.

Every access check this level has covered so far — tokens, descriptors and ACLs, the SRM — answers the same question: does this caller's identity satisfy this object's policy? Process protection asks a different question, asked first, before any of that: is the caller itself trusted enough to touch this process at all? LSASS, SAM, and local security policy already mentioned that LSASS runs protected specifically to block credential-dumping techniques; this chapter is the mechanism that makes that true.

A label assigned at launch, not a policy attached to an object

A security descriptor is something an object carries and an access check consults. Process protection is different in kind: it's a trust level assigned to a process at creation time, derived from the signature on the image being launched, and it constrains what any other process — including one running as SYSTEM — can do to it afterward. There is no DACL involved in this decision at all.

Windows recognizes two tiers:

  • PP (Protected Process) — the stricter, older tier, originally built for DRM-sensitive media components that needed to be confident their decrypted content couldn't be read out of memory by anything else on the machine, no matter how privileged.
  • PPL (Protected Process Light) — a deliberately weaker variant introduced later, which relaxed the model enough that third parties (notably antimalware vendors) could participate without needing the same narrow trust Microsoft's own components get.

Both tiers carry a signer type — WinTcb, Windows, Lsa, Antimalware, CodeGeneration, and a few others — baked into the certificate that signed the image. The signer type is what actually gets compared when another process tries to open a handle to a protected one, not just the fact of being protected at all.

What actually happens on OpenProcess

When a caller requests a handle to a process, the kernel compares the caller's own protection level and signer against the target's. If the caller isn't at least as trusted a signer as the target requires, the sensitive rights the caller asked for — things like PROCESS_VM_READ and PROCESS_VM_WRITE, which are exactly what you'd need to read credential material out of memory — are silently stripped from the handle before it's returned. The call doesn't fail; it just succeeds with a handle that's much less useful than it looks. This is why tools and scripts that assume "the call succeeded, so I have access" get confusing, partial results against a protected target rather than a clean error.

Crucially, this check happens before anything this level has previously described — the SRM, the target's security descriptor, the caller's token SIDs — ever gets consulted for this purpose. An administrator's token can satisfy every DACL on the machine and still walk away with a crippled handle, because protection level isn't a DACL concept at all.

PPL's actual job: letting more parties in, without opening the door all the way

Full PP was workable for a small, Microsoft-controlled set of signers, but it was never going to scale to "any antimalware vendor can protect their own service process" — that would require trusting arbitrary third-party code with PP's strongest guarantees. PPL exists specifically to thread that needle: an antimalware-signed PPL process is protected from unprotected and lower-tier callers the same way a PP process is, but the Antimalware signer type is deliberately scoped so it doesn't gain the same standing as WinTcb against other protected processes. The result is a graded trust ladder rather than a single on/off switch, which is why you'll see some protected services on a running machine that a kernel debugger can still interact with in ways LSASS itself refuses.

A worked example: why credential-dumping tools need a driver

A well-known category of post-exploitation tooling works by opening a handle to lsass.exe and reading its memory to reconstruct cached credential material. On a machine where LSASS runs as PPL — the default on modern Windows — an ordinary OpenProcess(PROCESS_ALL_ACCESS, ...) call made from a fully elevated administrator context returns a handle with exactly the rights needed for that technique removed, regardless of how permissive the caller's token otherwise is. This is precisely why that entire category of tooling has historically needed to load its own signed kernel-mode driver: a kernel-mode caller can manipulate the target EPROCESS protection field directly, sidestepping the user-mode OpenProcess check entirely rather than defeating it. The mitigation doesn't claim to stop a fully privileged kernel-mode attacker — it specifically raises the cost from "one unprivileged-looking API call" to "ship and load a driver," which is a meaningfully different bar, and one that other defenses (code integrity, driver signing enforcement) are positioned to catch.

A common mistake

Treating process protection as "the same idea as being an administrator, just stricter" is backwards. UAC and MIC (from UAC, integrity levels, and the secure desktop) are about how privileged this particular execution context is, for a token that's still fundamentally the signed-in user's. Process protection doesn't care about the caller's privilege level at all — it cares about what signed the caller's own image. A SYSTEM-level service with no special signature is still just an unprotected caller as far as this check is concerned, and gets exactly the same stripped-down handle an ordinary standard user process would.

Where this connects