<pre><code class="language-markdown">## 一、有一次的实战经历:杀软无情干掉了我的Payload
大概两个月前,我接到一个内网渗透项目,目标是一家中型企业。我很快通过邮件钓鱼成功拿下了一台外网暴露的 Windows 终端。按照常规操作,我生成了一个 Cobalt Strike 的 Beacon 载荷,准备用来建立后续的 C2通信。然而问题出现了,我的恶意载荷刚准备执行,目标上的杀软就直接弹窗击杀了我的进程,甚至还把文件标记为高危并隔离。
像这种杀软拦截问题,我相信各位渗透测试工程师都遇到过。后来,我针对这个问题进行了一系列免杀优化,最终成功在目标内网横向渗透,完成了任务。今天,我就结合这个案例,详细拆解下恶意载荷免杀的一些实战技巧和思路,希望对各位有所帮助。
---
二、免杀背后的原理:杀软到底在看什么?
在聊免杀技术之前,我们先搞清楚一个问题:杀软到底是怎么检测恶意载荷的?只有明白它的工作机制,才能有针对性地进行绕过。
1. 静态检测
静态检测是杀软最基础的检测方式,通过扫描文件的二进制特征或者代码特征来判断是否恶意。比如:
- 特征字符串匹配:某些关键代码片段(如"Cobalt Strike"、"Mimikatz")被杀软直接标记成恶意;
- 文件头特征:PE 文件的元数据(如编译器签名、时间戳)可能会暴露你的工具来源;
- 代码结构分析:杀软会分析文件中的函数、字符串表、导入表,如果发现某些恶意行为对应的模式,就会判定为恶意。
2. 动态检测
动态检测是杀软通过行为分析判断文件是否恶意的方式,它会监控文件运行时的行为,比如:
- API 调用监控:如果你的载荷频繁调用可疑 API(如 VirtualAlloc、WriteProcessMemory、CreateRemoteThread),很可能会被标记;
- 内存注入检测:某些杀软会实时扫描目标进程的内存,发现异常代码段时直接拦截;
- 沙盒分析:部分杀软会在独立的环境中运行你的程序,观察其行为是否恶意。
3. 基于 AI 的检测
近年来,越来越多的杀软引入了机器学习模型。它们会通过大量样本训练一个恶意行为模型,再用这个模型动态判定你的文件是否存在风险。

因此,想要绕过杀软,我们需要针对不同的检测方式来设计免杀技术,接下来我将逐步分析具体的实现技巧。
---
三、免杀实战:从特征混淆到内存加载
在实战中,免杀的核心目标就是让我们的恶意载荷既能隐匿自身,又能顺利执行。我常用以下几个套路:
1. 文件特征混淆
静态检测通常是杀软的第一道防线,因此我们需要尽量混淆文件特征,让它无法匹配到恶意样本的签名。以下是几种常用的方法:
方法一:代码混淆
可以通过改变代码的结构,让杀软无法直接匹配到特征字符串。例如,对恶意代码中的 API 调用进行重命名或加密。以下是一个简单的例子: </code></pre>ruby
原始恶意代码
require 'fiddle'
kernel32 = Fiddle.dlopen('kernel32') VirtualAlloc = Fiddle::Function.new(kernel32['VirtualAlloc'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_SIZE_T, Fiddle::TYPE_UINT, Fiddle::TYPE_UINT], Fiddle::TYPE_VOIDP) VirtualAlloc.call(0, 4096, 0x1000, 0x40) # 分配可执行内存 <pre><code> 如果直接这样写,可能会被杀软识别为恶意行为。我们可以通过重命名和加密的方法来混淆: </code></pre>ruby
混淆后的代码
require 'fiddle'
k = Fiddle.dlopen('kernel32') v = Fiddle::Function.new(k[('V'+'irtual'+'Alloc').downcase], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_SIZE_T, Fiddle::TYPE_UINT, Fiddle::TYPE_UINT], Fiddle::TYPE_VOIDP) v.call(0, 4096, 0x1000, 0x40) # 调用前先混淆函数名 <pre><code> 这样可以有效躲过杀软的特征扫描。
方法二:文件加壳
可以使用一些简单的加壳工具对文件进行加密或压缩,比如 UPX。这种方法可以对抗大多数静态检测。 </code></pre>shell
使用 UPX 对文件加壳
upx -9 payload.exe <pre><code> 当然,市面上很多杀软已经能识别 UPX 的壳,所以更推荐自己写一个定制的加壳工具。
---
2. 动态行为伪装
绕过动态检测的关键是让恶意代码表现得“不像恶意”。以下是几种实用的方法:
方法一:延迟执行
大多数沙盒分析的时间都在 1-2 分钟左右,我们可以通过延迟执行来规避沙盒。例如,在恶意代码中加入一个长时间的睡眠: </code></pre>ruby
延迟执行示例
sleep(120) # 先睡 2 分钟,等沙盒超时退出 exec("powershell.exe -c 'Write-Host \"Payload执行中...\"'") <pre><code>
方法二:正常行为掩护
可以将恶意行为藏在正常的系统操作中,比如把恶意代码注入到合法的系统进程中。以下是一个简单的例子: </code></pre>ruby
将代码注入到 explorer.exe 进程中
require 'fiddle'
pid = tasklist | find "explorer.exe".split[1].to_i
注入代码的逻辑省略,可以调用 Windows API 完成
<pre><code> ---
3. 内存加载技术
将恶意载荷直接加载到内存中执行,是目前最常用的免杀技术之一。这样可以完全避开静态检测。以下是一个 Ruby 示例: </code></pre>ruby
使用 Fiddle 实现内存加载
require 'fiddle'
shellcode = [0xfc, 0x48, 0x83, 0xe4, 0xf0, ...] # 省略部分shellcode字节 ptr = Fiddle::Pointer.malloc(shellcode.size) ptr[0, shellcode.size] = shellcode.pack('C*')

create_thread = Fiddle::Function.new(Fiddle.dlopen('kernel32')['CreateThread'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_SIZE_T, Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP, Fiddle::TYPE_UINT, Fiddle::TYPE_VOIDP], Fiddle::TYPE_UINT) create_thread.call(nil, 0, ptr.to_i, nil, 0, nil) <pre><code> 这种方式可以完全避免生成落盘文件,直接在内存中运行恶意代码。
---
四、如何应对检测策略的升级?
杀软和攻击者的对抗是一个“猫鼠游戏”,随着杀软防御策略的升级,我们的免杀技术也需要不断迭代。以下是我总结的一些应对策略:
- 动态调试杀软:用调试器分析杀软的行为,找到拦截的核心逻辑,并进行针对性绕过。
- 使用多阶段加载:将恶意载荷分成多个部分,逐步加载进内存,减少一次性暴露的风险。
- 多样化工具链:不要只依赖一种工具,比如 Cobalt Strike,可以结合 Sliver 或自写工具。
---
五、个人经验分享:免杀是门艺术
回到最开始的那个案例,我最终通过代码混淆和内存加载技术完成了目标的突破。从这个过程我也深刻感受到,免杀不仅是技术活,更是一项“心理战”。你需要时刻站在杀软的角度思考,找到它的盲点并加以利用。
当然,所有这些技术只能用于授权环境,切勿用于非法用途。希望本次分享对大家的实战渗透有所帮助!</code></pre>
