暗影随行:EDR对抗与免杀艺术(Bypass AV/AMSI)
暗影随行:EDR对抗与免杀艺术(Bypass AV/AMSI)
在十年前,杀毒软件(AV)主要依靠“特征码”和“黑名单”来查杀木马。那时候的免杀(Bypass)往往只需要加个壳(Packer)或者把特征字符串混淆一下就能轻松过关。
但在现代企业内网中,我们面对的是武装到牙齿的 EDR(终端检测与响应系统)。EDR 不再只看文件长什么样,而是像幽灵一样盯着程序在内存中的每一个动作:它调用了什么系统 API?它向哪块内存注入了代码?它的线程在执行什么逻辑?
本文将摒弃低级的字符串混淆,直击 Windows 操作系统的内核边缘,推演现代 EDR 的动态查杀机制,并探讨高级红队是如何利用 Direct Syscalls、反射式 DLL 注入以及 AMSI 盲化等技术,在 EDR 的眼皮底下跳舞的。
1. 现代 EDR 的“天眼”:API Hooking
要知道怎么免杀,必须先知道 EDR 是怎么“看”到你的恶意行为的。
当我们在 C/C++ 中调用 VirtualAllocEx(在目标进程申请内存)和 WriteProcessMemory(向目标进程写入 Shellcode)时,这些函数实际上是由 kernel32.dll 提供的。而 kernel32.dll 最终会调用更底层的 ntdll.dll 中的 NtAllocateVirtualMemory。
EDR 的 Hook 机制推演:
- EDR 软件在系统启动时,会将自己的动态链接库(DLL)强行注入到所有用户态进程的内存空间中。
- EDR 找到
ntdll.dll中诸如NtAllocateVirtualMemory这样的敏感函数,将其开头的前几个字节机器码,强行修改为一个JMP(跳转)指令。 - 结果:当攻击者的木马调用这个 API 时,程序执行流会被那个
JMP强行拐带到 EDR 的分析引擎中。EDR 仔细检查传入的参数(申请了多大内存?是不是 RXW 权限?),如果发现异常,立刻弹窗拦截并终止进程。
2. 撕裂天眼:Direct Syscalls (直接系统调用)
既然 EDR 在用户态的 ntdll.dll 里布下了天罗地网(Hook),那我们就不走这条路了。
2.1 Syscall 的本质
ntdll.dll 中的函数,实际上只做了一件事:把系统调用号(Syscall Number,比如 0x18)放入 eax 寄存器,然后执行一条特殊的汇编指令 syscall。
这条指令会让 CPU 的执行特权级从 Ring 3(用户态)瞬间切换到 Ring 0(内核态),由操作系统内核去完成真正的内存分配。
2.2 Direct Syscalls 攻击推演
攻击者不再调用 kernel32.dll 或 ntdll.dll。
而是直接在自己的木马代码中,嵌入纯汇编代码:
防守盲区:因为攻击者直接在自己的代码里执行了 syscall 陷入内核,完全绕过了内存中被 EDR 篡改(Hook)过的 ntdll.dll。EDR 在用户态的监控瞬间变成了瞎子。
3. 内存中的幽灵:Reflective DLL Injection (反射式注入)
传统的后门木马是一个完整的 .exe 或 .dll 文件,落盘时极易被静态查杀,使用 LoadLibrary API 加载时也会触发文件系统过滤驱动的报警。
反射式 DLL 注入(Reflective DLL Injection) 是一种完全不需要文件落地的顶级内存加载技术。它是 Cobalt Strike 等高级 C2 工具的核心基石。
3.1 底层加载逻辑推演
普通的 DLL 是由 Windows 的 PE Loader 负责解析、分配内存并加载的。
反射式注入的核心是:木马自带一个微型的、完全用代码实现的 PE Loader。
- 攻击者利用漏洞(如反序列化),将恶意 DLL 的二进制数据以字节流的形式直接打入目标进程的内存中。
- 攻击者通过一小段启动代码(Bootstrap),把执行流引向 DLL 内部自带的那个
ReflectiveLoader函数。 ReflectiveLoader开始在内存中“自我孵化”:- 解析自身的 PE 头。
- 在内存中重新分配空间,把自己的各个段(Text, Data)搬运到正确的位置。
- 修复导入表(IAT),处理重定位(Relocation)。
- 最后,调用自己的
DllMain,恶意逻辑开始执行。
致命隐蔽性:由于没有调用系统的 LoadLibrary,这个 DLL 在操作系统的进程模块列表(PEB,进程环境块)中是完全隐形的。传统的杀软在遍历进程加载的模块时,根本看不见它的存在。
4. 盲化微软的守护神:AMSI Bypass
在后渗透中,攻击者极其依赖 PowerShell 或 C# (VBScript) 来执行无文件攻击。但微软在 Windows 10 引入了 AMSI (Antimalware Scan Interface,反恶意软件扫描接口)。
当你执行一段经过严重混淆的 PowerShell 脚本时,无论你怎么混淆,在脚本最终被解释器(如 powershell.exe)执行前,都会被解密成明文。
AMSI 的厉害之处在于,它会在这个最终解密的瞬间,将明文脚本内容拦截下来,发送给底层的杀毒软件(如 Windows Defender)进行扫描。
4.1 内存 Patch 盲化 AMSI
AMSI 的核心检查函数位于 amsi.dll 中的 AmsiScanBuffer。
攻击推演 (Memory Patching):
- 攻击者在执行恶意 PowerShell 脚本之前,先执行一小段前置脚本。
- 这段前置脚本通过反射机制或 API 调用,找到当前进程内存中加载的
amsi.dll。 - 定位到
AmsiScanBuffer函数的入口地址。 - 修改内存权限为可写(RWX),并强行将该函数开头的几个字节修改为
ret指令(机器码C3),或者修改为永远返回错误码(如80070057)。
终局:
当后续真正的恶意脚本开始执行时,PowerShell 依然会忠实地调用 AmsiScanBuffer。但由于这个函数已经被我们在内存中“阉割”了,它一被调用就立刻 ret 返回,并且告诉系统“扫描通过,没有威胁”。
至此,AMSI 彻底变成了形同虚设的摆设。
5. 免杀的终局
在 EDR 时代,免杀已经从简单的“躲避文件特征”,进化成了与操作系统底层机制(内存布局、系统调用、事件追踪 ETW)的全面战争。
高级红队不仅要精通 C/C++ 与汇编,更要比 EDR 厂商更懂操作系统的内核调度。而防守方(蓝队)也正在从用户态的 API Hooking,逐步向更深邃的内核层(如 ETW-Ti、内核驱动回调)演进。在这场没有硝烟的暗影随行中,对底层原理的敬畏与探索,才是制胜的唯一法则。