reverse-skills-max: initial public release (restored, redacted, hardened)
This commit is contained in:
183
vendor-new/rev-skills/.claude/skills/re-shellcode/SKILL.md
Normal file
183
vendor-new/rev-skills/.claude/skills/re-shellcode/SKILL.md
Normal file
@@ -0,0 +1,183 @@
|
||||
---
|
||||
name: re-shellcode
|
||||
description: >
|
||||
Shellcode 分析:提取、解码循环、模拟执行。
|
||||
触发词:shellcode、shell code、位置无关、解码循环、恶意载荷
|
||||
capabilities: [shellcode-analysis]
|
||||
---
|
||||
|
||||
# Shellcode 分析
|
||||
|
||||
## 何时使用 / 何时不用
|
||||
|
||||
- 用:拿到的是"裸代码 blob"(无 PE/ELF/Mach-O 文件头)——漏洞利用载荷、文档/脚本解出的第二层载荷、释放器写进内存的代码
|
||||
- 用:载荷带自解码(XOR/ROL 解码循环、msfvenom 类编码器生成)——静态看是乱码,需要先还原再分析
|
||||
- 用:需要"无环境跑起来"验证载荷行为(架构不匹配/无宿主进程/不想在真实进程触发)——Unicorn 模拟
|
||||
- 用:回答"这段代码调用哪些 API、想干什么"(API hash 解析)
|
||||
- 不用:有文件格式头的完整程序(那是 [[re-binary-core]] 域)
|
||||
- 不用:只需初勘结论(file/hash/熵,那是 [[re-triage]])
|
||||
- 注意:模拟执行属动态执行,默认在沙箱内进行([[re-analyze/platform-tips]] 最高原则);纯静态提取/解码分析可免沙箱,但把解码产物落盘后真实执行验证时必须进 [[re-sandbox]]
|
||||
|
||||
## 工具准备
|
||||
|
||||
所有工具先验证再使用。本技能静态解码可免沙箱;模拟执行默认沙箱内进行([[re-analyze/platform-tips]] 最高原则)。
|
||||
|
||||
### python3 —— 解码/模拟脚本运行环境
|
||||
|
||||
- Linux: `apt install python3 python3-pip` / `dnf install python3 python3-pip` / `pacman -S python python-pip`(多数自带)
|
||||
- macOS: `brew install python`(或 Xcode CLT 自带);Windows: python.org 安装包(勾选 Add to PATH)或 `choco install python`;WSL 内 Linux 版
|
||||
- 验证: `python3 --version`
|
||||
|
||||
### unicorn —— 模拟执行主力(pip,Python 3)
|
||||
|
||||
- 全平台: `pip install unicorn`(Python 3)
|
||||
- 验证: `python3 -c "import unicorn; print(unicorn.__version__)"`
|
||||
|
||||
### capstone —— 反汇编输出(pip,Python 3)
|
||||
|
||||
- 全平台: `pip install capstone`
|
||||
- 验证: `python3 -c "import capstone; print(capstone.__version__)"`
|
||||
|
||||
### binwalk —— 从宿主文件里扫出嵌入 blob
|
||||
|
||||
- 安装渠道与版本形态(v2 legacy / v3 主线)见 [[re-fw-extract]] 工具准备;使用前先按该处确认 major
|
||||
- 验证: `binwalk --version`
|
||||
|
||||
### xxd —— 十六进制查看/特征定位
|
||||
|
||||
- Debian/Ubuntu: `apt install xxd`(新版独立包,旧版在 vim-common);Fedora: `dnf install xxd`(F38+ 独立子包,旧版 vim-common);Arch: `pacman -S xxd`
|
||||
- macOS: 自带(vim 附送);Windows: 无自带——用 WSL 或 PowerShell `Format-Hex`
|
||||
- 验证: `xxd -v`
|
||||
|
||||
### objdump(binutils)—— 裸二进制反汇编扫描
|
||||
|
||||
- Linux: `apt install binutils` / `dnf install binutils` / `pacman -S binutils`(多数发行版预装)
|
||||
- macOS: 自带;Windows: 用 rizin 或 Ghidra 替代
|
||||
- 验证: `objdump --version`
|
||||
|
||||
### 反汇编器 —— 按 `RE_DECOMPILER` 或环境选
|
||||
|
||||
- [[re-radare2]](rizin,命令行/低内存,Raw Binary 支持好,推荐);[[re-ghidra]](GUI,Import File → Raw Binary 选架构)
|
||||
- 安装与验证见对应技能工具准备
|
||||
|
||||
### scdbg —— 可选(shellcode 调试器)
|
||||
|
||||
- GitHub 检索 `Nypreo/scdbg` releases 下载(Windows 可执行文件;Linux 可在 Wine 下运行,或直接用 Unicorn 方案替代)
|
||||
- 验证: `scdbg.exe -f blob.bin` 能输出反汇编与模拟结果
|
||||
|
||||
### msfvenom —— 可选(生成对照样本/编码器特征参考)
|
||||
|
||||
- Kali: 预装(`apt install metasploit-framework`);Fedora: 添加 Rapid7 RPM 仓库后 `dnf install metasploit-framework`;Arch: `pacman -S metasploit`(extra 仓库,6.x)
|
||||
- 验证: `msfvenom -l encoders | head -5`(msfvenom 可独立使用,不依赖 msfconsole 的 PostgreSQL)
|
||||
- 用途: 用已知编码器(如 `x86/shikata_ga_nai`)生成样本,对照解码循环指令形态
|
||||
|
||||
## 操作步骤
|
||||
|
||||
按顺序执行,每步产物(blob/解码结果/模拟输出)存档 sha256 + 路径([[re-ioc]] 证据链要求)。
|
||||
|
||||
1. **提取 shellcode blob(文件/内存/流量三来源)**:
|
||||
```sh
|
||||
# 文件内嵌: 从宿主文件里扫嵌入段
|
||||
binwalk sample.bin
|
||||
binwalk -e sample.bin # 按签名自动提取
|
||||
# 定长段提取(已知偏移):
|
||||
dd if=sample.bin of=blob.bin bs=1 skip=<off> count=<len>
|
||||
# ELF .text 段导出(载荷伪装成代码段时):
|
||||
objcopy --dump-section .text=blob.bin sample.elf
|
||||
# 内存: 沙箱内 gcore 转储后按特征找(见 [[re-memdump]])
|
||||
# 流量: tshark 提取 pcap 中的载荷字节(见 [[re-netcap]])
|
||||
tshark -r c2.pcap -Y 'http' -T fields -e http.file_data > payload.hex
|
||||
xxd -r -p payload.hex blob.bin
|
||||
```
|
||||
- 提取原则: 先确认边界再提取——多提比少提好(入口在 blob 内部),边界不确定时先按 0xEB 短跳/解码循环跨度扩展范围
|
||||
- 三来源共性: 载荷常被宿主"加工"过(编码/切块/重排)——提取出的 blob 先 `file blob.bin` + 熵判断,乱码先假设有解码层(步骤 3)
|
||||
|
||||
2. **定位入口(0xEB 短跳 / 反汇编扫描)**:
|
||||
```sh
|
||||
xxd blob.bin | head -20 # 头部特征
|
||||
# 入口候选: 首字节 0xEB(jmp short,跳到解码器)、0xE8(call,自定位取 EIP)
|
||||
objdump -D -b binary -m i386 blob.bin | head -40 # 32 位 x86 裸反汇编
|
||||
objdump -D -b binary -m i386:x86-64 blob.bin | head -40 # 64 位
|
||||
```
|
||||
- 常见入口模式: ① `EB xx` 短跳(跳到解码循环,入口前留解码器空间)② `E8 00 00 00 00` + `pop reg`(call/pop 自定位,拿当前 EIP)③ 解码循环直接开头
|
||||
- rizin 侧: `rizin -a x86 -b 32 blob.bin` 进入后 `s <候选偏移>` + `pd 20` 逐段看(见 [[re-radare2]]);Ghidra 导入选 Raw Binary(x86:LE:32:default),在候选入口 `L` 标函数
|
||||
- 定位判据: 入口处应为"自定位 + 解码循环 + 跳转解码后代码"结构;反汇编有意义的指令序列才算入口,全乱码先走步骤 3 解码再回来
|
||||
|
||||
3. **解码循环分析(XOR/ROL 自解码)**:
|
||||
```sh
|
||||
# 特征指令(反汇编中找):
|
||||
# XOR byte ptr [reg+disp], imm 单字节 XOR 解码
|
||||
# ROL/ROR byte ptr [reg], imm 循环移位解码
|
||||
# ADD/SUB byte ptr [reg], imm 加减常量解码
|
||||
# LOOP / JECXZ 循环控制(计数在 ECX/RCX)
|
||||
```
|
||||
- 静态还原: 从反汇编提取解码参数(key 字节/移位量/长度/起点)→ python 复刻:
|
||||
```python
|
||||
data = bytearray(open('blob.bin','rb').read())
|
||||
key, start, length = 0x55, 0x00, len(data) # 参数来自反汇编
|
||||
for i in range(start, start + length):
|
||||
data[i] ^= key
|
||||
open('decoded.bin','wb').write(data)
|
||||
```
|
||||
- 还原后回步骤 2 重新定位入口——解码后的 blob 才是要分析的载荷(编码器可能还有第二层)
|
||||
- 对照验证: msfvenom 用 `x86/xor_dynamic` 等编码器生成样本,比较解码循环指令形态,确认识别没走偏(见坑 2)
|
||||
|
||||
4. **模拟执行(Unicorn 无环境运行、hook 系统调用)**:
|
||||
```python
|
||||
from unicorn import *
|
||||
from unicorn.x86_const import *
|
||||
blob = open('blob.bin','rb').read()
|
||||
mu = Uc(UC_ARCH_X86, UC_MODE_32) # 按步骤 2 确认的架构选模式
|
||||
CODE, STACK = 0x1000, 0x3000
|
||||
mu.mem_map(CODE, 0x1000); mu.mem_map(STACK, 0x1000)
|
||||
mu.mem_write(CODE, blob)
|
||||
mu.reg_write(UC_X86_REG_ESP, STACK + 0x800) # 设栈(不设必崩)
|
||||
def hook_code(uc, addr, size, user):
|
||||
if addr in API_ADDRS: # 步骤 5 定位的 API 调用点
|
||||
uc.reg_write(UC_X86_REG_EIP, addr + size) # 跳过 stub
|
||||
print("hit api", hex(addr))
|
||||
mu.hook_add(UC_HOOK_CODE, hook_code)
|
||||
try:
|
||||
mu.emu_start(CODE, 0x100000) # until 给大上限,非法访问即终止
|
||||
except UcError as e:
|
||||
print("stop:", e) # 缺 API/越界 = 预期终止点
|
||||
open('decoded.bin','wb').write(mu.mem_read(CODE, len(blob))) # 解码产物
|
||||
```
|
||||
- 让解码循环"自己跑"比手工还原快且准(坑 3 的对策);`hook_mem_write` 可记录解码产物落点(见 [[re-emulation]] 步骤 4)
|
||||
- 系统调用/API 处理: Unicorn 无 OS——先反汇编列出所有 call 目标,对已知 API 调用点 hook 返回假值并记录参数(stub 化,见步骤 5 与坑 4)
|
||||
|
||||
5. **与宿主交互面(API 解析:kernel32 遍历、hash 查找)**:
|
||||
- Windows shellcode 拿 API 的标准手法: PEB → PEB_LDR_DATA → 模块链表遍历到 kernel32.dll → 导出表按名字 hash 查找(GetProcAddress 的 shellcode 等价物)
|
||||
- 静态定位 API 集合: 反汇编找"push 4 字节常量; ...; call"模式,4 字节即 API 名 hash;用常见 hash 算法穷举对照:
|
||||
```python
|
||||
def djb2(s):
|
||||
h = 5381
|
||||
for c in s:
|
||||
h = ((h << 5) + h + c) & 0xffffffff
|
||||
return h
|
||||
for name in ['LoadLibraryA','VirtualAlloc','VirtualProtect',
|
||||
'WriteProcessMemory','CreateThread','WinExec']:
|
||||
print(name, hex(djb2(name.encode())))
|
||||
```
|
||||
- 算法对照: Metasploit 系(msfvenom 生成)API hash 默认 ROR13(非 djb2)——穷举对照时两种都要算,命中即确认生成器来源
|
||||
- 还原调用约定: 先确定位数——32 位无唯一约定:Win32 API 常见 `WINAPI`/`__stdcall`(callee 清栈、收尾为 `RET n`),程序自身默认 `__cdecl`、成员函数 `__thiscall`(只有 `/Gz` 才把默认改成 `__stdcall`),按符号修饰 / 调用者栈平衡 / ECX-EDX 使用判定;64 位用统一 x64 ABI(前四整型/指针参数 RCX/RDX/R8/R9 + 32 字节 shadow space,类 fastcall,不是 `__fastcall`)——决定参数寄存器/栈顺序与返回值位置(坑 3)
|
||||
- API 集合 = 行为意图: VirtualAlloc/VirtualProtect + WriteProcessMemory → 注入;CreateThread/CreateRemoteThread → 执行;WinExec/CreateProcess → 运行程序——直接对应 [[re-behavior]] 的 ATT&CK 映射(T1055 进程注入等)
|
||||
- 模拟 stub: 把 API 调用点加入 hook_code 的 API_ADDRS,按功能返回假值(如 VirtualAlloc 返回一块已映射内存地址),观察控制流走向
|
||||
|
||||
## 跨域联合
|
||||
|
||||
- [[re-binary-core]]:本技能是该网关的载荷分支——无文件格式头的裸代码 blob 专项分析;反编译底座复用 [[re-radare2]] / [[re-ghidra]]
|
||||
- [[re-emulation]]:模拟执行框架与 hook 技巧(本技能步骤 4 直接复用其步骤 2-4)
|
||||
- [[re-radare2]]:Raw Binary 反汇编、入口扫描([[re-ghidra]] 同效,按 `RE_DECOMPILER` 选)
|
||||
- [[re-memdump]]:从进程内存提取 shellcode(默认转储优先,gcore 后按特征找 blob)
|
||||
- [[re-malware]]:恶意样本载荷层——加载器/无文件样本的解码载荷走到本技能
|
||||
- [[re-anti-analysis]]:带壳/混淆样本里摘出的载荷段(脱壳产物回到本技能分析)
|
||||
- [[re-triage]]:提取前初勘(file/熵判断有无编码层)
|
||||
- 引用 [[re-analyze/platform-tips]] 最高原则(沙箱)与「静态优先」原则(先静态定位,动态按需)
|
||||
|
||||
## 常见坑与陷阱
|
||||
|
||||
- **位置无关地址(无基址概念)**:现象——Ghidra/objdump 反汇编出来跳转/数据引用全是"乱"地址,按虚拟地址分析对不上;原因——shellcode 无 PE/ELF 头、无固定基址,位置无关代码用相对偏移和 call/pop 自定位,偏移而非绝对地址才是真相;对策——按 blob 内偏移分析(rizin `s <偏移>`、xref 看相对偏移),Ghidra 用 Raw Binary 导入并从候选入口标函数,别拿绝对地址对齐
|
||||
- **编码器变体(msfvenom 多种编码)**:现象——入口定位后反汇编全是垃圾字节,找不到"正常代码";原因——载荷是编码器生成(shikata_ga_nai/xor_dynamic 等),解码循环执行前一切都是密文;对策——先找解码循环(XOR/ROL 特征指令 + LOOP),用步骤 4 模拟执行直接让它自己解码,或用 msfvenom 生成同编码器对照样本确认指令形态;shikata_ga_nai 的 key 是动态插入的,静态 key 还原法会失效——模拟执行是首选
|
||||
- **宿主依赖(需先还原调用约定)**:现象——模拟执行到某处行为怪异/栈错乱/返回值不对;原因——shellcode 设计为在宿主进程内运行,假定宿主已初始化(已加载 DLL 基址、栈对齐、调用约定);对策——先确定位数与调用约定再 stub(32 位整型/指针参数多在栈上、按 callee/caller 清栈判定;64 位前四整型/指针参数在 rcx/rdx/r8/r9),栈页按宿主近似布局初始化,API 按签名返回合理值
|
||||
- **模拟执行缺 API(stub)**:现象——Unicorn 执行到 `call kernel32...` 或 `syscall` 时 UcError 终止,控制流走不下去;原因——Unicorn 无 OS,内核与 DLL 都不存在;对策——步骤 5 先解析 API hash 还原真实 API 集合,按功能 stub(hook 调用点返回假值 + 记录参数),必要时 hook_mem 补内存映射(VirtualAlloc 返回的"新页"先 mem_map 好)
|
||||
Reference in New Issue
Block a user