c:\\windows\\system32\\amsi.dll
它提供了通用的标准接口(COM接口、Win32 API),API是为接入 AMSI 的程序提供的,COM 接口是为防病毒厂商提供的。System.Management.Automation.dll
实现vbscript.dll
实现jscript.dll
、jscript9.dll
和 jscriptlegacy.dll
实现VBE7.dll
实现excel.exe
和 excelcnv.exe
实现Microsoft.Exchange.HttpRequestFiltering.dll
实现fastprox.dll
实现clr.dll
和 coreclr.dll
实现VSSVC.exe
和 swprv.dll
实现consent.exe
实现amsi.dll
加载并调用其 AmsiInitialize
和 AmsiOpenSession
函数以建立 AMSI 会话。然后通过 AmsiScanString
或 AmsiScanBuffer
函数提交要扫描的内容。在 AmsiScanString 绕过方法公开后,AmsiScanBuffer
取代了AmsiScanString
。(顺带一提,AmsiScanBuffer
也能被绕过,但微软不再认为 AMSI 绕过是一种漏洞,修复的并不积极。)
amsiContext
的上下文句柄的指针。这个名为 amsiContext
的上下文句柄在每个后续 AMSI 相关函数中都会使用。AmsiInitialize
的调用发生在我们能够调用任何 PowerShell 命令之前,这意味着我们无法以任何方式影响这个过程。AmsiInitialize
的第一次调用无法被干扰,但可以反射调用AmsiUninitialize
,这会导致powershell 重新进行AmsiInitialize
,这次调用是可以提前干扰的。
AmsiInitialize
完成并创建上下文结构,AMSI 就可以解析发出的命令。当我们执行 PowerShell 命令时,会调用 AmsiOpenSession
API。amsiContext
上下文句柄来创建一个新的会话句柄。amsiContext
保存了 AMSI 的全局状态,而每个会话句柄则代表了一个独立的扫描会话。HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\AMSI\Providers
注册表项中注册多个 AMSI 提供程序。HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\AMSI\Providers
,有一个子键,其名称为 {2781761E-28E0-4109-99FE-B9D127C57AFE}
HKEY_CLASSES_ROOT\CLSID\{2781761E-28E0-4109-99FE-B9D127C57AFE}\InprocServer32 - (default)
.该注册表值的内容为 %ProgramData%\Microsoft\Windows Defender\Platform\4.18.2110.6-0\MpOav.dll
。因此,MpOav.dll 是特定供应商(在本例中为 Microsoft)的 AMSI 提供程序 DLL,负责接收和处理应用程序传递给它的内容。PowerShell/src/System.Management.Automation/security/SecuritySupport.cs
和PowerShell/src/System.Management.Automation/engine/runtime/CompiledScriptBlock.cs
。CompiledScriptBlock.cs
的 PerformSecurityChecks
函数调用 ScanContent
进行扫描。ScanContent
只是调用了WinScanContent
,实际的扫描逻辑都是在WinScanContent
里。不少文章 都是这么写的,但这只是开源的跨平台 powershell 7.x 才有的设计,在 windows 自带的 powershell 5.x 中,扫描逻辑位于ScanContent
。
PSEtwLog.LogAmsiUtilStateEvent
这个也是7.x新增的,windows自带的 powershell 没有,有了这个,安全产品检测 AMSI 绕过就容易多了(这个也能绕过)。
AmsiInitialize
调用发生在powershell初始化时,执行语句时,先 AmsiOpenSession
打开会话 ,然后用 AmsiScanBuffer
扫描,再用AmsiCloseSession
关闭会话。AmsiOpenSession
的调用,其他函数的调用与之类似 。bp amsi!AmsiOpenSession
PAGE_EXECUTE_READ
,不修改内存权限的情况下只有 call rax
这个有可能用于绕过,别的 都是写死的。 [System.Reflection.Assembly]::Load()
可以加载C#程序集,在powershell中也可以调用,其也会调用 AMSI ,扫描不通过时会产生上述报错。Load
方法的实现。 nLoadImage
。[MethodImpl(MethodImplOptions.InternalCall)]
,这意味着它是通过内部调用实现的,而不是用托管代码(C#)实现。nLoadImage
的对象的方法表的地址。方法表 (Method Table) 是一种数据结构,用于描述类型的所有方法,并提供访问这些方法的机制。
nLoadImage
CLRLoadLibraryEx
是对 LoadLibraryExW
的封装,因为没有启动新进程,当前进程还是 powershell进程,LoadLibraryExW
会返回已经加载的 amsi.dll
模块的句柄,并增加其引用计数。这意味着在powershell 中对 amsi.dll
进行的修改,在 [System.Reflection.Assembly]::Load()
也会生效。AmsiInitialize
建立了新的 amsiContext
,着意味着对 powershell amsiContext
的修改不会影响到Load()
的 AMSI 检测。AmsiScanBuffer
进行扫描(这里并没有使用 AmsiOpenSession
建立会话),根据扫描结果决定是否进行阻断。 fastprox.dll
,对象初始化函数如下AmsiEnable
值被设置为0后续就不进行 AMSI 扫描,这块和 JScript.dll
处理差不多,早期 win10 版本可以修改 HKCU 注册表绕过,现在的版本都是检查 HKLM
了。JAmsiInitialize
函数中,加载 amsi 前进行了一些判断。g_Amsi
是前文检查的注册表项,JAmsiIsCurrentProcessWmiprvse
则是比对当前文件的路径是否为系统目录下的\wbem\wmiprvse.exe
,如果是的话则不进行检测。JAmsi::JAmsiProcessor
中进行扫描前,还进行了一个判断,判断需不需要进行扫描。JAmsiIsScannerNeeded
会对对象及方法进行判断,比如执行wmic Process Call Create "cmd /c whoami"
时对象 | 方法 |
MSFT_CliAlias.FriendlyName='Process' | GetObject |
MSFT_LocalizablePropertyValue.ObjectLocator="",PropertyName="Description",RelPath="MSFT_CliAlias.FriendlyName=\"Process\"" | GetObject |
MSFT_CliAlias.FriendlyName='Process' | GetObject |
Win32_Process | GetObject |
SetPropValue | CommandLine |
SetPropValueCommandLine
hash得到的结果为 c0b29b3d,故进行AMSI检测。WMI 检测的 hash 没太大爆破的价值,jscript 和 vba 的检测 hash 如下文所示。 https://github.com/synacktiv/AMSI-Bypass/tree/master
AMSI.dll
不开源,只能通过逆向的方法分析,并且不同版本之间存在一定的差异,以下分析以windows 11 为准,与旧版本的差异会指出一部分,CoCreateInstance
实现,其会通过注册表CLSID查找和定位COM服务器,由于其会先在注册表 HKCU 项查询,故普通用户也可以对其进行劫持。微软为了修复这个问题将代码改为直接调用更底层的 DllGetClassObject
创建 COM 类工厂。AtlComModuleGetClassObject
,网上并未找到分析为什么替换的文章。 AtlComModuleGetClassObject
查看参数。ATL::CComClassFactory::CreateInstance
是 ATL 类工厂的一部分,用于创建 COM 对象的实例。创建了 CAmsiAntimalware
对象。
amsi!ATL::CComCreator<ATL::CComObject<CAmsiAntimalware> >::CreateInstance
函数负责实际的对象创建过程。CAmsiAntimalware
是 AMSI 的一个具体实现类。AmsiComCreateProviders
负责创建和初始化 IAntimalwareProvider
接口的实际实例。AmsiComCreateProviders
调用栈如下。AmsiComCreateProviders
函数中调用 RegOpenKeyExW
和 RegQueryInfoKeyW
获取"Software\\Microsoft\\AMSI\\Providers"
的信息,并使用 RegEnumKeyExW
枚举其子项,获取已注册的 AMSI 提供程序的类标识符。随后将提供程序的类标识符传递给 AmsiComSecureLoadInProcServer
,用 RegGetValueW()
查询与 AMSI 提供程序对应的 InProcServer32
值。AmsiComSecureLoadInProcServer
会调用 amsi!CheckTrustLevel()
读取SOFTWARE\Microsoft\AMSI\FeatureBits
的值,这个键包含一个 DWORD 值,可以是 1(默认值)或 2,用于禁用或启用对提供程序的 Authenticode 签名检查。LoadLibraryExW
以加载 AMSI 提供程序的 DLL。通过以下方法调试可以看到加载了 MpOav.dll
DllGetClassObject
函数创建一个类对象,并调用调用 IClassFactory::CreateInstance
获取请求的接口指针。amsiSession
或 amsiContext
为 NULL
,或者 amsiContext
的第二和第三个 _QWORD
为0,则返回错误代码 -2147024809
(通常表示无效参数)。amsi!CAmsiAntimalware::Scan
。CAmsiAntimalware::Scan
会遍历每个已注册的 AMSI 提供程序,调用供应商实现的IAntimalwareProvider::Scan()
函数,如果各个 AMSI 提供程序都没有发现恶意软件,则将结果设置为AMSI_RESULT_NOT_DETECTE
。