AMSI: The Pre-Execution Window Where Defender Catches a Base64 Payload It Has Never Seen Before
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.
Permalink1. A 200-Millisecond Story
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 Invoke-Expression of a 12-layer obfuscated stager whose final payload is Invoke-Mimikatz.
Two hundred milliseconds later, Microsoft Defender flags the deobfuscated string Invoke-Mimikatz 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.
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?
The answer is a seven-function Win32 API called the Antimalware Scan Interface [1], or AMSI, and it is the single most consequential malware-defense primitive Microsoft has shipped since Authenticode. 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.
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 [1][2][3].
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.
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's August 2020 disclosure of Defender's pair-of-classifiers architecture [4] describes "performance-optimized" 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.
2. Why Static AV Failed: 25 Years of the Obfuscation Arms Race
Consider a benign one-liner:
Write-Host 'pwnd!'
A signature on that exact byte string catches the lazy attacker, and only the lazy attacker. The next attacker writes:
Write-Host ('pwn' + 'd!')
The signature dies. So the defender starts emulating expression-evaluation; the attacker switches to Invoke-Expression of a concatenated string; the defender starts emulating Invoke-Expression; 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, "we're generally past what antivirus engines will emulate or detect, so we won't necessarily detect what this script is actually doing," and even where a defender writes a signature for an obfuscator's pattern, "a signature for it would generate an unacceptable number of false positives" [5].
The ladder was not theoretical. It was the operating reality of script-borne malware for 20 years.
In 1995, WM/Concept [6] 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 [7] 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.
By 2014, the surface had matured into a thriving offensive tradecraft: PowerSploit, PowerView, Invoke-Mimikatz, and the Empire C2 framework all ran fileless inside powershell.exe memory after deobfuscation. On-disk antivirus saw only the encoded wrapper, not the deobfuscated payload that actually ran.
Daniel Bohannon would close the file on signature-based defenses publicly at DerbyCon 6.0 on September 25, 2016 with Invoke-Obfuscation [8], a PowerShell obfuscator that automated the regress above and turned every public-script signature into a one-bug-away walking target. Bohannon'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.
The diagnosis that Holmes named in 2015 and that Bohannon proved a year later is structural. Detection must happen after deobfuscation (so the obfuscation does not hide the payload), before execution (so the detector can still refuse), and in the engine that did the deobfuscation (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.
3. The Pre-AMSI Patchwork
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.
Diagram source
gantt
title Pre-AMSI script-malware defense timeline
dateFormat YYYY-MM
axisFormat %Y
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 The first attempt was IOfficeAntiVirus, 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 Application.Run of strings decoded from cells, because the decoded string was never on disk. The Office 365 Threat Research team's 2018 retrospective on the limitation [9] is direct: file-open AV does not see what the VBA runtime decides to run at runtime.
The second attempt was PowerShell module logging, shipped in PowerShell 3.0 in 2012 [10] as Event ID 4103. It records, after the fact, that a cmdlet ran with a given parameter binding [11]. 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 Invoke-Expression's argument string, so it sees the call but not the payload.
The third attempt, shipped on July 29, 2015 alongside Windows 10 1507 and PowerShell 5.0, was Script Block Logging [11]. 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 what it sees, but the wrong relationship in terms of what it can do with what it sees: 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.
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 [11].
The fourth attempt was the antivirus industry'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 "fragile" 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.
Lee Holmes summarized the dead end in one sentence in his June 9, 2015 post: "antimalware software starts to do basic language emulation," but "this is a fairly fragile approach" [5]. The next paragraph in this article is the same paragraph in his.
4. The 2015 Eureka: Lee Holmes and the Birth of AMSI
On June 9, 2015, Lee Holmes published Windows 10 to Offer Application Developers New Malware Defenses [5] on the Microsoft Security Blog. It is the most important malware-defense blog post Microsoft has ever shipped. The same day, Holmes also published PowerShell the Blue Team [12], which named the assume-breach mindset that made AMSI's design possible.
The architectural fix Holmes named is the one the previous section'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 "malware," the application refuses to execute the buffer. The whole exchange happens synchronously, in the calling process, before the engine commits.
While the malicious script might go through several passes of deobfuscation, it ultimately needs to supply the scripting engine with plain, unobfuscated code.
-- Lee Holmes, Microsoft Security Blog, June 9, 2015
The same observation appears verbatim on the live Microsoft Learn how-amsi-helps page [13], which carries Holmes 2015's argument forward in Microsoft's current documentation: "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." The dual primary-source anchor makes the citation durable against future Wayback rot.
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's deobfuscation work is now the defender's free lunch.
The release vehicle was Windows 10 1507 on July 29, 2015, paired with PowerShell 5.0 [14]. The companion piece, "PowerShell the Blue Team" [12], framed the broader assume-breach posture: "What did they do? What systems did they connect to? Was any dynamic code invoked, and what was it?" 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 "PowerShell heart the Blue Team" 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 [12].
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 scan. Holmes pointed out that the answer was about when 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's process.
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.
5. The AMSI Architecture: Two API Surfaces, One Provider Model
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 amsi.dll, and amsi.dll runs in the calling process. Here is the full hot path for one PowerShell command.
Diagram source
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)
User->>PS: iex ([Convert]::FromBase64String($stager))
PS->>PS: tokenize, expand, deobfuscate
PS->>AU: ScanContent(buf, name, session)
AU->>AD: AmsiScanBuffer(ctx, buf, len, name, session, out result)
AD->>MP: IAntimalwareProvider::Scan(stream)
MP->>ME: local RPC: scan(stream)
ME-->>MP: AMSI_RESULT_DETECTED (>= 32768)
MP-->>AD: HRESULT S_OK, result set
AD-->>AU: AMSI_RESULT_DETECTED
AU-->>PS: AmsiResultIsMalware(result) == TRUE
PS-->>User: ParseException: script content is malicious 5.1 The Win32 flat-C API
The flat-C surface is seven functions, declared in amsi.h, exported from amsi.dll, with minimum support Windows 10 / Windows Server 2016 [15]. A host typically calls them in this order:
AmsiInitialize(LPCWSTR appName, HAMSICONTEXT *amsiContext)once at startup. TheappNamestring identifies the host: PowerShell passes"PowerShell_<GUID>", .NET passes"DotNet", Office passes its application name [16]. The string later surfaces in telemetry asDeviceEvents.AmsiProcessName.AmsiOpenSession(HAMSICONTEXT, HAMSISESSION *session)per logical user command. The session handle is a correlation primitive: multipleAmsiScanBuffercalls inside one session let the provider re-join partial deobfuscations into one decision [17].AmsiScanBuffer(ctx, buffer, length, contentName, session, &result)per buffer. This is the hot path.contentNameis a human-readable label the SOC analyst will see [15].AmsiResultIsMalware(result)to interpret the out parameter. The macro evaluates to non-zero when the AMSI_RESULT is at or above 32768 [18].AmsiCloseSessionto release the session handle.AmsiUninitializeat shutdown.
The seventh function, AmsiScanString, is a thin wrapper that takes a wide-character string instead of a buffer-plus-length pair. Microsoft replaced PowerShell's AmsiScanString call site with AmsiScanBuffer in Windows 10 1709 as part of the response to the first CyberArk in-memory patch attack [19]; we will return to that in §8.
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 [15].
The AMSI_RESULT enumeration is the interface contract for verdicts. The values are:
| Value | Name | Semantics |
|---|---|---|
| 0 | AMSI_RESULT_CLEAN | Known clean |
| 1 | AMSI_RESULT_NOT_DETECTED | Unknown but not malicious |
| 16384 (0x4000) | AMSI_RESULT_BLOCKED_BY_ADMIN_START | Policy block (range start) |
| 20479 (0x4FFF) | AMSI_RESULT_BLOCKED_BY_ADMIN_END | Policy block (range end) |
| 32768 (0x8000) | AMSI_RESULT_DETECTED | Provider verdict: malicious; AmsiResultIsMalware true |
Any return value at or above 32768 is malware; values 16384 to 20479 are administrative policy blocks (e.g. AppLocker / WDAC), and values 0 and 1 are negative results [20]. The split between 16384 and 32768 lets a host distinguish "the AV refused this" from "policy refused this," which lets the host display different error messages.
5.2 The COM surface
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's COM surface, IAmsiStream plus IAntimalwareProvider, lets the host hand a stream callback to the provider and lets the provider pull as much content as it wants [21]. The reference implementation is in Microsoft's Windows-classic-samples AmsiProvider [22] repository.
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.
5.3 The provider model
AMSI providers are in-process COM servers. Registration writes two registry trees [23]:
Diagram source
flowchart TD
A[Provider DLL: vendor implements IAntimalwareProvider] --> B[regsvr32 vendor.dll]
B --> C[HKLM Software Classes CLSID {CLSID} InprocServer32 = vendor.dll]
B --> D[HKLM Software Classes CLSID {CLSID} InprocServer32 ThreadingModel = Both]
B --> E[HKLM Software Microsoft AMSI Providers {CLSID} = present]
C --> F[amsi.dll AmsiInitialize]
D --> F
E --> F
F --> G[CoCreateInstance for each registered CLSID]
G --> H[Provider loaded in-process; called on every AmsiScanBuffer] The first tree, HKLM\SOFTWARE\Classes\CLSID\{CLSID}, is standard COM. It names the provider DLL and the ThreadingModel (which must be Both; marshaling proxies would defeat the in-process performance assumption). The second tree, HKLM\SOFTWARE\Microsoft\AMSI\Providers\{CLSID}, is the AMSI-specific opt-in. amsi.dll enumerates the Providers subkey at AmsiInitialize time, calls CoCreateInstance for each one in-process, and then calls each provider on every subsequent AmsiScanBuffer.
Two security mitigations have hardened that load over time. Windows 10 1709 (October 17, 2017) tightened the loader rules: provider DLLs must LoadLibrary their dependencies with full paths, or the DLL hijack mitigations will refuse to satisfy unqualified loads [23]. Windows 10 1903 (May 21, 2019) added an optional Authenticode signing check: when HKLM\SOFTWARE\Microsoft\AMSI\FeatureBits is set to 0x2, unsigned provider DLLs are refused [21].
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's process at AmsiInitialize time [23].
The Both 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 [23].
5.4 The default provider: MpOav.dll
Microsoft Defender's AMSI provider is MpOav.dll. CLSID {2781761E-28E0-4109-99FE-B9D127C57AFE}. Path %ProgramData%\Microsoft\Windows Defender\Platform\<version>\MpOav.dll [24]. It loads in-process to the scanning application: into powershell.exe, into winword.exe, into wscript.exe. It does not do the heavy lifting; it bridges out to MsMpEng.exe via local RPC for the signature engine, cloud reputation lookup, and the on-endpoint machine-learning model.
Microsoft Defender's AMSI provider DLL, located at %ProgramData%\Microsoft\Windows Defender\Platform\<version>\MpOav.dll. Loaded in-process to the scanning application; bridges to MsMpEng.exe via local RPC for the heavy-lifting scan [24].
MpOav.dll lives in the scanning host's address space (powershell.exe, winword.exe, ...), not in MsMpEng.exe. Defender's Protected Process Light hardening protects MsMpEng.exe's process, but it does not protect the AMSI provider DLL that gets loaded into PowerShell. That asymmetry is the basis of every in-process bypass in §8 [24].
5.5 Sessions, correlation, content names
The HAMSISESSION handle returned by AmsiOpenSession is the correlation primitive. If a single PowerShell command produces three deobfuscation steps that yield three AmsiScanBuffer calls, sharing one session across all three lets the provider join them: "I just saw a base64 alphabet, then a key-rotation pattern, then Invoke-Mimikatz. Verdict: malicious. Reason: the three together are the obfuscation chain." The session-shared verdict is more informative than any single buffer would be in isolation [17].
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 [17].
The contentName argument to AmsiScanBuffer is what the SOC analyst sees in DeviceEvents.FileName at hunt time. Hosts that pass a meaningful contentName (the script-block ID, the assembly'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 [25].
AMSI'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.
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.
6. The Call-Site Catalogue: Where AMSI Plugs Into Windows
AMSI shipped in amsi.dll in 2015, but amsi.dll exporting AmsiScanBuffer 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.
Diagram source
gantt
title AMSI integration by runtime
dateFormat YYYY-MM
axisFormat %Y
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 PowerShell 5.0 (July 29, 2015)
PowerShell is the reference integration. The PowerShell host calls System.Management.Automation.AmsiUtils.ScanContent, which (after a one-time check on the amsiInitFailed flag and a lazy AmsiInitialize) calls AmsiNativeMethods.AmsiScanBuffer on the deobfuscated script block [26]. The integration matches Holmes's design intent verbatim: the buffer handed to AMSI is the buffer the executor is about to run.
Windows Script Host (2015)
wscript.exe and cscript.exe, the hosts that ran ILOVEYOU in 2000, integrate AMSI in the same release vehicle as PowerShell 5.0 [1]. Every JScript or VBScript source goes through AmsiScanBuffer before WSH executes it, and runtime eval-style constructions (new ActiveXObject('WScript.Shell').Run(...) with a dynamically built command line) get scanned at the point where the runtime resolves them.
Office VBA (September 12, 2018)
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 -- Shell invocation, CreateObject("WScript.Shell"), Application.Run of a decoded string -- it halts the macro and flushes the circular buffer through AmsiScanBuffer [13].
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("WScript.Shell"), Shell(), a file-write API) fires. The provider sees the trigger plus its prior-API context, not just one isolated call [13].
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.
-- Microsoft Office 365 Threat Research, September 12, 2018
The Office team published the design in the September 12, 2018 announcement [9]. The architectural payoff: a provider sees not just one trigger call but the macro's prior-API context, which is what distinguishes Application.Run("notepad.exe") from Application.Run(<base64-decoded-PowerShell>).
.NET Framework 4.8 (April 2019)
The next gap was in-memory .NET. Assembly.Load(byte[]), the load path Cobalt Strike's execute-assembly command and Sliver'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: "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 Assembly.Load(byte[]), 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" [27].
WMI scripting (Windows 10 1903, May 2019)
WMI is, in the abstract, an RPC protocol and a query language, but it is also a code-execution surface (__EventConsumer persistence; Win32_Process.Create lateral movement). The 1903 [14] AMSI integration scans WMI scripting paths [3], closing the persistence pivot that had been a favorite of post-exploitation toolkits since 2012.
Excel 4.0 macros (March 3, 2021)
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 [2]. The Microsoft post enumerates the full AMSI host list as of 2021: "Office VBA macros; JScript; VBScript; PowerShell; WMI; Dynamically loaded .NET assemblies; MSHTA/Jscript9."
Windows 11 in-memory script scanning (2021+)
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: "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)" [3]. Living-Off-the-Land Binary (LOLBin) paths that bypassed the classic script-engine entry points have become a continuing focus of Defender's per-release AMSI extensions.
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's pane of glass.
7. AMSI Meets ETW: The Correlation Story
The architectural dichotomy fits in one sentence: AMSI is synchronous and can block; Event Tracing for Windows (ETW) 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 decisions. ETW is for correlation and survives in-process bypass.
Diagram source
flowchart LR
A[powershell.exe / winword.exe
scanning host] --> B[amsi.dll AmsiScanBuffer prologue]
B --> C[ETW provider 2A576B87-09A7-520E-C21A-4942F0271D67 emit event]
B --> D[MpOav.dll IAntimalwareProvider Scan]
D --> E[MsMpEng.exe verdict]
E --> F[result returned synchronously to host]
F --> G[host refuses or allows execution]
C --> H[Defender ATP DeviceEvents AmsiScriptDetection]
C --> I[Third-party EDR via Antimalware-PPL]
C --> J[Sysmon SilkETW Sealighter on-host] The ETW provider name is Microsoft-Antimalware-Scan-Interface and its GUID is {2A576B87-09A7-520E-C21A-4942F0271D67} [28]. It emits a structured event for every AmsiScanBuffer call. The event template has ten fields: session, scanStatus, scanResult, appname, contentname, contentsize, originalsize, content, hash, contentFiltered. The content field is the deobfuscated buffer that just got scanned. That is the basis of every downstream telemetry product.
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's appName, and the host'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 [28].
Defender's MsMpEng.exe 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.
In Microsoft Defender for Endpoint, the same event surfaces in the DeviceEvents table with ActionType == "AmsiScriptDetection", and the AmsiData column carries the deobfuscated content, AmsiPatchedTextInResult carries any provider-side rewriting, and AmsiProcessName carries the host's appName [25]. 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.
<p>DeviceEvents
| where ActionType == "AmsiScriptDetection"
| 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 > ago(7d)
| sort by Timestamp desc</p> Press Run to execute.
The query is adapted from Bert-JanP's AMSIScriptDetections.md hunting pack [29] and maps each detection to MITRE T1059.001 -- Command and Scripting Interpreter: PowerShell [30]. The shape of the join is the load-bearing part: AMSI gives you the what (the deobfuscated buffer), and DeviceProcessEvents gives you the how (the parent process and its command line). Together they are the full attack chain.
The ETW provider runs from inside AmsiScanBuffer'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 result still leaks ETW telemetry: the prologue emit happens before the breakpoint fires. The provider sees the scan happened; only the verdict is muted [31].
AMSI hands out the deobfuscated buffer; ETW makes sure someone saw it happen. The attacker's job for the next seven years was to make neither happen. Here is how that went.
8. The Bypass Arms Race: Six Eras in Nearly Seven Years
In seven years, attackers have generated six distinct bypass eras. Each era was the necessary consequence of AMSI's same-process trust model. Each era's defeat by Defender required a new architectural insight, not a new signature. Here is the bird's-eye view.
| Era | First public | Attacker / source | Technique | Defender response |
|---|---|---|---|---|
| 1 | May 2016 | Matt Graeber (tweet) | AmsiUtils.amsiInitFailed = true via reflection | String signature on field-and-class proximity |
| 2 | February 2018 | Avi Gimpel and Zeev Ben Porat (CyberArk Labs) | In-memory patch of AmsiScanString; May 2018 redux patches AmsiScanBuffer (xor edi, edi) | Windows 10 1709 swaps to AmsiScanBuffer; byte-pattern signature on common patches |
| 3 | 2018-2019 | Various practitioners | Obfuscated patches: hashed GetProcAddress, indirect writes, randomized bytes | Behavior signature: detect any write into amsi.dll code section |
| 4 | 2019 | Various practitioners | Register a malicious CLSID under HKLM\...\AMSI\Providers | Windows 10 1709 DLL-hijack mitigation; 1903 Authenticode signing |
| 5 | January 29, 2020 | Cornelis de Plaa (Outflank) | DR0 hardware breakpoint plus Vectored Exception Handler; no memory patch | Microsoft-Windows-Kernel-Audit-API-Calls ETW NtSetContextThread event (admin-side, no PPL gate); Microsoft-Windows-Threat-Intelligence on the remote-thread subset |
| 6 | December 2022 | Practical Security Analytics | Patch CLR-side Assembly.Load(byte[]) AMSI callout in clr.dll; bypass at managed boundary | Signature on the CLR-side patch; Microsoft-Windows-DotNETRuntime ETW correlation |
Era 1: amsiInitFailed (May 2016)
The first public AMSI bypass is one line. Matt Graeber posted it as a tweet in May 2016; Sophos's June 2021 retrospective preserves the date and the tweet body for readers who cannot reach X: "In May of 2016, PowerShell hacker Matt Graeber published a one-line AMSI evasion in a tweet" [32]. MDSec's June 2018 walk-through reproduces the technique and its decompiled target:
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils'). `
GetField('amsiInitFailed','NonPublic,Static'). `
SetValue($null, $true)
Why does that work? Because the host's AmsiUtils.ScanContent checks a private static boolean before doing anything. If the host's own managed state says "AMSI failed to initialize," the host short-circuits every subsequent scan to "clean." MDSec's analysis quotes the field declaration verbatim: "What we are interested in is the variable amsiInitFailed, which is defined as: private static bool amsiInitFailed = false" [33].
The structural lesson is that AMSI's trust model includes the script engine's own managed state. PowerShell trusts itself. If you can flip a field in PowerShell's own memory, you have flipped PowerShell's trust in AMSI. The short-circuit logic looks like this:
// Decompiled sketch of System.Management.Automation.AmsiUtils.ScanContent.
// Source: Practical Security Analytics decompile of the PowerShell host.
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);
} Press Run to execute.
Defender's response was a string signature on amsiInitFailed plus class-name proximity. Sophos reports that "the amsiInitFailed bypass still accounts for about 1 percent of detections, based on a 90-day chunk of telemetry data from February to May of 2021" [32]. The bypass is detected almost everywhere it appears, yet it still appears, because some commodity actor still ships the literal string verbatim.
Era 2: In-memory AmsiScanString / AmsiScanBuffer patch (February 2018)
Avi Gimpel and Zeev Ben Porat at CyberArk Labs published the first canonical in-memory native-code patch in February 2018 [34]. The technique: LoadLibrary("amsi.dll"), GetProcAddress("AmsiScanString"), VirtualProtect the page to read-write-execute, overwrite the function prologue with mov eax, E_INVALIDARG; ret, restore the protection. Every subsequent AmsiScanString returns E_INVALIDARG without ever talking to the provider.
Microsoft's 1709 response was to change which function PowerShell called. Instead of AmsiScanString, PowerShell 5.0 in 1709 calls AmsiScanBuffer, breaking the Gimpel and Ben Porat patch outright [19]. Gimpel and Ben Porat published the redux in May 2018: target the new function with a smaller, semantically equivalent patch, xor edi, edi. The instruction zeros the buffer-length register, which makes AmsiScanBuffer think the host handed it an empty buffer and return AMSI_RESULT_CLEAN [19].
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 "Microsoft Security Response Center (MSRC) Response" in their May 2018 redux:
"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." -- Microsoft Security Response Center, quoted in CyberArk Labs (May 2018 redux) [19].
Era 3: Obfuscated patches (2018-2019)
Defender's first response was a byte-pattern signature on the common patches. Practitioners replied by randomizing the patch bytes, resolving AmsiScanBuffer 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 the write itself: any RWX write into amsi.dll's code section, regardless of byte pattern, is suspicious. Trend Micro's bypass-techniques retrospective lists the era's techniques side by side: "Obfuscation and/or encryption; PowerShell downgrade; Hooks and unhooks; Memory patching; Forcing an error; Registry modifications; DLL hijacking; Reflection" [35].
Era 4: Provider COM-hijack (2019)
A different attack class: register a malicious CLSID under HKLM\SOFTWARE\Microsoft\AMSI\Providers, write your own DLL into the standard COM tree, and amsi.dll will dutifully load it in-process at AmsiInitialize time [24]. Your provider then returns AMSI_RESULT_CLEAN 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.
The defender response was Windows 10 1709's DLL-hijack mitigation (provider DLLs that did not load their dependencies with full paths refused to load) and Windows 10 1903's optional Authenticode signing requirement. With HKLM\SOFTWARE\Microsoft\AMSI\FeatureBits = 0x2, an unsigned provider refuses to load entirely [21].
Era 5: Hardware-breakpoint VEH bypass (January 29, 2020)
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 amsi.dll 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) [31], CrowdStrike (2024) [36], and Trend Micro (December 2022) [35]. Steps:
AddVectoredExceptionHandler(1, exceptionHandler)-- register a vectored exception handler.- Set
DR0to the address ofAmsiScanBufferviaSetThreadContext. TheDR0-DR3registers are CPU hardware breakpoints; the processor raisesEXCEPTION_SINGLE_STEPthe moment instruction pointer hits the breakpoint address. - When the breakpoint fires, the exception handler executes. It sets
RAX = AMSI_RESULT_CLEAN, advancesRIPpast the function body, and returnsEXCEPTION_CONTINUE_EXECUTION.
There is zero static artifact in amsi.dll. The code section is untouched. Every signature that depends on a write into amsi.dll is dead. EthicalChaos reproduces the technique mechanics verbatim two years later: "the idea will be to register a vectored exception handler then set a breakpoint on a function within amsi.dll ... AddVectoredExceptionHandler(1, exceptionHandler) ... SetThreadContext((HANDLE)-2, &threadCtx)" [31].
The original Outflank 2020-01-29 blog post (outflank.nl, "Bypassing AMSI by manipulating the AMSI scan results") 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) [31], CrowdStrike (2024) [36], and Trend Micro (December 2022) [35].
The Defender response is the one this article keeps circling back to: kernel-side ETW. The hardware-breakpoint bypass calls SetThreadContext to write to DR0. The Microsoft-Windows-Threat-Intelligence (EtwTi) provider's NtSetContextThread event covers remote-thread context writes, but in-thread context writes (which is what the patchless bypass performs) are more reliably caught by Microsoft-Windows-Kernel-Audit-API-Calls, the provider CrowdStrike documents as its primary detection path. CrowdStrike's writeup gives the framing: "the DR0-DR3 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&CK framework (T1562.001)" [36] -- and MITRE's T1562.001 redirect [37] now sends readers to T1685 [38], the unified "Impair Defenses: Disable or Modify Tools" technique. The catch: EtwTi is gated on Antimalware-PPL consumers. Defender's MsMpEng.exe and a small set of onboarded third-party EDRs see it. Non-PPL products do not.
Antimalware Protected Process Light. A Windows signer level (introduced in Windows 8.1, hardened in Windows 10) that lets Defender'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.
Era 6: CLR-DLL load-time patch (December 2022)
If patching amsi.dll directly is signature-rich, patch something further up the call chain. Practical Security Analytics published the technique in December 2022 [26]: patch the .NET CLR's own AMSI callout (the managed-side wrapper inside clr.dll that calls AmsiScanBuffer on Assembly.Load(byte[])) rather than amsi.dll itself. The technique "has an advantage over other API Call Hooking techniques that target native functions such as AMSI.dll::AmsiScanBuffer in that this method is more difficult to prevent with EDR or Application Protection rules" -- the patched bytes live in clr.dll, not amsi.dll, and many defender rules only watch the latter.
The defender response was twofold: signature on the CLR-side patch bytes, and correlation against the Microsoft-Windows-DotNETRuntime ETW provider. The DotNETRuntime provider emits an AssemblyLoadFinished event for every Assembly.Load call. If the CLR-side AMSI callout has been muted, the load event fires anyway, and Defender now has a DotNETRuntime event with no corresponding AmsiScanBuffer event in the prior microseconds. That gap is the signal.
Era 7: The behavioral era (2023+)
By 2023, the bypass families had grown beyond enumeration. Microsoft's response was structural: stop trying to enumerate bypass techniques, and start scoring the gap. Defender's machine-learning models, described in the August 2020 disclosure on pairs-of-classifiers [4], feed not just on the content of AMSI events but on the cadence of AMSI events per process. A powershell.exe that has been alive for 90 seconds, run 14 commands, and emitted zero AmsiScriptDetection 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 "detect the bypass" but "notice that scanning has stopped."
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.
9. What AMSI Is Not: The MSRC Boundary Position
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 "MSRC Response" heading of their May 2018 redux is the most important single sentence in the AMSI corpus:
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.
-- Microsoft Security Response Center, quoted in CyberArk Labs (May 2018 redux)
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: "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" [39]. AMSI is published as neither a boundary nor a feature in that taxonomy. Bypasses of AMSI are not security bugs in MSRC's published framework. They get fixed when Microsoft can fix them. They do not get CVEs.
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.
So why is AMSI valuable anyway?
Diagram source
flowchart TD
A[AMSI same-process trust model] --> B{Attacker has code execution in the host?}
B -- No, the attacker is delivering an unprivileged script --> C[AMSI scans the deobfuscated buffer
provider returns DETECTED
host refuses to run]
B -- Yes, the attacker has unrestricted code execution --> D[AMSI scan is mutable in-process]
D --> E[ETW provider 2A576B87 emits from inside the prologue]
E --> F[Defender / EDR sees the scan happened; bypass leaks telemetry]
D --> G[Defender's behavioral cohort scoring]
G --> H[Gap detection: process emitted 0 AmsiData events; cohort expects ~6]
C --> I[AMSI as synchronous gate: WIN]
F --> J[AMSI bypass leaves ETW fingerprint: WIN]
H --> K[Behavioral gap detection: WIN] There are two trust assumptions, and both hold for most real-world attacks. The first is that the attacker is unprivileged: 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's synchronous gate beats them. The second is that Defender's ETW telemetry of AMSI scans, including the gaps in those scans, survives the bypass. Even when an in-process bypass mutes the synchronous return, the ETW provider's prologue emit still fires, and behavioral cohort scoring still notices the missing events. AMSI bypasses leak. Defender's win condition is that the leak is enough.
Why can AMSI not be moved into a VBS Trustlet (the isolated, kernel-attested user-mode environment that Hyper-V'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 [40]). AMSI is on the hot path of every script statement: PowerShell calls AmsiScanBuffer per command, Office VBA calls it per trigger, .NET calls it per Assembly.Load. 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.
Why can AMSI not be moved out-of-process to a broker? Same answer: the broker'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.
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?
10. Open Problems: The 2026 Frontier
Fred Cohen proved in 1984 that general virus detection is undecidable: "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" [43]. AMSI does not try to solve Cohen'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.
AMSI does not try to solve the undecidable problem of "is this program malicious?". It solves a tractable adjacent problem: "does this deobfuscated buffer match patterns we have seen?". The first is theoretically impossible (Cohen 1984). The second is the only thing that has ever scaled.
The empirical upper bound on the second problem is now known. Danny Hendler, Shay Kels, and Amir Rubin's 2020 ACM AsiaCCS paper, Detecting Malicious PowerShell Commands using Deep Neural Networks [44], reports on AMSI-collected PowerShell: "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%." The arXiv preprint carries the same headline figures [45]. 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's lower bound on the general problem means perfect is not on offer; the question is what fraction of the residual 10 percent the next ten years close.
Diagram source
flowchart TD
A[AMSI in 2026: open problems]
A --> B[Patchless bypass detection without PPL]
A --> C[Non-Microsoft script runtimes Python Node Ruby]
A --> D[AMSI for AI-runtime LLM-generated code]
A --> E[Cross-runtime correlation single chain]
A --> F[IAmsiStream adoption beyond scripts]
A --> G[AMSI on Linux macOS especially dotnet]
B -.user-mode-only detection requires polling.-> B1[Open: no general solution]
C -.PEP-578 audit hooks architecturally similar.-> C1[Open: no production bridge]
D -.does content-scan even apply to LLM output.-> D1[Open: design problem]
E -.no correlation_id joins macro-PowerShell-dotnet.-> E1[Open: per-host-app scope]
F -.designed for adoption but adoption thin.-> F1[Open: market problem]
G -.no shared script-engine host model.-> G1[Open: platform problem] Open problem 1: patchless hardware-breakpoint bypass on unprivileged user-mode EDR. The Outflank 2020 technique still works against EDR products that lack any kernel-side ETW consumer for thread-context writes [36]. CrowdStrike's recommended detector, Microsoft-Windows-Kernel-Audit-API-Calls, is available to admin-side consumers without an Antimalware-PPL gate; Microsoft-Windows-Threat-Intelligence 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'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.
Open problem 2: non-Microsoft script runtimes. Python, Node.js, Ruby, Lua, and the JavaScript hosts embedded in WebView2 are all script-execution surfaces that AMSI does not see. Python'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's contract assumes a host that has a clear "about to execute deobfuscated content" moment; not every runtime presents that moment to the OS in a way an external provider can intercept.
Open problem 3: AMSI for AI-runtime / LLM-generated code. When Copilot or AutoGen agents generate code that an automated runner executes, is AmsiScanBuffer 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 "policy at agent-execution time," may be the right model.
Open problem 4: cross-runtime correlation. 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 HAMSISESSION, but no single correlation_id joins Office VBA'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.
Open problem 5: IAmsiStream adoption beyond script engines. IAmsiStream 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.
Open problem 6: AMSI on Linux and macOS. PowerShell 7 runs on Linux. .NET runs on Linux. The same Assembly.Load(byte[]) attack surface that drove .NET 4.8'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 amsi.dll 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's path forward is uncertain.
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.
11. Practical Guide: For Four Roles on Monday Morning
The rest of this section is the action-oriented closing. One numbered subsection per audience. Skip to the one that applies to you.
11.1 For an application developer
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.
<h1 id="pseudocode-against-the-win32-flat-c-amsi-surface-in-amsidll">Pseudocode against the Win32 flat-C AMSI surface in amsi.dll.<a class="heading-anchor" aria-label="Link to this heading" href="#pseudocode-against-the-win32-flat-c-amsi-surface-in-amsidll"><span class="heading-anchor-icon" aria-hidden="true">#</span></a></h1><h1 id="a-real-implementation-would-use-c-or-rust-with-the-actual-amsih">A real implementation would use C++ or Rust with the actual amsi.h<a class="heading-anchor" aria-label="Link to this heading" href="#a-real-implementation-would-use-c-or-rust-with-the-actual-amsih"><span class="heading-anchor-icon" aria-hidden="true">#</span></a></h1><h1 id="types-the-lifecycle-and-error-handling-are-the-load-bearing-parts">types. The lifecycle and error handling are the load-bearing parts.<a class="heading-anchor" aria-label="Link to this heading" href="#types-the-lifecycle-and-error-handling-are-the-load-bearing-parts"><span class="heading-anchor-icon" aria-hidden="true">#</span></a></h1><p>amsi = ctypes.WinDLL("amsi.dll")</p><h1 id="1-once-at-startup-appname-is-what-shows-up-in-deviceeventsamsiprocessname">1. Once at startup. appName is what shows up in DeviceEvents.AmsiProcessName.<a class="heading-anchor" aria-label="Link to this heading" href="#1-once-at-startup-appname-is-what-shows-up-in-deviceeventsamsiprocessname"><span class="heading-anchor-icon" aria-hidden="true">#</span></a></h1><p>ctx = HAMSICONTEXT()
hr = amsi.AmsiInitialize("MyApp_v3.2", byref(ctx))
if hr != S_OK:
raise OSError(hr)</p><p>try:</p><h1 id="2-once-per-logical-user-command-not-once-per-buffer">2. Once per logical user command (NOT once per buffer).<a class="heading-anchor" aria-label="Link to this heading" href="#2-once-per-logical-user-command-not-once-per-buffer"><span class="heading-anchor-icon" aria-hidden="true">#</span></a></h1><p>session = HAMSISESSION()
hr = amsi.AmsiOpenSession(ctx, byref(session))
if hr != S_OK:
raise OSError(hr)</p><p>try:</p><h1 id="3-once-per-buffer-the-contentname-is-what-the-soc-analyst-sees">3. Once per buffer. The contentName is what the SOC analyst sees.<a class="heading-anchor" aria-label="Link to this heading" href="#3-once-per-buffer-the-contentname-is-what-the-soc-analyst-sees"><span class="heading-anchor-icon" aria-hidden="true">#</span></a></h1><p>result = AMSI_RESULT()
hr = amsi.AmsiScanBuffer(
ctx, buffer, len(buffer), "user-script.ps1", session, byref(result))
if hr != S_OK:
raise OSError(hr)</p><h1 id="4-interpret-the-verdict">4. Interpret the verdict.<a class="heading-anchor" aria-label="Link to this heading" href="#4-interpret-the-verdict"><span class="heading-anchor-icon" aria-hidden="true">#</span></a></h1><p>if amsi.AmsiResultIsMalware(result):
raise SecurityException("AMSI flagged the content as malicious")</p><p>finally:</p><h1 id="5-always-close-the-session">5. Always close the session.<a class="heading-anchor" aria-label="Link to this heading" href="#5-always-close-the-session"><span class="heading-anchor-icon" aria-hidden="true">#</span></a></h1><p>amsi.AmsiCloseSession(ctx, session)</p><p>finally:</p><h1 id="6-always-uninitialize-at-shutdown">6. Always uninitialize at shutdown.<a class="heading-anchor" aria-label="Link to this heading" href="#6-always-uninitialize-at-shutdown"><span class="heading-anchor-icon" aria-hidden="true">#</span></a></h1><p>amsi.AmsiUninitialize(ctx)</p> Press Run to execute.
The four common bugs to avoid: forgetting AmsiUninitialize (the handle leaks until the process dies); sharing one HAMSISESSION across threads (the correlation breaks and the provider sees one giant interleaved logical command); ignoring AMSI_RESULT_DETECTED (defeats the entire point of integrating); and passing a meaningless contentName (every SOC analyst hunting your application will quietly curse you).
11.2 For an AV or EDR vendor implementing a provider
If you are an AV vendor, the Microsoft Windows-classic-samples AmsiProvider [22] is your starting point. The skeleton: DllRegisterServer writes the two registry trees (the CLSID tree under HKLM\SOFTWARE\Classes\CLSID and the AMSI opt-in tree under HKLM\SOFTWARE\Microsoft\AMSI\Providers); the IClassFactory boilerplate creates an instance; IAntimalwareProvider::Scan consumes the IAmsiStream and bridges it to your scan engine [23].
Three operational gotchas that have bitten every vendor at least once:
- Load dependencies with full paths. Windows 10 1709's DLL-hijack mitigation refuses unqualified
LoadLibrarycalls from AMSI provider DLLs. Use full paths for every secondary DLL your provider loads [23]. - Authenticode-sign your provider. Windows 10 1903's optional signing check at
HKLM\SOFTWARE\Microsoft\AMSI\FeatureBits = 0x2refuses unsigned providers. Many enterprise customers set that bit by policy. - ThreadingModel must be
Both. Marshaling proxies break the in-process performance assumption.
11.3 For a detection engineer
The two-pronged hunt: query the cloud telemetry (DeviceEvents in Defender XDR) for the wide net, and run an on-host ETW consumer (SilkETW or Sealighter against GUID {2A576B87-09A7-520E-C21A-4942F0271D67}) 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 AMSIScriptDetections.md hunting pack [29].
Bonus rule: deploy a gap-detection alert. "Any powershell.exe process alive longer than 60 seconds with more than five ProcessCommandLine entries and zero AmsiScriptDetection events" is a high-signal pattern across every bypass family in §8, including the patchless ones. It does not detect the bypass; it detects the result of the bypass, which is silence where there should be sound.
11.4 For a red-team operator
The viability of each bypass family in 2026 against fully-patched Windows 11 23H2 with Defender for Endpoint and a PPL-onboarded EDR is:
AmsiUtils.amsiInitFailed: 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 [32].- In-process
AmsiScanBufferpatch: dead. Byte-pattern signature plus behavior signature on writes toamsi.dll. - Provider COM-hijack: dead. 1903 signing requirement plus 1709 DLL-hijack mitigation.
- Hardware-breakpoint VEH (Outflank 2020 family): generates
Microsoft-Windows-Kernel-Audit-API-CallsNtSetContextThreadevents to any admin-side ETW consumer; the stricterMicrosoft-Windows-Threat-Intelligenceevent fires only on remote-thread writes, so the in-thread variant is invisible to EtwTi but visible without PPL [36]. - CLR-DLL patch (Practical Security Analytics, 2021): niche; the
Microsoft-Windows-DotNETRuntimeETW correlation closes most variants.
Optional aside: red-team telemetry math on a high-assurance target
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 "I bypassed AMSI" cost is one event; the "I bypassed AMSI invisibly" 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.
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.
12. FAQ
Frequently asked questions
Is AMSI a security boundary?
No. Per Microsoft's published Windows Security Servicing Criteria [39], AMSI is not classified as a security boundary, which means AMSI-bypass bugs are not serviced as security vulnerabilities. The Microsoft Security Response Center's response to CyberArk Labs (reproduced in the May 2018 redux disclosure [19]) is verbatim: "the AMSI was not designed to prevent such attacks." See §9 for the architectural reasoning.
Does the Protected Process Light hardening on MsMpEng.exe mean my AMSI scans are PPL-protected?
No. MpOav.dll loads into the calling process (powershell.exe, winword.exe, wscript.exe), not into MsMpEng.exe. The PPL hardening protects MsMpEng.exe's process from tampering, but it does not extend to the AMSI provider DLL that gets loaded into the script host's memory space [24].
Why can a non-admin disable AMSI in their own PowerShell session?
Because AMSI'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's own memory (Era 2) or flip the host'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 [33].
Does the AMSI provider see the encrypted command line or the decrypted one?
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 [5].
Does AMSI catch all .NET reflection?
No. AMSI catches Assembly.Load(byte[]) since .NET Framework 4.8 (April 2019) [27]. It does not catch DynamicMethod [46] 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 "Reflection" bypass family Trend Micro catalogues separately from the in-memory AmsiScanBuffer patch family [35].
Why doesn't AMSI exist on Linux or macOS?
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.
What is the difference between AMSI and ETW for AMSI?
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) [28].
References
- (2024). Antimalware Scan Interface (AMSI) - Win32 apps. https://learn.microsoft.com/en-us/windows/win32/amsi/antimalware-scan-interface-portal - Canonical Microsoft Learn portal page for AMSI; integrated host list (PowerShell, WSH, Office VBA, UAC); Authenticode-signing note for Windows 10 1903+ ↩
- (2021). XLM + AMSI: New runtime defense against Excel 4.0 macro malware. https://www.microsoft.com/en-us/security/blog/2021/03/03/xlm-amsi-new-runtime-defense-against-excel-4-0-macro-malware/ - March 3, 2021 disclosure of XLM macro AMSI integration; full 2021-era AMSI host enumeration ↩
- (2024). AMSI integration with Microsoft Defender Antivirus. https://learn.microsoft.com/en-us/defender-endpoint/amsi-on-mdav - Defender AMSI host list: PowerShell, JScript, VBScript, WSH, .NET Framework 4.8+, WMI ↩
- (2020). Stopping Active Directory attacks and other post-exploitation behavior with AMSI and machine learning. https://www.microsoft.com/en-us/security/blog/2020/08/27/stopping-active-directory-attacks-and-other-post-exploitation-behavior-with-amsi-and-machine-learning/ - August 27, 2020 disclosure of Defender pair-of-classifiers on-endpoint + cloud architecture; BloodHound detection example ↩
- (2015). Windows 10 to Offer Application Developers New Malware Defenses (Wayback snapshot). https://web.archive.org/web/20221103061721/https://www.microsoft.com/en-us/security/blog/2015/06/09/windows-10-to-offer-application-developers-new-malware-defenses/ - Lee Holmes's June 9, 2015 design-rationale post for AMSI on the Microsoft Security Blog (archived since the original URL no longer resolves) ↩
- (2024). Concept (computer virus). https://en.wikipedia.org/wiki/Concept_virus - WM/Concept 1995 reference: first widely propagated Word macro virus ↩
- (2024). ILOVEYOU. https://en.wikipedia.org/wiki/ILOVEYOU - ILOVEYOU worm reference: 2000-05-04 release; VBScript via Windows Script Host; ~10% of internet-connected computers; US$10-15 billion damage estimate ↩
- (2016). Invoke-Obfuscation. https://github.com/danielbohannon/Invoke-Obfuscation - Daniel Bohannon's PowerShell obfuscator (released 2016-09-25 at DerbyCon 6.0); refuted signature-based defenses for obfuscation artifacts ↩
- (2018). Office VBA + AMSI: Parting the veil on malicious macros. https://www.microsoft.com/en-us/security/blog/2018/09/12/office-vba-amsi-parting-the-veil-on-malicious-macros/ - September 12, 2018 disclosure of Office VBA AMSI integration; trigger-buffer architecture ↩
- (2024). PowerShell. https://en.wikipedia.org/wiki/PowerShell - PowerShell version timeline; PowerShell 3.0 (2012) and 5.0 (2015) ship dates ↩
- (2024). about_Logging_Windows - PowerShell logging on Windows. https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_logging_windows - PowerShell Event 4104 Script Block Logging schema and module-logging Event 4103 ↩
- (2015). PowerShell the Blue Team. https://devblogs.microsoft.com/powershell/powershell-the-blue-team/ - Lee Holmes companion post (June 9, 2015) naming the assume-breach mindset; module + script-block logging framing ↩
- (2024). How AMSI helps. https://learn.microsoft.com/en-us/windows/win32/amsi/how-amsi-helps - Microsoft Learn explanation of AMSI mechanics; trigger-buffer architecture for Office VBA; non-script-engine use cases ↩
- (2024). Windows 10 version history. https://en.wikipedia.org/wiki/Windows_10_version_history - Windows 10 release dates: 1507 (July 29, 2015), 1709 (October 17, 2017), 1903 (May 21, 2019) ↩
- (2024). AmsiScanBuffer function (amsi.h). https://learn.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-amsiscanbuffer - AmsiScanBuffer signature, parameters, return values; minimum supported Windows 10 desktop / Windows Server 2016 ↩
- (2024). AmsiInitialize function (amsi.h). https://learn.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-amsiinitialize - AmsiInitialize signature; appName/context handshake at startup ↩
- (2024). AmsiOpenSession function (amsi.h). https://learn.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-amsiopensession - Session correlation primitive for joining multiple AmsiScanBuffer calls into one logical user command ↩
- (2024). AmsiResultIsMalware macro (amsi.h). https://learn.microsoft.com/en-us/windows/win32/api/amsi/nf-amsi-amsiresultismalware - Verdict-interpretation macro; nonzero when AMSI_RESULT indicates malware ↩
- (2018). AMSI Bypass Redux. https://www.cyberark.com/resources/threat-research-blog/amsi-bypass-redux - CyberArk Labs May 2018 follow-up after 1709; AmsiScanString -> AmsiScanBuffer change; canonical xor edi, edi patch; verbatim MSRC "AMSI was not designed to prevent" quote ↩
- (2024). AMSI_RESULT enumeration (amsi.h). https://learn.microsoft.com/en-us/windows/win32/api/amsi/ne-amsi-amsi_result - AMSI_RESULT verdict enumeration (CLEAN, NOT_DETECTED, BLOCKED_BY_ADMIN_START/END, DETECTED); >= 32768 indicates malware ↩
- (2024). IAntimalwareProvider interface (amsi.h). https://learn.microsoft.com/en-us/windows/win32/api/amsi/nn-amsi-iantimalwareprovider - IAntimalwareProvider COM contract; FeatureBits 0x1/0x2 Authenticode-signing check semantics from Windows 10 1903 ↩
- (2024). Windows-classic-samples / AmsiProvider. https://github.com/Microsoft/Windows-classic-samples/tree/master/Samples/AmsiProvider - Microsoft reference IAntimalwareProvider sample; regsvr32-installable AMSI provider skeleton ↩
- (2024). AMSI for developers - audience and registration. https://learn.microsoft.com/en-us/windows/win32/amsi/dev-audience - Provider registration scheme (paired registry trees); Windows 10 1709 DLL-hijack mitigation; ThreadingModel = Both ↩
- (2024). Antimalware Scan Interface (AMSI). https://redcanary.com/blog/threat-detection/amsi/ - Red Canary explainer; Defender provider CLSID {2781761E-28E0-4109-99FE-B9D127C57AFE}; MpOav.dll path; provider registration walk-through ↩
- (2024). DeviceEvents table - Microsoft Defender XDR advanced hunting. https://learn.microsoft.com/en-us/defender-xdr/advanced-hunting-deviceevents-table - Defender XDR DeviceEvents schema (ActionType AmsiScriptDetection, AmsiData, AmsiPatchedTextInResult, AmsiProcessName columns) ↩
- (2022). New AMSI Bypass Using CLR Hooking. https://practicalsecurityanalytics.com/new-amsi-bypass-using-clr-hooking/ - Practical Security Analytics December 2022 CLR-side AMSI callout patch (Assembly.Load(byte[])); decompiled AmsiUtils.ScanContent with amsiInitFailed short-circuit ↩
- (2019). Announcing the .NET Framework 4.8. https://devblogs.microsoft.com/dotnet/announcing-the-net-framework-4-8/ - .NET Framework 4.8 release announcement (April 18, 2019); Assembly.Load(byte[]) AMSI scan integration ↩
- (2020). Microsoft-Antimalware-Scan-Interface ETW manifest (etw-providers-docs). https://raw.githubusercontent.com/repnz/etw-providers-docs/master/Manifests-Win10-18990/Microsoft-Antimalware-Scan-Interface.xml - ETW manifest dump showing AMSI provider GUID {2A576B87-09A7-520E-C21A-4942F0271D67} and event template fields (session, scanStatus, scanResult, appname, contentname, contentsize, originalsize, content, hash, contentFiltered) ↩
- (2024). Hunting-Queries-Detection-Rules - AMSIScriptDetections.md. https://github.com/Bert-JanP/Hunting-Queries-Detection-Rules/blob/main/Defender%20For%20Endpoint/AMSIScriptDetections.md - Community KQL hunting pack for AmsiScriptDetection in Defender for Endpoint; canonical join patterns; T1059.001 mapping ↩
- (2024). Command and Scripting Interpreter: PowerShell (T1059.001). https://attack.mitre.org/techniques/T1059/001/ - MITRE ATT&CK PowerShell technique; standard taxonomic anchor for AMSI script detections ↩
- (2022). In-process patchless AMSI bypass. https://ethicalchaos.dev/2022/04/17/in-process-patchless-amsi-bypass/ - EthicalChaos April 2022 in-process patchless bypass write-up; AddVectoredExceptionHandler + DR0 + SetThreadContext((HANDLE)-2) pattern verbatim ↩
- (2021). AMSI bypasses remain tricks of the malware trade. https://news.sophos.com/en-us/2021/06/02/amsi-bypasses-remain-tricks-of-the-malware-trade/ - Sophos June 2021 retrospective; preserves verbatim Graeber tweet text (May 2016 attribution) and field-telemetry data showing residual ~1% amsiInitFailed share ↩
- (2018). Exploring PowerShell AMSI and Logging Evasion. https://www.mdsec.co.uk/2018/06/exploring-powershell-amsi-and-logging-evasion/ - MDSec June 2018 walk-through of the Matt Graeber amsiInitFailed reflection bypass; decompiled AmsiUtils.amsiInitFailed field ↩
- (2018). AMSI Bypass: Patching Technique. https://www.cyberark.com/resources/threat-research-blog/amsi-bypass-patching-technique - CyberArk Labs February 2018 disclosure of the first canonical in-memory AmsiScanString patch; Windows 10 1607 -> 1709 mitigation ↩
- (2022). Detecting Windows AMSI Bypass Techniques. https://www.trendmicro.com/en_us/research/22/l/detecting-windows-amsi-bypass-techniques.html - Trend Micro December 2022 bypass-technique retrospective; full bypass-family catalogue; AmsiEnable registry knob ↩
- (2024). CrowdStrike Investigates the Threat of Patchless AMSI Bypass Attacks. https://www.crowdstrike.com/en-us/blog/crowdstrike-investigates-threat-of-patchless-amsi-bypass-attacks/ - CrowdStrike 2024 patchless-bypass analysis; DR0-DR3 + Vectored Exception Handler mechanics; Microsoft-Windows-Kernel-Audit-API-Calls vs Microsoft-Windows-Threat-Intelligence detection guidance; T1562.001 mapping ↩
- (2024). Impair Defenses: Disable or Modify Tools (T1562.001, redirects to T1685). https://attack.mitre.org/techniques/T1562/001/ - Legacy T1562.001 slug; now meta-refreshes to T1685 ↩
- (2024). Impair Defenses: Disable or Modify Tools (T1685). https://attack.mitre.org/techniques/T1685/ - MITRE ATT&CK unified Disable-or-Modify-Tools technique (the destination of the T1562.001 redirect) ↩
- (2024). Windows Security Servicing Criteria. https://www.microsoft.com/en-us/msrc/windows-security-servicing-criteria - MSRC framework defining what counts as a security boundary or security feature for Windows servicing ↩
- (2024). Top-Level Functional Specification: Virtual Secure Mode. https://learn.microsoft.com/en-us/virtualization/hyper-v-on-windows/tlfs/vsm - Hyper-V TLFS VSM page; documents VTL switches via HvCallVtlCall hypercall and per-VTL register state preservation ↩
- (2017). PowerShell Constrained Language Mode. https://devblogs.microsoft.com/powershell/powershell-constrained-language-mode/ - Constrained Language Mode (CLM) definition; WDAC UMCI auto-activation; standalone-insecurity caveat ↩
- (2024). Macros from the internet are blocked by default in Office. https://learn.microsoft.com/en-us/microsoft-365-apps/security/internet-macros-blocked - MotW-anchored Office "Block Macros from Internet" default and trusted-location carve-outs ↩
- (1984). Computer Viruses - Theory and Experiments. https://doi.org/10.1016/0167-4048(87)90122-2 - Cohen's 1984 (published 1987) undecidability result for general virus detection by a priori or runtime analysis ↩
- (2020). Detecting Malicious PowerShell Commands using Deep Neural Networks. https://www.microsoft.com/en-us/research/publication/detecting-malicious-powershell-commands-using-deep-neural-networks/ - ACM AsiaCCS 2020 paper on AMSI-collected PowerShell classification; character + token level model; ~90% TPR / <0.1% FPR upper bound ↩
- (2019). Detecting Malicious PowerShell Commands using Deep Neural Networks (arXiv preprint). https://arxiv.org/abs/1905.09538 - arXiv preprint of the Hendler-Kels-Rubin paper; v1 23 May 2019, v2 19 Sep 2019 ↩
- (2024). DynamicMethod Class (System.Reflection.Emit). https://learn.microsoft.com/en-us/dotnet/api/system.reflection.emit.dynamicmethod - DynamicMethod / Reflection.Emit reference; demonstrates a .NET in-memory IL path AMSI does not catch ↩