界面已本地化;技能介绍与说明文档按所选语言由 AI 自动翻译,代码、命令、路径和专业术语保留原文,以原作者文档为准。
探索技能
效率与协作 · SKILL.md

re-swift

re-swift

【效率与协作】Swift 二进制逆向:mangling 解码、协议 witness table、闭包捕获、ObjC 互操作。 触发词:Swift逆向、swiftc、witness table、swift demangle、闭包捕获、Swift 产物。

独立 GitHub 快照 · 自动分类 · 以原作者文档为准

Ddslsdzc / rev-skillsApache-2.0

技能说明

GitHub

name: re-swift type: atomic description: > Swift 二进制逆向:mangling 解码、协议 witness table、闭包捕获、ObjC 互操作。 触发词:Swift逆向、swiftc、witness table、swift demangle、闭包捕获、Swift 产物。 capabilities: [lang-runtime-analysis]

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(反编译底座)

  • 安装与验证见 [[re-ghidra]] / [[re-ida]];Swift 符号解码结果用于批量重命名(Ghidra 可脚本化:demangle 输出写回 symbol 表,反编译视图即恢复可读函数名)

操作步骤

按顺序执行,每步产物存档(路径 + sha256,见 [[re-triage]])。

  1. 产物识别:

    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)确认运行时
    • 纯 ObjC 无上述特征 → 转 [[re-ios]] / [[re-macos]];带 __swift5_* 段但 strip 了符号名 → 仍是 Swift(反射元数据不随 strip 消失,见坑 5)
  2. 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]]。

  3. 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]]。

  4. 协议 witness table(动态分派核心):

    • witness table 是协议方法的分发表;conformance 记录定位:Mach-O __swift5_proto 段 / ELF swift5_protocol_conformances(协议描述符在 __swift5_protos / swift5_protocols),或搜索 _swift_getWitnessTable 调用点
    • 还原:conformance 记录 = 方法槽位数组,槽值 = 对应协议要求的实现地址 → 结合 [[re-cpp-abi]] 的 vtable 恢复思路反推"哪个类型 conforms 哪个协议"
    • 无符号名时用协议方法调用点反推:找到 witness method 调用处的函数指针表,表序即协议声明序
    • 注意间接 conformance(witness_table_indirect 标志):槽位存的是间接表指针而非直接实现地址,需再解一层
  5. 闭包捕获:

    • 闭包是 context 对象:swift_allocObject 调用点 → context 分配 → 捕获字段初始化;context 头部是指向捕获布局描述符的指针,其后按捕获顺序排列字段
    • 还原:context 结构体字段 → 捕获变量名/类型(与函数签名关联);引用语义字段由 swift_retain/swift_release 调用点确认
    • 编译器生成的"partial apply forwarder"符号(解码含 partial apply forwarder)是闭包入点的典型形态
  6. ObjC 互操作:

    • @objc 桥接:Swift 类经 ObjC runtime 可见(NSObject 子类、消息发送 objc_msgSend);Swift 侧实现入口是 _To 结尾的桥接 thunk
    • 分析:ObjC 方法列表(class-dump 思路)→ 桥接 thunk → Swift 实现;swift_ 运行时函数调用点区分 Swift 侧逻辑
    • 混合产物:先走 ObjC 路径([[re-ios]] / [[re-macos]])再进 Swift 层;Linux 上的 Swift 产物无 ObjC 层(无 __objc_classlist),纯 ELF 视角分析
  7. 反射元数据(类型结构还原):

    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 可还原结构体/类布局,比纯反编译更快。

  8. 符号化恢复:解码结果批量重命名后,泛型特化符号(大量 Gy 后缀)按模块/类型聚类过滤,先恢复"每个类型的核心方法"再处理特化副本(见坑 2)。

  9. 字节级核对:符号提取/段定位输出异常时,用 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)补类型骨架
我的收藏