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逆向工程知识体系 安全架构 Secure Enclave 沙箱机制 代码签名 FairPlay DRM IPA结构 Payload/ MyApp.app Info.plist Frameworks/ embedded.mobileprovision 逆向方案 越狱逆向 不越狱逆向 脱壳方案 Hook方案 调试方案 工具链 Theos class-dump Cycript Reveal frida lldb 理解安全架构 → 分析IPA结构 → 选择逆向方案 → 使用工具链

这张图从左到右,就是我们逆向一个iOS App的典型流程。先理解苹果设了哪些防线,再拿到IPA拆开看结构,然后决定用越狱还是不越狱的方案,最后选择合适的工具下手。

好了,这一章的内容就到这儿。记住,逆向工程不是蛮力活,而是对系统理解的深度较量。你越了解苹果怎么设计的,就越能找到突破口。


公众号:蓝海资料掘金营,微信deep3321