计划执行与检查节点
executing-plans
按既定实施计划完成工作,保持过程检查和反馈。
re-swift【效率与协作】Swift 二进制逆向:mangling 解码、协议 witness table、闭包捕获、ObjC 互操作。 触发词:Swift逆向、swiftc、witness table、swift demangle、闭包捕获、Swift 产物。
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
$s 前缀,或 __swift5_* 反射段),需要还原符号/协议一致性/闭包结构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 出解码树swift --versionllvm-nm -m sample 看符号与段归属;otool -l sample | grep swift5 定位反射段按顺序执行,每步产物存档(路径 + 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 反射段存在性
$s(Swift 5+)、$S(Swift 4.2)、_T0(Swift 4.0)是三代 mangling 前缀;swift_* 运行时导入(swift_retain / swift_release / swift_allocObject)确认运行时__swift5_* 段但 strip 了符号名 → 仍是 Swift(反射元数据不随 strip 消失,见坑 5)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(动态分派核心):
__swift5_proto 段 / ELF swift5_protocol_conformances(协议描述符在 __swift5_protos / swift5_protocols),或搜索 _swift_getWitnessTable 调用点witness_table_indirect 标志):槽位存的是间接表指针而非直接实现地址,需再解一层闭包捕获:
swift_allocObject 调用点 → context 分配 → 捕获字段初始化;context 头部是指向捕获布局描述符的指针,其后按捕获顺序排列字段swift_retain/swift_release 调用点确认partial apply forwarder)是闭包入点的典型形态ObjC 互操作:
@objc 桥接:Swift 类经 ObjC runtime 可见(NSObject 子类、消息发送 objc_msgSend);Swift 侧实现入口是 _To 结尾的桥接 thunkswift_ 运行时函数调用点区分 Swift 侧逻辑反射元数据(类型结构还原):
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 产物"丢弃。
$s/$S/_T0 三代);对策——用与目标编译版本同代或更新的 swift-demangle,_T0 老符号新工具也能解,但新符号老工具不行$s...Gy 泛型实例;原因——泛型特化;对策——按模块/类型聚类过滤,先还原未特化原型$s 却按 Swift 流程走;原因——产物实为 ObjC;对策——步骤 1 先确认(无 $s/__swift5_* 即转)strings/grep 截断或漏检部分符号;原因——mangled 名可含 \x01-\x1f 控制字符后跟指针字节(symbolic reference),不是以 \0 结尾的干净串;对策——符号提取用 llvm-nm/readelf -s 的符号表,不用 strings 兜底;解析器遇到控制字符要拒读指针字节__swift5_proto 段还在、$s 符号列表为空;原因——strip 只删符号表,反射元数据(conformance/fieldmd)属数据段;对策——从反射段重建类型/协议关系,不依赖符号名_To 后缀被当普通符号:现象——foo.bar.bas(zim:) 和 @objc foo.bar.bas(zim:) 两个符号并存,漏掉后者;原因——_To 是 @objc 桥接 thunk;对策——_To 与本体成对分析,ObjC 侧调用经 thunk 进 Swift 实现main 函数分析发现"入口"只有薄薄一层;原因——Swift 可执行文件入口是 @main/顶层代码经运行时初始化(swift_retain 大量出现于启动路径);对策——从 main 反汇编沿运行时调用链找到第一个用户类型的方法调用点再深入,别在初始化包装里打转__swift5_* 段、符号又被 strip,只剩 _T/_T0 前缀残迹;原因——反射系统 5.0 才引入;对策——按 mangling 前缀(_TtC 等)与 swift_retain/swift_release 运行时调用点识别 Swift 侧代码,从 ObjC 段(__objc_classlist)补类型骨架