mcpField
A
analyzing-shellcode

analyzing-shellcode

github.com/trilwu安全17751 次安装12 周前更新
代码仓库

关于 analyzing-shellcode

面向恶意软件与逆向分析人员的技能,用于分析不带 PE、ELF 等文件头的原始 shellcode。涵盖解码、反汇编、模拟执行和提取 IOC,并强调隔离执行。

主要功能

  1. 01规范化 shellcode 字节
  2. 02识别架构与解码器特征
  3. 03反汇编原始代码
  4. 04模拟执行并解析 API
  5. 05识别载荷阶段与家族
  6. 06提取 C2 和其他 IOC

适用场景

  1. 01分析从利用载荷中提取的代码
  2. 02解码混淆或自解码载荷
  3. 03调查 Metasploit 或 Cobalt Strike 阶段
  4. 04从 shellcode 提取网络指标

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/trilwu/secskills --skill analyzing-shellcode

安装后在 Claude Code 中输入/analyzing-shellcode确认已加载。

详细介绍

分析 Shellcode

Shellcode 是没有容器的代码——没有 PE/ELF 头、没有导入表,也没有固定加载地址——因此必须在运行时自行查找所需的一切。正是这种自给自足暴露了它的身份:PEB 遍历、API 哈希、与位置无关的 GetPC 技巧,都是结构指纹,即使在了解载荷功能之前也能识别。

适用场景

  • 没有 PE/ELF/Mach-O 头的原始位置无关代码块
  • 从漏洞利用、宏、加载器或 JS/HTA 投放器中提取的载荷
  • \x 转义、带 0x 前缀、Base64 或经过 XOR 混淆的字节缓冲区
  • 疑似 Metasploit stager(reverse_tcp/https)或 Cobalt Strike 信标阶段
  • 在较大二进制文件分流分析时发现的 egg-hunter 或 GetPC/解码器存根
  • 从网络捕获、RWX 内存或文档流中恢复的字节

不适用场景

  • 样本是带头部的常规 PE/ELF/Mach-O 文件——使用 analyzing-binaries 执行由加载器支持的反汇编工作流
  • 需要进行更广泛的样本分流分析或家族识别——使用 analyzing-malware; 仅在其中包含原始载荷阶段时再回到这里
  • 代码块是打包后的可执行文件,而非原始 Shellcode——先使用 unpacking-protected-binaries 解包
  • 需要先从内存转储中提取 Shellcode——使用 analyzing-memory-images 定位并提取内存区域,然后再回到这里
  • 解码后的深度静态逆向分析——reverse-engineering 不是一项 技能;持续进行反汇编工作时,请转到 analyzing-binaries

先获取字节

Shellcode 通常经过封装。首先将其规范化为扁平二进制文件。

# \x-escaped or 0x C-array pasted from an exploit → raw bytes
python3 -c "import sys,re; d=open('sc.txt').read(); \
  b=bytes(int(x,16) for x in re.findall(r'(?:0x|\\\\x)([0-9a-fA-F]{2})',d)); \
  open('sc.bin','wb').write(b); sys.stderr.write(f'{len(b)} bytes\n')"

# base64 buffer
base64 -d payload.b64 > sc.bin

# single-byte XOR brute (find the key that yields printable / low entropy)
python3 -c "import sys; d=open('sc.bin','rb').read(); \
  [print(k, bytes(c^k for c in d[:16]).hex()) for k in range(256)]"

然后在反汇编之前先识别内容:

file sc.bin && wc -c < sc.bin           # size: stagers ~200-800B, stages tens of KB
ent sc.bin 2>/dev/null || binwalk -E sc.bin   # high entropy → encoded/self-decoder

对于来自文档和捕获文件的 Shellcode,请先提取:Office 宏使用 olevba --deobf (analyzing-malware 涵盖此内容);使用 tshark -r c.pcap --export-objects http,out/ 或跟踪 tcp.stream 从 PCAP 中提取, 然后按上述方法处理字节。

初步分流分析

xxd sc.bin | head -40

查看开头以判断结构,而非含义:

  • 架构推测。 x86 会出现 55 8B EC 序言、64 A1 30 00 / 64 8B(fs:[0x30])PEB 访问,以及 E8 00000000 5x 调用/弹出指令。x64 会出现 65 48 8B(gs:[0x60])、带 REX 前缀的 48 ...,以及 RSP 对齐指令 48 83 EC。 ARM/AArch64 使用统一的 4 字节字,没有 x86 序言特征。
  • GetPC 技巧。 E8 00 00 00 00 58/59/5A/5B/5E(call $+5 然后 pop),或 fnstenv / D9 EE D9 74 24 F4 5B 这种 FPU 技巧,可恢复 EIP 以实现位置无关。 出现这些指令意味着代码会自行重定位,后面通常会跟着解码器。
  • Egg-hunter 标记。 重复出现的 8 字节标记,例如 w00tw00t(77 30 30 74 ×2),表示 egg-hunter 正在内存中搜索真正的载荷。
  • 无空字节 / 字母数字编码。 不含 00 字节,或仅含可打印字符,意味着编码器存根 (Metasploit alpha_mixed、shikata_ga_nai)封装了真正的代码——你看到的是解码器,而非载荷。

在正确的基址进行静态反汇编

最常见的错误是在错误的架构下或从指令中间开始反汇编。尝试两种位宽,判断哪种能产生合理的序言。

# objdump on a raw blob — set the arch explicitly, Intel syntax
objdump -D -b binary -m i386        -M intel sc.bin     # 32-bit
objdump -D -b binary -m i386:x86-64 -M intel sc.bin     # 64-bit

# ndisasm — quick, and -e skips a leading offset if the entry is not at 0
ndisasm -b 32 sc.bin
ndisasm -b 64 sc.bin

# rizin / radare2: assemble/disassemble a blob directly
rasm2 -a x86 -b 32 -d "$(xxd -p sc.bin | tr -d '\n')"
r2 -a x86 -b 32 -m 0x400000 sc.bin      # load raw at a base
#   e asm.arch=x86 ; e asm.bits=32 ; s 0 ; pd 60      disassemble entry
#   pdf @ 0                                            treat entry as a function

要在 Ghidra/IDA/Cutter 中加载原始 Shellcode:作为原始二进制导入,设置处理器 (x86:LE:32 或 x86:LE:64),选择基址,并定义入口点 / 在真正的第一条指令处反汇编—— 如果前面有长度前缀或无用填充指令序列,不要从偏移 0 开始。若未定义入口,自动分析器会将数据 反汇编为代码,生成的清单也就毫无意义。

解码自解码器

编码后的 shellcode 由一个小型解码器桩和其后的加密主体组成。不要尝试静态读取主体——只反汇编桩,然后让它运行。

  • 识别解码器桩。 一个短循环使用 xor/add/sub/rol,操作对象包括运行指针、长度计数器以及密钥或密钥调度。循环末尾会跳回,直到计数器归零,然后顺流执行已解码的主体。
  • shikata_ga_nai。 Metasploit 的多态反馈 XOR 解码器:先是 GetPC 代码块,然后是 XOR 循环,其中每个密钥字都会根据前一个明文进行修改(加法反馈)。每次构建时都会重新编码,因此字节特征无法匹配——应通过执行来解码,而不是依靠特征匹配。
  • 让它自行解码。 模拟或单步执行解码器桩,在循环退出后转储内存;它写入的区域就是真正的阶段。然后对其进行反汇编。
# scdbg dumps the decoded buffer and unpacks the second stage for x86 Windows
scdbg -f sc.bin -s -1                     # -s -1 lifts the step limit (default 2M)
scdbg -f sc.bin -d                       # dump self-modified/decoded memory

通过哈希解析 Windows API

Shellcode 没有导入表,因此会遍历已加载模块列表,并根据 API 名称的哈希解析 API。识别出这一点,往往就完成了大部分分析。

  • 遍历 PEB。 fs:[0x30](x86)或 gs:[0x60](x64)会加载 PEB,然后遍历 PEB->Ldr->InMemoryOrderModuleList,查找 kernel32.dll(或 ntdll.dll),无需硬编码地址。
  • 导出项哈希。 对每个导出名称,解码器桩都会计算一个哈希——通常是 ror13 加法算法(经典的 Metasploit block_api)或 djb2——并与一个 4 字节常量进行比较。只要反向还原该常量,它就是 API 名称。
  • 将哈希映射回名称。 预先计算系统 DLL 中每个导出项的哈希,然后查找你看到的常量:
# Reproduce Metasploit block_api ror13 — the constant is hash(module)+hash(name)
python3 - <<'PY'
def ror13(b):
    h=0
    for c in b:
        h=((h>>13)|(h<<19))&0xffffffff; h=(h+c)&0xffffffff
    return h
mod=ror13("KERNEL32.DLL\0".encode('utf-16le'))   # module: UTF-16LE, uppercased
for api in ("LoadLibraryA","GetProcAddress","WinExec","VirtualAlloc"):
    print(hex((ror13((api+"\0").encode('ascii'))+mod)&0xffffffff), api)
PY

工具:scdbg 会在模拟时打印已解析的 API 调用及其参数;在调试器下使用 jmp2it/blob_runner,可以在解析器处设置断点;导出表可通过 dllexp 或 rabin2 -E kernel32.dll 获取。哈希算法为自定义算法时,将其移植到 Python 脚本中,并对导出项列表进行暴力匹配。

模拟是最快的途径

对于 Windows x86 shellcode,模拟可在几秒内解析 API、解码各个阶段并打印行为——应优先尝试,而不是手动跟踪。

# scdbg — emulates x86 Windows shellcode, reports API calls, C2, and stages
scdbg -f sc.bin                          # default trace of resolved API calls
scdbg -f sc.bin /findsc                  # brute-force the correct start offset
scdbg -f sc.bin -foff 0x1a               # force entry at a known offset

# speakeasy — emulate x86/x64 shellcode or a raw module
speakeasy -t sc.bin --raw --arch x86     # --raw shellcode, --arch x86|amd64
speakeasy -t sc.bin --raw --arch amd64 -o report.json

对于自定义逻辑或非 Windows 目标,构建一个脚本环境:

  • Unicorn — 映射内存、设置寄存器、挂钩 code/mem 事件、运行,然后读取解码后的区域。最适合需要编写脚本进行解码的场景。
  • Qiling — 更高层的工具;它会模拟操作系统层,使基于哈希的 API 解析以及 VirtualAlloc/WinExec 能正常运行,并提供 rootfs 和系统调用拦截。
  • 在调试器下运行。 blob_runner、shellcode2exe 或 jmp2it 可将 blob 封装成可加载的桩;在 x32dbg/x64dbg 中打开,将 EIP/RIP 设置为入口,然后单步跟踪解码器,直到到达正在运行的阶段。

识别阶段和家族

  • Metasploit 分阶段载荷。 reverse_tcp 会创建套接字,然后通过 recv 将第二阶段接收到 RWX 内存中并跳转到该阶段;reverse_https 使用 WinInet(InternetOpen/HttpOpenRequest)。scdbg/speakeasy 会直接显示连接地址和端口。
  • Cobalt Strike。 Beacon 阶段包含编码后的配置;解码后,解析其中的 C2 主机、端口、URI、用户代理、休眠时间/抖动值以及可塑配置文件。社区解析器(例如 beacon 配置提取工具)可以读取配置块;确认是 Beacon 后,将其交给 analyzing-malware。
  • 提取端点。 从模拟日志或解码后的配置中提取 C2 主机/端口和请求 URI——这些是需要立即封锁的 IOC。
  • 与已知解码器对比。 shikata、call4_dword_xor、alpha_mixed 和 block_api 解析器都有可识别的结构;匹配其中一种即可确定所用工具包,并判断后续字节应是什么内容。

安全执行规范

模拟执行是安全的;实际运行 shellcode 就是引爆。请采用与 analyzing-malware 相同的隔离措施。

  • 使用隔离的 VM,在执行前创建快照,执行后还原。
  • 断开网络,或使用伪造的 C2(INetSim/FakeNet-NG),让加载器的回连请求指向你的监听器,而非真实操作者。
  • 在正在处置的事件中,绝不要让加载器连接真实 C2——回连会告知操作者你正在调查,而下载第二阶段载荷则会泄露你的分析。

提取 IOC 并移交

解码时收集稳定的指标:C2 主机/IP、端口、请求 URI 和 user-agent、互斥体名称、解码密钥或哈希常量,以及 egg 标签。像 analyzing-malware 一样对它们排序——行为和配置比不断轮换的主机更持久。

然后移交工作:

  • 解码器或解析器的字节/结构特征 → writing-yara-rules(编写规则),然后交给 engineering-detections(部署规则)
  • 落地或下载的第二阶段载荷 → analyzing-malware
  • 对大型解码阶段进行持续反汇编 → analyzing-binaries
  • 撰写分析报告 → reporting-security-findings

应当否决的自我安慰

  • “objdump 显示的是乱码,所以它经过了加密。” 更可能是反汇编时使用了错误的架构、错误的偏移,或直接反汇编了整个解码器桩代码。先尝试两种位宽,并找到具有合理函数序言的偏移。
  • “没有导入表,所以我无法判断它调用了什么。” Shellcode 会通过哈希解析 API。找到 PEB 遍历代码,并还原哈希常量。
  • “我直接运行看看它会做什么。” 不行,除非使用隔离且网络受控的测试环境。应改用 scdbg/speakeasy 进行模拟。
  • “字节特征可以捕获这个加载器。” 对多态加载器无效——shikata_ga_nai 会在每次构建时重新编码。应针对解码后的阶段或结构性解析器制作特征,而不是针对编码器。
  • “字符串里没有明显的 C2。” 地址是在运行时构造的,配置也经过编码。先解码桩代码,再通过模拟读取 connect 调用。
  • “它从偏移 0 开始。” 通常并非如此——入口之前可能有长度前缀、对齐滑道或 GetPC 代码块。使用 /findsc,或读取代码以确定真正的第一条指令。
  • “反汇编器自动分析过了,所以列表是正确的。” 对于没有文件头的二进制块,分析器会猜测入口,并把数据当作代码处理。应指定真实入口和处理器,否则整个反汇编列表都会错位。

ATT&CK 覆盖范围

根据 secskills-core/ttp-index.json 生成——编辑该文件,然后运行 python3 scripts/sync_attack.py --write。在报告中引用 ID 前,请对照当前 ATT&CK 版本再次核实。

防御规避(TA0005)

  • T1027 混淆文件或信息——另请参阅 analyzing-malware、analyzing-binaries
  • T1140 对文件或信息进行反混淆/解码——另请参阅 analyzing-malware、analyzing-binaries
  • T1620 反射式代码加载——另请参阅 analyzing-malware

针对以上任一项编写检测内容:engineering-detections。主动搜寻:hunting-threats。入侵后处置:responding-to-incidents。

参考资料

  • analyzing-binaries — 解码阶段的反汇编工作流
  • analyzing-malware — 样本分流、隔离、家族识别和落地阶段分析
  • analyzing-memory-images — 先从内存转储中提取 shellcode
  • writing-yara-rules — 为解码器或解码阶段编写持久有效的特征
  • engineering-detections — 将解码器/解析器特征转化为已部署的规则
  • reporting-security-findings — 撰写分析报告
  • 使用 scdbg、speakeasy、Unicorn、Qiling 进行模拟
  • 使用 radare2/rizin(rasm2/pd)、Cutter 对原始二进制块进行静态反汇编
  • 使用 x64dbg/x32dbg 搭配 blob_runner / shellcode2exe / jmp2it 在调试器下运行

评论

支持 Markdown