14、iOS逆向工程基础:iOS系统安全架构
说实话,做iOS逆向这么多年,我最大的感受就是——苹果的安全体系,就像一座精心设计的城堡。你每攻破一层,都会发现后面还有更坚固的防线。今天咱们就来聊聊这座城堡的构造,以及我们这些「攻城者」常用的工具。
iOS系统安全架构
苹果从硬件到软件,构建了一套纵深防御体系。我把它拆成四个核心模块来讲。
Secure Enclave:硬件级保险箱
Secure Enclave(安全隔区)是集成在A系列芯片里的一个协处理器。它独立于主CPU,有自己的操作系统(叫sepOS),有独立的加密引擎和内存。
说白了,这就是个硬件级别的保险箱。你的指纹数据、面容ID模板、Apple Pay的支付令牌,全锁在这里。主CPU想读?门都没有。它只能给Secure Enclave发请求:「帮我验证一下这个指纹对不对」,然后等一个「是/否」的答复。
关键点:Secure Enclave的固件是苹果签名的,每次启动都会验证完整性。即使你越狱了,也拿不到里面的数据。我在做某个金融App的安全评估时,就深刻体会到这一点——你可以在用户态为所欲为,但Secure Enclave里的生物特征数据,始终是块硬骨头。
沙箱机制:每个App都是孤岛
iOS的沙箱,比Android严格得多。每个App只能访问自己的容器目录,不能随便读别的App的文件。系统文件?更是想都别想。
沙箱的目录结构大概是这样的:
/var/mobile/Containers/Data/Application/<UUID>
├── Documents/ # 用户文档,会备份
├── Library/ # 偏好设置、缓存等
│ ├── Caches/ # 缓存,不会备份
│ ├── Preferences/ # NSUserDefaults 存储
│ └── ...
├── tmp/ # 临时文件
└── ...
每个UUID都是随机生成的,你根本猜不到别的App的路径。而且从iOS 10开始,连NSLog的输出都被限制了——非越狱设备上,你只能看到自己App的日志。
避坑指南:我曾经在逆向一个社交App时,想通过读取系统日志来追踪网络请求。结果发现iOS 10之后,控制台里干干净净。后来才意识到,得用os_log或者直接hook网络库才行。嗯,这就是沙箱带来的限制。
代码签名:不是谁都能跑
iOS上的每一个可执行文件、动态库、甚至脚本,都必须经过苹果签名才能运行。签名验证是内核级别的,启动时检查,运行时也检查。
签名包含两部分:
- 代码目录(CodeDirectory):哈希了所有代码页
- 代码资源(CodeResources):包含了所有资源文件的哈希
你改了一个字节?哈希对不上,系统直接杀掉进程。这就是为什么越狱后要装ldid或者用jailbreakd来伪造签名。
FairPlay DRM:App Store的加密锁
从App Store下载的App,都是经过FairPlay加密的。这个加密是在应用层做的,解密过程由苹果的AppleMobileFileIntegrity内核扩展完成。
加密后的二进制,你拿otool或者class-dump直接看,全是乱码。必须先解密才能分析。这就是为什么越狱设备上需要dumpdecrypted或者Clutch这类工具来脱壳。
注意:脱壳后的App再分发,是违反苹果版权协议的。咱们做安全研究,自己玩玩可以,别拿去商用。我曾经见过有人把脱壳后的付费App重新打包上架,结果吃官司了——这可不是闹着玩的。
IPA文件结构
IPA本质上就是个ZIP包,把后缀改成.zip就能解压。里面长这样:
MyApp.ipa
└── Payload/
└── MyApp.app/ # 真正的应用包
├── MyApp # 可执行文件(Mach-O格式)
├── Info.plist # 应用配置
├── embedded.mobileprovision # 描述文件(企业包才有)
├── Frameworks/ # 内嵌的动态库
├── PlugIns/ # 扩展(Widget、Notification等)
└── .app/ # 资源文件(图片、nib等)
你想想看,逆向的第一步,往往就是拿到这个IPA,解压,然后盯着MyApp这个Mach-O文件发呆。嗯,别急,后面咱们会一步步把它拆开。
越狱与不越狱逆向方案对比
这个问题,我几乎每次培训都会被问到。直接上表格吧:
| 维度 | 越狱逆向 | 不越狱逆向 |
|---|---|---|
| 权限 | root权限,可读写系统分区 | 沙箱限制,只能操作自己的容器 |
| 脱壳 | 用dumpdecrypted/Clutch,简单 | 需要frida-ios-dump或手动砸壳 |
| Hook | Cydia Substrate / Substitute,全局生效 | 只能用frida的Gadget模式,或注入dylib |
| 调试 | debugserver + lldb,无限制 | 只能用Xcode的调试器,限制多 |
| 稳定性 | 容易白苹果,系统更新后可能失效 | 稳定,但需要开发者账号 |
| 适用场景 | 深度逆向、内核研究、越狱插件开发 | 日常App分析、安全测试 |
我个人习惯是:能越狱就越狱,省事。但如果你只有一台主力机,或者客户要求不能越狱,那frida的不越狱方案也够用。说白了,工具是死的,思路是活的。
iOS逆向工具链
工欲善其事,必先利其器。下面这几个,是我包里常备的。
Theos:越狱插件开发框架
Theos是一套Makefile系统,帮你快速搭建越狱插件的工程结构。你写个Tweak,本质上就是写一个动态库,通过Cydia Substrate注入到目标进程里。
一个典型的Tweak长这样:
%hook ClassName
- (void)someMethod {
%log; // 打印调用日志
%orig; // 调用原始方法
// 你的代码
}
%end
编译后生成.dylib,配合plist文件指定要注入的进程。嗯,就这么简单。
class-dump:头文件提取器
class-dump能把Objective-C的运行时信息,还原成头文件。对于OC写的App,这简直是神器。你跑一下:
class-dump -H MyApp -o ./headers
就能得到一堆.h文件,所有类名、方法名、属性名一目了然。不过要注意,Swift写的App,class-dump基本废了——Swift的运行时信息比OC少得多。
小技巧:我一般先用class-dump扫一遍,看看有没有敏感的方法名。比如看到- (NSString *)decryptData:(NSData *)data,那基本就找到加密入口了。
Cycript:运行时交互式控制台
Cycript是Saurik写的,结合了JavaScript和Objective-C的语法。你可以把它注入到运行中的App里,然后动态调用方法、查看对象、修改属性。
比如:
cy# UIApp.keyWindow.recursiveDescription().toString()
// 打印当前窗口的视图层级
cy# [NSUserDefaults standardUserDefaults] setObject:@"hacked" forKey:@"token"]
// 修改UserDefaults
Cycript最大的好处是即时反馈。你改一行代码,马上能看到效果。不过它已经很久没更新了,现在更多人用frida的frida-trace或者frida -l script.js。
Reveal:UI调试神器
Reveal能让你看到App的完整UI层级。每个View的frame、颜色、约束,甚至CALayer的动画,都能实时查看和修改。
用法很简单:越狱设备上装Reveal Loader,然后在设置里开启要分析的App。非越狱设备需要手动注入Reveal的dylib。
我记得有一次,客户说某个页面在iPhone 12上布局错乱。我直接用Reveal一看,发现是一个Auto Layout约束的优先级设错了。改完约束,问题解决。前后不到10分钟。
知识体系总览
下面这张图,是我自己整理的iOS逆向知识框架。你可以把它当成一张地图,随时回来看看自己走到哪了。
这张图从左到右,就是我们逆向一个iOS App的典型流程。先理解苹果设了哪些防线,再拿到IPA拆开看结构,然后决定用越狱还是不越狱的方案,最后选择合适的工具下手。
好了,这一章的内容就到这儿。记住,逆向工程不是蛮力活,而是对系统理解的深度较量。你越了解苹果怎么设计的,就越能找到突破口。