
iOS 逆向这事儿,网上教程不少,但大多停在某个点:有人只讲砸壳,有人只贴 Frida 脚本,有人还在用 2018 年的 Cycript 示例。真上手的时候你会发现,卡点往往不在某个命令,而在不知道下一步该换哪条路。
这篇文章干的事很简单:把国内外社区(看雪、博客园、GitHub、OffensiveCon、Ostorlab、Corellium)里反复出现的工具和方法,按一条能落地的流程重新排一遍。尤其把 Swift、Objective-C、混编三条技术栈的差异和常见坑单独拎出来说——这块是大多数人从「会砸壳」到「真能分析」的分水岭。
逆向不是跟代码较劲,是跟一整套防护体系较劲。不了解这些机制,后面很多操作会莫名其妙失败。
| 机制 | 干什么用的 | 逆向时你会碰到 |
|---|---|---|
| FairPlay DRM | ||
| 代码签名 + 沙盒 | ||
| ASLR / PAC | ||
| Keychain / ATS | ||
| 反调试 / 反注入 | ||
| Swift 混淆 | _TtC...,得 demangle |
最传统、资料最多
Frida server、SSH、Clutch/fdump 直接跑
Theos 写 Tweak,运行时 Hook
适合:深度动态分析、插件开发
现状:Dopamine(iOS 15–16)、palera1n、checkra1n 等,注意 rootless 适配
砸壳后的 IPA + insert_dylib 注入 FridaGadget
需要开发者证书或企业签重签名
适合:没有越狱机、只能测自己的包
工具:optool、insert_dylib、ios-deploy
Corellium 等虚拟 iOS 环境
不越狱也能做系统级观测
适合:安全团队、固件/内核研究
成本高,但可复现、可快照
已有解密二进制或自研 Debug 包
Hopper / Ghidra / IDA 反汇编
适合:逻辑梳理、算法还原
Linux 也能做静态,动态基本离不开 macOS + 设备
大多数人走的是 A 或 A+D 组合。如果你只有一台没越狱的 iPhone,也不是完全没戏,只是流程会多几步重签名,且很多系统 API 调用不到。
| 工具 | 用途 | 备注 |
|---|---|---|
| Xcode + CLT | ||
| OpenSSH / iproxy | ||
| libimobiledevice | ||
| Frida | ||
| ipsw |
| 工具 | 干什么 | 关键点 |
|---|---|---|
| Theos | ||
| Logos | ||
| ElleKit / Substitute | ||
| insert_dylib / optool | ||
| ldid / codesign |
App Store 下来的包,可执行文件带 LC_ENCRYPTION_INFO,不跑起来解密,静态工具只能看到一团乱。所谓砸壳,就是让 App 在运行时由系统解密,再把内存里的明文 Mach-O dump 到磁盘。
老教程爱讲 Clutch、dumpdecrypted 手动拷 dylib 到 Documents 目录——能跑,但步骤繁琐。现在更常见的做法是 frida-ios-dump:连上越狱机,一条 Python 命令,按 Bundle ID 直接出解密 IPA。
解密之后,别急着上 Frida。先花半小时把地形摸清,后面 Hook 才不会瞎猜。
class-dump 适合 ObjC 项目,一把梭出头文件,搜关键字(login、jailbreak、encrypt、password)往往就能圈出可疑类。纯 Swift 或混编时头文件会缺不少——混编项目 class-dump + dsdump 各跑一遍,后面有专门章节展开。
把 Mach-O 拖进 Hopper 或 Ghidra,看伪代码、跟字符串交叉引用,是日常操作。比如越狱检测函数,社区案例里常见名字带 isJailbroken,Swift 则是 mangled 符号 _$s9No_Escape12isJailbrokenSbyF 这类——Ghidra 搜字符串往往比搜类名更快。
静态能看出「有什么」,动态才能验证「是不是这里」。Frida 在 iOS 上和 Android 思路一样,语法不同——核心都是 JavaScript 跑在目标进程里,通过 ObjC runtime 或原生符号 Hook。OC 项目用 ObjC API,Swift 项目用 mangled 符号,混编先桥接层再下钻——搞混了就是前面坑清单里那些惨案。
| 任务 | Hook 目标 | 说明 |
|---|---|---|
| 越狱检测绕过 | ||
| SSL Pinning | ||
| 读 Keychain | ||
| 读本地缓存 | ||
| 协议还原 | ||
| 反 Frida 对抗 |

拿到解密二进制,第一件事不是砸 Frida 脚本,而是判断这门 App 主要用什么写的。判断错了,后面全是白忙活——最典型的误会:对着纯 Swift 项目狂跑 class-dump,导出一堆残缺头文件,还以为 App 加了壳。
| 类型 | 怎么认出来 | 优先工具 | 难度 |
|---|---|---|---|
| 纯 ObjC | -[Class method:] 符号 | ||
| 纯 Swift | ObjC.classes 里业务类很少 | ||
| Swift / OC 混编 | |||
| SwiftUI 为主 | SwiftUI.ViewContentView 等结构体符号多 |
ObjC 是 iOS 逆向的「母语」。动态运行时把类、方法、协议全摊在桌面上,工具链也最成熟。如果你刚入门,建议先用 ObjC 为主的 App 练手(老版工具类、社交、支付 SDK 往往还是 ObjC)。
方法调用走 objc_msgSend
类、元类、isa 指针可查
Category 能加方法
Protocol 约束清晰
class-dump 一把梭
dsdump 支持更好
Hopper 伪代码可读
字符串交叉引用好跟
Frida ObjC.classes
Theos %hook 直接写
method swizzling
objection 一键枚举
-S -s 能看到更多符号信息objc_msgSend 交叉引用很顺ObjC.choose(ObjC.classes['XXX']) 找存活实例;Hook - method: 看入参出参| 关注点 | 常见类 / 方法 | 提示 |
|---|---|---|
| 网络层 | ||
| 存储 | ||
| 鉴权 | ||
| 加密 | ||
| 防护 |

Swift 编译器会把函数名「揉」成一串 _$s... 或 _TtC... 的 mangled 符号,用来区分重载、泛型、所有权(owned/borrowed)。你看不懂这串,就没法 Hook,也没法在 Ghidra 里搜到正确函数。
举个例子,ContentView.dummyFunction(flag:) 在二进制里长这样:
更麻烦的是:没有 @objc 标注的纯 Swift 类,根本不会出现在 ObjC runtime 里。你在 Frida 里 ObjC.classes['LoginViewModel'] 搜不到,不代表没有这个类——它只是对 ObjC 不可见。
nm / Module.enumerateExports
frida-trace -i "*关键词*"
swift-demangle 批量还原
dsdump 导 Swift 类型
Module.findExportByName
Interceptor.attach(符号)
frida-swift-bridge 辅助
Release 包靠静态定签名
Bool/Int 多在 x0
String 看 x0+x1
小字符串 ≤15B 内联寄存器
大字符串 x1+32 读堆数据
ARM64 上 Swift 的 String 不是简单指针。社区 CTF 案例里反复验证过:小字符串直接塞在 x0/x1 寄存器里;大字符串 x1 指向堆对象,实际 UTF-8 内容在指针 +32 偏移处。读错了就一堆乱码,还以为 Hook 没生效。
| 版本特征 | 对逆向的影响 | 应对 |
|---|---|---|
| Swift 4/5 过渡期 | ||
| Swift 5+ 稳定 ABI | ||
| async/await | ||
| Actor | ||
| SwiftUI | ||
| Release + strip |
dsdump(Swift 版 class-dump)能导出 Swift 类型信息,比老 class-dump 靠谱;IDA 的 Swift 插件、frida-swift-bridge 可以自动解析部分参数类型。但 Release 包 strip 之后,任何自动化工具都会变笨——静态定签名 + 动态验证仍是老办法。
绝大多数国内 App 是混编:底层网络、支付、老业务模块留着 ObjC,新页面、ViewModel 用 Swift 写。两边通过 Bridging Header 和 @objc 注解打通。逆向时,桥接层往往是最薄的切入口。
| 方式 | 编译器做了什么 | 逆向时能不能看到 | Hook 策略 |
|---|---|---|---|
| 继承 NSObject + @objc | |||
| @objcMembers class | |||
| 纯 Swift(无 @objc) | |||
| @_silgen_name / @_cdecl |
模块名+类名 的 mangled 符号,demangle 后定位真正干活的 Swift 方法verifyLogin:,Swift 侧叫 verifyLogin(password:),mangled 符号又是第三套名字。建议在笔记里建一张「三方对照表」,静态看到一个就记一个,别靠脑子硬记。| 任务 | 纯 ObjC | 纯 Swift | 混编 |
|---|---|---|---|
| 导头文件 | |||
| Frida 枚举类 | |||
| Hopper 伪代码 | |||
| Theos %hook | |||
| LLDB 下断 | |||
| 字符串搜线索 | |||
| 闭包 / Block |

下面这些是社区里反复出现的坑,不分先后,撞上哪个算哪个。每条都写了「怎么认」和「怎么绕」。
['- actualSelector:label:'] 而非猜image list -o -f 看实际基址;断点地址 = slide + 静态偏移br set -n mangledName;或先 attach 再算运行时地址frida --version 和手机上 frida-server -version 必须一致,差一个小版本都可能出问题frida -f bundleId --no-pause .spawn;或 %ctor / 延迟 setTimeout 2000otool -l TargetApp | grep crypt 确认;重新 frida-ios-dump;注意 App Extension 要单独脱lipo -info 确认;lipo -thin arm64 抽出真机 slice 再分析return %orig; 再覆盖不少 App 会主动检测 Frida 线程、端口、/tmp 下的特征文件。这时候 LLDB 反而更「低调」——毕竟是苹果官方调试器,只是配置麻烦。
典型流程:越狱机起 debugserver,Mac 上 lldb 远程 attach。因为 ASLR,Ghidra 里看到的地址要加运行时 slide 才是真实地址。ARM64 上函数返回值在 x0 寄存器,在 ret 前把 x0 改成 0,就能让「是否越狱」返回 NO——这是很多安全课程里的经典练手题,实战里照样管用。
Frida 适合探测,Tweak 适合稳定复现。你确认某个方法 Hook 有效后,用 Theos 写成 dylib,装进越狱机,每次启动自动生效,不用反复 attach。
Logos 语法很直白:%hook ClassName 包住要改的方法,%orig 调原实现,%ctor 写初始化。编译链是 make → package → install,通过 SSH 推到手机的 /Library/MobileSubstrate/DynamicLibraries(rootless 环境路径会变,Makefile 里要加 THEOS_PACKAGE_SCHEME = rootless)。
| 类型 | 特点 | 常见工具 / 思路 |
|---|---|---|
| Flutter | ||
| React Native | ||
| Unity IL2CPP | ||
| 小程序容器 | ||
| SwiftUI 新项目 |
很多人栽在 Flutter 上:class-dump 几乎没东西,Hopper 打开是一堆 C 函数。先确认技术栈再选工具,能少熬好几个通宵。
现在稍微正规点的 App,至少会带几样:越狱检测、调试器检测、完整性校验、SSL Pinning,有些还有 Frida/Substrate 检测。Corellium 去年的分享里提过一个趋势——攻防都在往更隐蔽的方向走:检测系统调用异常、内存映射特征、快照 diff 找 patch 点。
应对没有银弹,只有套路:
做安全审计时,建议把「检测项清单 + 绕过难度 + 业务影响」写进报告,而不是只贴一个「已绕过」的截图。开发侧也能对照 OWASP MASTG(移动应用安全测试指南)逐项查。
| 类别 | 推荐配置 |
|---|---|
| 电脑 | |
| 设备 | |
| 系统版本 | |
| 账号 | |
| 网络 |
iOS 逆向看起来工具庞杂,核心其实就一句话:拿到明文二进制 → 判断 OC/Swift/混编 → 选对工具链 → 运行时验证 → 用 Tweak 或补丁固化结论。Frida 降低了 OC 项目的入门门槛,但 Swift 的 mangling 和 ABI 又把门槛抬回去了——混编项目则是两条路都要走。
如果你刚开始,建议分两条线练:找一款 ObjC 老 App 走通 class-dump → Hopper → Frida → Theos;再找一款 Swift 项目,专门练 nm → swift-demangle → 符号 Hook → 读 String 参数。两条都走过一遍,混编的自然就会了。
这篇把流程、工具、Swift/OC/混编差异和 25 条常见坑都收进来了。实际动手时当手册翻,比收藏十篇碎片化教程管用。
夜雨聆风