【免杀技术】致盲组件 - AMSI内存修复 (修改一个字节就能绕过AMSI?)
真理的半说 发表于 四川 技术文章 1806浏览 · 2024-12-31 06:08

简介

AMSI是微软开发的一个用于扫描特殊攻击面的反病毒组件,可以看微软官方的AMSI介绍。这是微软官方的扫描原理介绍,ASMI接口会提供扫描的结果给杀毒软件,最后还是由杀软来处理恶意事件。官方还给出了AMSI的一些扫描API:

函数名称 说明
AmsiCloseSession 关闭由 AmsiOpenSession 打开的会话。
AmsiInitialize 初始化 AMSI API。
AmsiNotifyOperation 向反恶意软件提供程序发送任意操作的通知。
AmsiOpenSession 打开一个会话,在该会话中可以关联多个扫描请求。
AmsiResultIsMalware 确定扫描结果是否指示应阻止内容。
AmsiScanBuffer 扫描包含恶意软件内容的缓冲区。
AmsiScanString 扫描字符串以查找恶意软件。
AmsiUninitialize 删除最初由 AmsiInitialize 打开的 AMSI API 实例。

AMSI内存修补绕过的攻击面主要集中在AmsiOpenSession、AmsiScanBuffer、AmsiScanString。

逆向分析

AMSI的功能都是从amsi.dll中调用的API,可以在HackerProcess中看见这个powershell进程中加载了amsi.dll:

这也是我们能够进行修补的根本原因(反病毒的代码就在当前的进程内存中)。

在system32目录下找到amsi.dll,拖进IDA中

HRESULT __stdcall AmsiOpenSession(HAMSICONTEXT amsiContext, HAMSISESSION *amsiSession)
{
  signed __int32 v3; // eax

  if ( !amsiSession || !amsiContext || !*((_QWORD *)amsiContext + 1) || !*((_QWORD *)amsiContext + 2) )
    return -2147024809;
  v3 = _InterlockedExchangeAdd((volatile signed __int32 *)amsiContext + 6, 1u);
  *amsiSession = (HAMSISESSION)(v3 + 1);
  if ( v3 == -1 )
    *amsiSession = (HAMSISESSION)_InterlockedIncrement((volatile signed __int32 *)amsiContext + 6);
  return 0;
}

AmsiOpenSession接受两个参数HAMSICONTEXT amsiContextHAMSISESSION *amsiSession,然后会对这两个变量做一些是否为空指针的判断,如果都有空指针,那么就return -2147024809;。根据ChatGPT的回答:这个特定的值 -2147024809 是一个常见的 Windows 错误代码,它在十六进制表示为 0x80070057。0x80070057 是 Windows 系统中的 E_INVALIDARG 错误代码,表示 “无效的参数”(Invalid Argument)。

以我的编程习惯,如果遇到这种参数无效的情况一般都是用return去退出当前函数,这个return -2147024809看着还挺像这么一回事。事实也是如此,只要执行到了return -2147024809这步,那么这个AMSI的API就会执行失败,后续的扫描也是不会执行的。

这部分if判断对应的汇编如上,我们只需把圈出来的部分的字节码0x75替换成0x74即可,这样就可以让if判断的逻辑反着来了。

再对AmsiScanString和AmsiScanBuffer进行分析也可以发现有类似结构,并且AmsiScanString貌似是直接调用的AmsiScanBuffer。:

那只要对AmsiScanBuffer做单字节修补,其实也变相对AmsiScanString做了修补了。

单字节修补AmsiScanBuffer绕过AMSI扫描接口

正常情况下执行ps1恶意脚本会遭遇到AMSI的扫描,并且被WD拦截:

打开xdbg,附加一个PowerShell进程,找到AmsiScanBuffer的位置,箭头所指的地方就是return -2147024809;

先运行一个反弹shell的nishang脚本。


可以看到正常情况下的AMSI不会执行到return -2147024809;,并且还会报毒。

写好POC后,我们再次用xdbg查看PowerShell进程

可以发现jne指令已经被替换成了je,下次运行按道理来说肯定就会跳转到return -2147024809;了。

AmsiOpenSession和这两个函数略有不同,区别主要是在jz jnz的排布上。

因为这里会多一个jne,所以你要找到第二个jne指令,原理都是一样的。

AMSI的内存修补POC的规避

一些杀软会对AMSI修补行为进行规避,参考一下Elatic的规则,似乎不是很严格。规则中对API的使用进行了检测,如WriteProcessMemoryVirtualProtectEx等。在只是面对杀毒软件的情况下,这完全就可以通过系统调用直接绕过了。规则中还有大量的白名单排除项,路径排除项,可信进程排除项等(白+黑直接绕了)。

我的POC在没有做任何免杀的情况下,没有被WD查杀,能够正常执行,不过却触发了一个文件上传请求,WD觉得可疑,所以向我索要POC文件。

其实用C语言操作远程进程的内存是非常可疑的,内存修补更好的方案是写成C#语言的POC,直接在PowerShell当前进程中执行。

这下直接连告警都没了,实战中可以写进ps1文件中进行远程的无文件攻击。

附件:
10 条评论
某人
表情
可输入 255