9.5 KiB
9.5 KiB
name, type, description, capabilities
| name | type | description | capabilities | |
|---|---|---|---|---|
| re-swift | atomic | Swift 二进制逆向:mangling 解码、协议 witness table、闭包捕获、ObjC 互操作。 触发词:Swift逆向、swiftc、witness table、swift demangle、闭包捕获、Swift 产物。 |
|
Swift 逆向
何时使用 / 何时不用
- 用:Swift 产物(Mach-O/ELF 含 Swift mangling 特征
$s前缀,或__swift5_*反射段),需要还原符号/协议一致性/闭包结构 - 用:混合产物中区分 Swift 层与 ObjC/C 层(先走 ObjC 路径再进 Swift 层)
- 不用:纯 ObjC(走 re-ios / re-macos 的 ObjC 路径);纯 C/C++(走 re-cpp-abi)
- 不用:只需普通反编译(re-ghidra / re-ida 直接上)
工具准备
swift-demangle(Swift 工具链,mangling 解码)
- macOS: Xcode 自带(
xcrun swift-demangle);Linux: Swift 官方工具链(swift.org 下载 tar 包解压即用,含swift-demangle);Windows: Swift 官方工具链 - 验证:
echo '$s4main6myFuncyyF' | swift-demangle(macOS 用xcrun swift-demangle;无参数时读 stdin,输出$s... ---> main.myFunc() -> ()) - 批量:
llvm-nm sample | swift-demangle;-simplified出精简名;-expand出解码树
swiftc / swift(可选,对照编译)
- macOS: Xcode 自带;Linux/Windows: 同上官方工具链(含编译器,可本地编译同版本样本对照符号形态)
- 验证:
swift --version
llvm-nm / llvm-objdump(符号与段)
- 安装与验证见 re-cpp-abi 工具准备;Mach-O 侧额外需要 otool(见 re-format-macho 工具准备)
- 用途:
llvm-nm -m sample看符号与段归属;otool -l sample | grep swift5定位反射段
Ghidra / IDA(反编译底座)
操作步骤
按顺序执行,每步产物存档(路径 + sha256,见 re-triage)。
-
产物识别:
strings -a sample | grep -E '^\$s|^_T0|^_\$S' | head llvm-nm sample 2>/dev/null | grep -E 'swift_|^\$s' | head # ELF 用 readelf -s 同效果 otool -l sample 2>/dev/null | grep -c swift5 # Mach-O 反射段存在性 -
mangling 解码(批量):
llvm-nm -m sample | awk '$3 ~ /^\$s/ {print $3}' | swift-demangle | head -20 # ELF: readelf -s sample | awk '$8 ~ /^\$s/ {print $8}' | swift-demangle解码内容:模块名/类型/函数签名/泛型参数展开;结果写入符号表供 re-ghidra / re-ida 重命名。mangling 结构速览见步骤 3 与 layout。
-
mangling 结构速览(手读符号):
$s 前缀 + 长度前缀标识符 + 类型字母 + 签名 $s 13ExampleModule 3Foo C 3bar Si y F └模块(13字符) └类型 └类 └方法 └结果 └参数 └函数 C=类 O=枚举 V=结构体 P=协议;Si=Int SS=String Sq=Optional;y=空元组 ()编码规则:标识符=十进制长度+ASCII;签名顺序是结果类型在前、参数在后,
F结尾表示函数;签名可带修饰位(async/throws,解码输出中显式出现);_To后缀=@objc 桥接 thunk;TE后缀=distributed thunk。详细规则与官方示例见 layout/examples。 -
协议 witness table(动态分派核心):
- witness table 是协议方法的分发表;conformance 记录定位:Mach-O
__swift5_proto段 / ELFswift5_protocol_conformances(协议描述符在__swift5_protos/swift5_protocols),或搜索_swift_getWitnessTable调用点 - 还原:conformance 记录 = 方法槽位数组,槽值 = 对应协议要求的实现地址 → 结合 re-cpp-abi 的 vtable 恢复思路反推"哪个类型 conforms 哪个协议"
- 无符号名时用协议方法调用点反推:找到 witness method 调用处的函数指针表,表序即协议声明序
- 注意间接 conformance(
witness_table_indirect标志):槽位存的是间接表指针而非直接实现地址,需再解一层
- witness table 是协议方法的分发表;conformance 记录定位:Mach-O
-
闭包捕获:
- 闭包是 context 对象:
swift_allocObject调用点 → context 分配 → 捕获字段初始化;context 头部是指向捕获布局描述符的指针,其后按捕获顺序排列字段 - 还原:context 结构体字段 → 捕获变量名/类型(与函数签名关联);引用语义字段由
swift_retain/swift_release调用点确认 - 编译器生成的"partial apply forwarder"符号(解码含
partial apply forwarder)是闭包入点的典型形态
- 闭包是 context 对象:
-
ObjC 互操作:
-
反射元数据(类型结构还原):
otool -l sample | grep -A2 swift5_fieldmd # 字段描述(类型内布局) otool -l sample | grep -A2 swift5_typeref # 类型引用(字符串池) otool -l sample | grep -A2 swift5_reflstr # 反射字符串__swift5_fieldmd记录类型字段名/类型引用;配合__swift5_typeref可还原结构体/类布局,比纯反编译更快。 -
符号化恢复:解码结果批量重命名后,泛型特化符号(大量
Gy后缀)按模块/类型聚类过滤,先恢复"每个类型的核心方法"再处理特化副本(见坑 2)。 -
字节级核对:符号提取/段定位输出异常时,用
xxd按字节核对(mangled 名是长度前缀+ASCII,反射段是数据段内的定长记录)——最小字节样例与核对流程见 examples,别把工具解析失败当"非 Swift 产物"丢弃。
跨域联合
- re-binary-core 网关:本技能归属(选择树「Swift 产物」分支)
- re-ios / re-macos:生态衔接(ObjC 层、App 层分析)
- re-format-macho:Mach-O 段/反射段定位工具链
- re-cpp-abi:vtable/witness table 恢复思路同源
- re-frida:运行时 hook Swift 函数(符号解码后的名称直接作 hook 点)
- re-analyze/analysis-contract:符号表按数据契约传递
常见坑与陷阱
- mangling 版本差异:现象——新版本 Swift 产物解码失败或输出乱码;原因——mangling 方案演进(
$s/$S/_T0三代);对策——用与目标编译版本同代或更新的 swift-demangle,_T0老符号新工具也能解,但新符号老工具不行 - 泛型展开符号爆炸:现象——解码输出大量
$s...Gy泛型实例;原因——泛型特化;对策——按模块/类型聚类过滤,先还原未特化原型 - witness table 无符号名:现象——分发表槽位全是裸地址;原因——协议 conformance 无命名符号;对策——结合协议方法调用点(witness method 调用处)反推槽位语义
- 闭包捕获含引用语义:现象——context 字段是引用计数对象;原因——捕获变量含类实例;对策——按 swift_retain/release 调用点确认引用字段,别当值类型读
- 纯 ObjC 误入:现象——无
$s却按 Swift 流程走;原因——产物实为 ObjC;对策——步骤 1 先确认(无$s/__swift5_*即转) - mangled 名不是普通字符串:现象——
strings/grep 截断或漏检部分符号;原因——mangled 名可含\x01-\x1f控制字符后跟指针字节(symbolic reference),不是以\0结尾的干净串;对策——符号提取用llvm-nm/readelf -s的符号表,不用strings兜底;解析器遇到控制字符要拒读指针字节 - strip 后反射段仍在但符号名没了:现象——
__swift5_proto段还在、$s符号列表为空;原因——strip 只删符号表,反射元数据(conformance/fieldmd)属数据段;对策——从反射段重建类型/协议关系,不依赖符号名 _To后缀被当普通符号:现象——foo.bar.bas(zim:)和@objc foo.bar.bas(zim:)两个符号并存,漏掉后者;原因——_To是 @objc 桥接 thunk;对策——_To与本体成对分析,ObjC 侧调用经 thunk 进 Swift 实现- 入口不是 ObjC 的 main 视角:现象——按
main函数分析发现"入口"只有薄薄一层;原因——Swift 可执行文件入口是@main/顶层代码经运行时初始化(swift_retain 大量出现于启动路径);对策——从main反汇编沿运行时调用链找到第一个用户类型的方法调用点再深入,别在初始化包装里打转 - Swift 4 老产物无反射段:现象——没有
__swift5_*段、符号又被 strip,只剩_T/_T0前缀残迹;原因——反射系统 5.0 才引入;对策——按 mangling 前缀(_TtC等)与swift_retain/swift_release运行时调用点识别 Swift 侧代码,从 ObjC 段(__objc_classlist)补类型骨架