iOS逆向储备指南:从设备到工具链的完整基线

发布时间:2026/10/14 15:45:58
iOS逆向储备指南:从设备到工具链的完整基线
不知道你在群里见过几次这种提问class-dump 导出的头文件空白一片Frida attach 上去就闪退Charles 里明明装了证书还是抓不到 https 包。这些问题我几乎每周都能看到而它们的根子多数不在操作层面而在储备层面——你要做 iOS 逆向设备、系统、工具链、知识结构、目标情报这五样东西没备齐就算把某个单独工具的教程倒背如流照样会在第一步翻车。所以这个系列的第 5 篇我想认真聊聊逆向储备这件事。它是我认为整个逆向工程里最容易被低估、却最能拉开效率差距的部分。这篇我会先讲清楚储备到底包含哪些维度再分别从设备环境、静态分析工具、动态调试工具、网络抓包、目标情报收集几个方向给出我自己的储备清单和判断标准最后补上几次真实的翻车复盘。适合刚跑通一遍 Demo 的新手也适合已经会一两个工具但总被环境问题卡住的人。看完之后你至少能建立一套属于自己的逆向储备基线而不是每次拿到新目标都从零开始折腾环境。1. 为什么我不建议你跳过逆向储备这一步1.1 翻车现场背后不是手法不够是储备缺位我先举三个很典型的翻车现场。第一个某新人用 class-dump 导出某资讯 App 的头文件结果得到的头文件几乎全是空的。他以为是工具问题换了两个版本还是不行。实际上这个 App 加了壳主二进制里__TEXT段的代码是加密的class-dump 读到的只是一个带cryptid标记的壳真正的 OC 方法列表根本没暴露出来。没有砸壳这一步class-dump 再怎么换版本都是徒劳。第二个某朋友用 Frida 去 attach 一个社交 App刚连上 App 就闪退。他怀疑是 Frida 脚本写法有问题改来改去浪费了一下午。真正原因是目标 App 做了越狱检测和反调试Frida 在注入早期就触发了它的保护逻辑进程被主动 kill 掉。储备里如果没有目标 App 可能带反调试这个预期你会把时间全花在错误的方向上。第三个抓包党最常遇到WiFi 代理配了CA 证书也装了但 https 请求就是 decrypt 不出来。这里有两个隐藏条件一是 iOS 10.3 之后必须在设置-通用-关于本机-证书信任设置里手动开启证书信任不少人只安装了证书却没做这一步二是目标 App 很可能做了 SSL Pinning服务端证书和客户端预期值对不上代理直接被忽略。这三个场景有一个共同点操作层都会做但环境层、知识层、目标认知层的储备缺位导致所有操作全是白费。所以我一直认为储备不是可选项是整个逆向流程的地基。1.2 把储备拆成四个维度我习惯把逆向储备拆成四块知识储备OC Runtime 的基本模型isa、method_t、方法调配原理、Mach-O 文件格式的 Load Commands 布局、ARM64 常见指令的含义、越狱环境的权限边界、App 签名与沙盒机制。不需要你精通但遇到现象时能快速定位是哪一类问题。工具储备静态、动态、网络、辅助这四类工具不追求数量追求每个阶段有一两个趁手的。关键是要知道每个工具的输出是什么、什么时候该用、什么时候不该用。环境储备调试设备、系统版本、可恢复的完整备份、SSH 通道、CA 证书管理、工具链版本锁。这层最容易被忽略因为平时根本感觉不到它的存在直到系统升级或数据线断了才发现。情报储备目标 App 的 Bundle ID、版本、架构、签名方式、加壳状态、越狱检测类型、第三方 SDK 痕迹、协议特征。情报决定你第一步用静态还是动态也决定你会不会在开头就撞墙。这四个维度不是孤立的。比如你知识储备里知道 Mach-O 有加密偏移你才会在看到otool -l输出cryptid时想到要砸壳你环境储备里有一台稳定可控的越狱机你才不会因为重启后越狱失效而中断分析。所以这篇不是工具列表堆砌而是告诉你每样东西为什么备、怎么备、备到什么程度。2. 设备与系统环境越狱机到底该怎么备2.1 选机的两个硬性条件设备是第一层储备也是最难补的一层。很多人随手拿一台主力机做实验结果越狱失败、数据丢失、系统被锁后悔都来不及。我的建议是单独准备一台专门用来逆向调试的设备不要混用。选机时先看芯片。A11 及以下芯片的设备普遍支持基于硬件引导漏洞的越狱越狱状态在重启后可以保持只要你不在系统层面乱动。A12 及以上芯片的设备大部分走软件漏洞路线优势是可以在较新系统上越狱代价是在某些配置下重启后会失效需要重新触发一次越狱。我个人的基线是至少准备一台支持稳定越狱的老芯片设备作为长期调试底板再根据目标 App 的最低系统要求决定要不要补充新设备。再就是系统版本。越狱工具的适配通常是滞后的新系统发布头两三个月主流越狱往往还没跟上。拿到设备后第一件事就是关掉自动更新甚至装屏蔽升级提示的配置描述文件。我的原则是在目标项目期间设备停留在已验证可用的系统版本上不做任何原地升级。除非有明确必要不要追求最新系统因为一旦越狱失效你要花更多时间恢复环境。2.2 系统版本锁定与恢复兜底光靠关自动更新还不够系统可能会在夜间自动下载固件。我会额外做三件事关闭 App Store 自动更新同时关闭设置里的自动更新开关。安装屏蔽升级提示的描述文件避免系统弹窗骚扰。保存当前系统版本的 SHSH2 文件。这样如果误升级还有机会通过验证恢复到旧版本。恢复兜底同样重要。越狱环境本质上是对系统目录的修改任何一次插件冲突、依赖损坏都可能让你想重刷系统。务必在做任何越狱操作之前先完整备份一次设备数据并把越狱工具包、插件清单、SSH 公钥、开发者证书等全部归档保存。我遇到过一次设备白苹果因为提前有完整备份和固件包重刷加恢复只花了不到半天否则所有环境都要手动重建。2.3 最小化越狱环境越狱机上不要装一堆桌面主题、状态栏插件、快捷手势之类的花哨组件每多一个插件就多一个系统不稳定因素还可能在分析目标 App 时引入干扰。我在这类设备上只保留跟调试相关的必要组件SSH 服务、调试服务、包管理器、以及让测试 App 能正常安装运行的基础补丁。连接方式上USB 优先WiFi 作为备用。USB 连接稳定、速度快尤其是用 LLDB 调试时走 USB 通道的延迟远低于 WiFi。WiFi 连接在远程操作或者设备不方便接数据线时很有用但要注意网络环境是否稳定。我会把设备的 USB 调试授权提前保存好避免换电脑后还要重新信任。环境隔离是另一个容易被忽视的点。如果目标 App 做了设备指纹检测你在同一台越狱机上反复安装卸载痕迹会越来越重。我的习惯是用独立的调试容器或独立用户环境来跑目标 App避免污染正常的系统状态。这样即使目标 App 崩溃或者触发反调试也不会影响我其它测试环境。3. 静态分析武器库把 Mach-O 档案翻个底朝天3.1 从 Mach-O 里取情报的基础操作拿到目标 App 的安装包之后我一般不急着看代码先用几个命令确认它的基本属性。首先用lipo -info看二进制架构确认是 arm64 还是包含 armv7、arm64e。再用otool -l查看 Mach-O 的 Load Commands重点看cryptid和cryptoff这两个字段决定有没有加壳、加密范围从哪开始。如果cryptid为 1说明还没脱壳class-dump 大概率导出不了有效内容。脱壳这一步我用 frida-ios-dump 这类基于 Frida 的方案或者 dumpdecrypted 这样的老牌工具。脱壳完成后再用 class-dump 提取 Objective-C 的头文件。下面是一套我常用的命令行流程# 查看架构 lipo -info 目标App # 查看加密信息 otool -l 目标App | grep -A5 crypt # 脱壳后导出头文件 class-dump -H 目标App -o ./Headersclass-dump 导出的头文件是 OC 层的主线索你会看到每个类的声明、方法名、属性名。配合strings命令提取可读字符串基本能摸清这个 App 的功能模块、URL Scheme、网络接口路径和堆栈线索。3.2 class-dump 的边界与 Swift 的替代路线class-dump 只对 Objective-C 的方法列表有效遇到 Swift 写的类是哑火的。Swift 有自己的一套符号机制类名和方法名会被 mangled 成一串难读的符号class-dump 解析不出来。碰到这种情况我会切换到另外几条路用nm查看符号表过滤 Swift mangled symbol能看出函数名、模块名再结合反汇编器还原逻辑。用strings查找硬编码的字符串比如接口 URL、错误提示、日志前缀然后用反汇编器对字符串地址做交叉引用定位是谁在用这些字符串。在 Hopper 或 Ghidra 里直接看伪代码Swift 方法虽然符号难读但控制流结构比 OC 回调更直观。这里要强调一个认知静态分析不是一步到位的它像拼图。OC 头文件给你框架字符串给你线索反汇编器给你实现细节。三者交叉验证才能还原一个目标 App 的真实逻辑。单独依赖某一个工具都会卡在一半。3.3 反汇编器的选择现在主流反汇编器无非是 Hopper、IDA、Ghidra。我的建议是根据使用习惯和预算来选不必三个都装。工具优势劣势HoppermacOS 原生伪代码质量不错上手快商业化收费重度分析时性能一般IDA插件生态强反编译能力全面价格较高对新手不友好Ghidra免费开源反编译伪代码可用界面较土大型二进制分析偏慢我的个人习惯是先用 Ghidra 做快速全景浏览因为免费且能批量分析适合先把目标 App 的所有类、函数、导入导出表扫一遍。等到需要精细分析某一段核心逻辑时再切回 Hopper 单文件深入。两种工具的伪代码风格略有差异交叉对比也能避免单一工具的误判。3.4 给目标 App 建一份静态情报卡静态分析时我会顺手记录一份情报卡后续动态调试都会围绕它展开。模板大致这样字段内容示例Bundle ID某 App 的 bundle id版本/最低系统4.2.0 / iOS 14.0架构arm64加壳状态已脱壳加密偏移 0x4000关键字符串API 路径、域名、AES 关键词核心类登录逻辑、网络层封装、数据加密类第三方 SDK推送 SDK、统计 SDK、加固 SDK情报卡不需要很正式自己能看懂就行。它的价值在于当你从静态切换到动态时你不会忘记静态阶段已经确认过的信息也不会在动态调试里重复发现同样的接口。我现在每分析一个目标 App都会顺手建一个备忘录文件里面存一张这样的卡。时间久了这就是你自己的逆向知识库。4. 动态调试与 Hook 储备别等要用了才装环境4.1 LLDB debugserver 这套古典组合动态调试我把它分成三层底层指令级调试、方法级 Hook、进程级注入。最底层的自然是 LLDB 配合 debugserver。把 debugserver 放到越狱设备上做好签名和权限处理然后用 LLDB 连接。这里的关键点在于LLDB 直接附加到大多数 App 上会触发系统的ptrace反调试逻辑进程会被终止。所以这套组合真正开工前要先确认目标 App 有没有反调试有的话需要提前做 ptrace 绕过。连接后我常用的命令不多但都很关键# 在 device 上启动 debugserver debugserver 1234 -a 目标进程 # 本地 LLDB 连接 process connect connect://设备IP:1234 # 查看模块基地址 image list -o -f # 对某个地址下断点 breakpoint set -a 0x1004c80 # 读写内存 memory read 0x1004c80 memory write 0x1004c80 0x90没做过底层调试的人会觉得 LLDB 很劝退其实你只需要掌握几个核心动作暂停进程、看模块基址、下断点、读寄存器、改内存。这些技能一旦上手排查疑难杂症时的效率会提升一大截。4.2 Frida 生态快速验证思路的首选如果说 LLDB 是手术刀那 Frida 就是瑞士军刀。Frida 的优势在于脚本化你不用每次重编译而是用 JavaScript/python 脚本动态注入、Hook、trace。从储备角度看Frida 环境要常备而且要注意版本一致性。Frida 分为本地的 frida-tools 和装在设备上的 frida-server两者要做到严格版本匹配否则会一直提示连接失败。我一度以为是自己脚本写错了折腾了半小时才发现是 frida-server 装了个旧版。现在我的习惯是设备上装哪个版本的 frida-server本地就固定用同一个版本。日常操作里frida-ps看进程、frida-trace批量跟踪方法调用、objection做内存探索和简单的绕过都是高频能力。我在验证某个 OC 方法有没有被调用时会直接跑一段小脚本// 打印某个方法的参数和返回值 var clazz ObjC.classes.XXXClass; Interceptor.attach(clazz[-loginWithUser:password:].implementation, { onEnter: function(args) { console.log(login called, user:, new ObjC.Object(args[2])); console.log(password:, new ObjC.Object(args[3])); }, onLeave: function(retval) { console.log(retval:, new ObjC.Object(retval)); } });这套做法的好处是快几秒钟就能看到目标方法到底被没被调用、参数是什么、返回值是什么。等你想明白逻辑了再决定要不要写一个正式的 tweak 工程。4.3 theos 驱动的 Tweak 工程如果需要给某个 App 做一个稳定的功能增强或者长期挂载某个 Hook 逻辑我一般会用 theos 搭一个 tweak 工程。它比 Frida 脚本更适合做完整项目可以编译出 dylib通过包管理器装进越狱机实现进程启动时的自动注入。搭工程很容易但有几个坑我提前说编译时要注意签名处理越狱机上ldid签名是常规操作没有正确签名插件装上去也会生效失败。依赖的 framework 路径要写对尤其是用到私有框架时路径差一个字符就会编译报错。编译产物装到设备上之后先跑一个只打印日志的迷你版确认注入链路通了再写正式逻辑。不要一上来就写几百行后来排查时根本分不清是注入问题还是代码问题。4.4 动态工具选型速查我根据自己的使用频率和场景做了这么一个速查表工具/方案适用场景优点注意点LLDB指令级断点、内存剖析能力最强、系统级上手门槛高反调试绕不过就白搭Frida快速 Hook、批量 trace脚本化、迭代快版本必须匹配部分场景易被检测theos生产级 Tweak可编译可分发编译慢签名易出错CycriptREPL 交互探查类似命令行的临时探索稳定性一般压力大时会挂你不需要全部精通但至少要有一个快速验证方案和一个深度调试方案。我个人的快速方案是 Frida深度方案是 LLDB生产级需求才动用 theos。这个组合目前覆盖了我 90% 的动态调试需求。5. 网络层储备抓包这件事没那么简单5.1 代理链路与证书信任网络抓包是逆向里不可避免的一步因为你要理解这个 App 和服务端是怎么通信的。我常用的方式是在 Mac 上开一个代理然后在设备上把 WiFi 代理指向这台 Mac。流程本身不复杂查 Mac 的局域网 IP设备连同一个 WiFi设置 HTTP 代理安装代理的 CA 证书到设备描述文件。但要注意 iOS 的证书信任机制安装完证书之后一定要去设置-通用-关于本机-证书信任设置里手动开启完全信任否则抓到的 https 流量依然是密文。我在命令行里通常这样操作# 查看本机 IP ifconfig en0 | grep inet # 确认代理端口监听 lsof -i :8888Charles 和 mitmproxy 我都在用。Charles 图形化适合快速分析单个请求mitmproxy 适合写脚本批量处理请求比如自动化修改返回包。储备层面两者任意一个熟练用好就够了不要求都装。5.2 SSL Pinning 的应对与边界当你发现抓包工具能抓到流量但全是在跳证书错误甚至 Target App 无响应时大概率是 SSL Pinning。这个概念简单说就是 App 在客户端也固定了服务端证书或公钥代理伪造的证书在它眼里是无效的。处理思路有两类一类是越狱机上装 SSL Kill Switch 插件全局禁用证书校验逻辑另一类是直接用 Frida 批量 patch 验证方法。我用 Frida 的情况更多因为它可以精确控制只绕过某个特定 SDK 的 Pinning 逻辑而不是对整个系统开刀。这里必须说一句边界这类绕过手段只应该用于你自己拥有或已经获得明确授权的 App 测试。任何形式的越权测试都不在我的分享范围内。你储备这些技术是为了理解和排查自己的项目不是为了绕过别人的安全机制。5.3 长连、WebSocket 和自定义协议的处理很多现代 App 已经不再用传统 HTTP 请求而是用 WebSocket 长连保持实时通信或者直接用 protobuf、msgpack 之类的二进制格式。代理工具默认对 WebSocket 的解析能力有限你需要额外配置子协议解析或者干脆用 Wireshark 抓原始包再手动分析。我遇到过某个 App 的登录握手走 WebSocket抓包工具里只看到一堆二进制帧。后来我用瀑布流分析发现帧头固定几个字节的长度字段后面跟着 payload。识别出这个规律之后自己写了个脚本转成可读 JSON才慢慢看到里面的字段。这个过程考验的是经验和耐心但前提是你的储备里有这个 App 可能不用 HTTP的意识。蜂窝网络环境下没有 WiFi 代理我会退回到 USB 共享网络配合代理或者用 tcpdump 在设备本机抓包导出 pcap 文件。反正不要因为环境变了就停掉网络层的分析灵活切换链路才是储备的意义。6. 目标 App 情报收集动手前先做背景调查6.1 五个必须查的字段拿到一个目标 App我建议先花 10 分钟确认五个字段而不是直接漂着看代码。Bundle ID确定唯一身份防止多版本混淆。版本号和最低系统版本判断它可能用到的系统 API 和兼容策略。架构arm64 还是 arm64e决定你是否需要关注指针认证等机制。签名方式App Store 签名、企业签名还是开发签名影响安装方式和运行环境。加壳状态确定第一步是脱壳还是直接静态分析。这五个字段建立起来之后你已经能判断大致的技术栈。比如一个最低系统版本要 iOS 16 的纯 Swift 项目和一个是 iOS 12 的混合 OC 项目分析路径完全不一样。6.2 越狱检测与防护预判越狱检测是很多目标 App 的第一道门槛。常见类型包括文件系统检测检查是否存在越狱相关的目录或文件。动态链接检测检查进程环境变量、已加载的动态库。沙盒逃逸试探通过某些 API 判断当前进程权限是否超出正常范围。调试器检测基于 ptrace 状态或 sysctl 判断是否被调试。预判的意义在于你可以在起步时就部署对应的绕过方案而不是等动态调试闪退后才开始排查。我的习惯是先用 Frida 跑一个防护探测脚本看看哪些检测点被触发再决定绕过策略。这个步骤放在情报收集阶段能省很多试错。6.3 给每个目标建一份情报卡片我在第 3 节提过静态情报卡这里的情报卡是动态和网络层的补充。一个完整的情报卡片大概长这样维度记录内容进程信息主进程名、子进程、守护进程网络特征域名列表、协议类型、API 路径、关键 Header加密痕迹AES/RSA/ECDH 关键词、IV/Key 的存储位置检测点已触发的越狱检测、反调试、反 Hook 逻辑注入方式已成功的注入入口、失败的原因我每次完成一轮分析都会把这个卡片更新一遍。时间长了你会发现自己对一个目标 App 的理解越来越立体很多之前看不懂的问题会突然连成线。这种情报积累才是逆向储备里最值钱的部分。7. 储备清单落地我的个人习惯与避坑记录7.1 固定版本与归档配置工具链的版本漂移是我踩过最大的坑。某个越狱工具升级了原来的插件全不兼容Frida 更新了设备上 frida-server 没跟上连接就会失败class-dump 换了仓库的新版导出格式和旧版不一样。我现在维护了一份工具版本锁定表记的是当前项目期正在使用的各工具版本号并把对应安装包、配置文件全部归档到一个目录。这样哪怕设备要重刷也能快速恢复到可用状态。别高估自己的记性版本这种细节半年后你绝对记不住不如趁现在写下来。7.2 三类储备陷阱第一类是系统升级。很多人明明锁定过版本某天手滑点了系统更新越狱直接失效整个调试环境跟着报废。碰到这种问题只能重刷耗时不说之前的一些临时改动也会丢。第二类是版本漂移。本地工具和设备端工具各自升级导致版本不对齐。常见的是 Frida其次是一些依赖 Python 版本的辅助脚本。解决方案就是我上面说的锁定版本表任何一方升级之前都要评估兼容性。第三类是不备份。很多新手把越狱环境当成一次性配置觉得坏了就重装。实际上重装一次的隐性成本很高SSH 密钥、证书信任、插件清单、历史调试配置这些都需要重新整理。我现在每做一次大改动前都会用备份工具做完整快照不到一个月就会执行一次。7.3 三次翻车复盘第一次我在一台新设备上升级系统后越狱失效但当时既没保存 SHSH2 也没做完整备份。重刷后花了整整一个晚上才把调试环境恢复到可用状态。从那以后任何设备在进行系统相关操作前我都会强制自己按下备份、记录版本、再动手的流程。第二次Frida 本地升到新版设备上还是旧版 frida-server连接时一直报错。我误判成目标 App 防护升级足足查了两天。最后换回与设备端匹配的本地版本一秒钟就通了。这个教训让我彻底养成了先查版本再查脚本的习惯。第三次某次拿到一个加壳 App忘了先砸壳直接用 class-dump 导头文件导出结果是空的。我还傻乎乎地分析了一小时最后才想起来看otool的加密字段。这个坑提醒我静态情报卡上的一行字比临时排查效率高得多。7.4 别成为武器收藏家储备工具时很容易犯的一个毛病是看到一个教程就安装一个新工具最后机器里塞了几十个二进制每个都只试过一次。我不反对多试工具但真正可靠的储备应该是围绕流程组织的从目标情报、到静态摸底、到动态验证、再到网络分析每个环节有一两个工具能熟练使用就够了。我的建议是给自己做一个最小储备集先把一条完整的分析链路跑通。等到遇到特定问题时再针对性地把链路中某个环节替换成更强的工具。储备是为了解决问题服务的不是为了让你看起来工具很多。说到底iOS 逆向本身的深度和广度都很吓人但绝大多数日常分析工作其实只依赖一套非常朴素的基础储备。设备稳定可控工具版本对齐静态和动态链路通畅再叠加一份清晰的目标情报卡你已经能解决大部分问题。如果你现在还在为环境折腾而头疼不妨照着我上面列的清单一项一项补齐。你会发现很多之前卡住的地方根本不需要你变得更聪明只需要你把这些基础储备做得更扎实。往后做逆向时的状态会完全不一样。