16、iOS二进制分析:Mach-O文件格式(Header、Load Commands、Segment)、class-dump与Hopper Disassembler
聊到iOS逆向,Mach-O格式是绕不开的坎。我刚开始接触那会儿,对着二进制文件一头雾水,后来才发现,理解了Mach-O,就等于拿到了iOS应用的“骨架图”。今天咱们就把这个骨架拆开看看。
Mach-O是什么?
Mach-O是macOS和iOS上的可执行文件格式。说白了,你的App被编译后,就变成了一个Mach-O文件。它负责告诉系统:“我的代码在哪,数据在哪,该加载哪些库”。
我习惯把Mach-O比作一栋楼:Header是大门上的铭牌,Load Commands是施工图纸,Segment是各个楼层。咱们一层层看。
核心结构三件套:
- Header:文件头,标识架构、类型
- Load Commands:加载指令,告诉dyld怎么加载
- Segment:段,存放代码、数据、符号等
Header:看一眼就知道是谁
用otool -h或者MachOView打开一个二进制,最先看到的就是Header。它包含:
- 魔数(Magic):0xFEEDFACE(32位)或0xFEEDFACF(64位)。嗯,苹果工程师的幽默感。
- CPU类型:ARM64、x86_64等。我遇到过有人把armv7的库扔进iOS 14的项目,直接崩了。
- 文件类型:MH_EXECUTE(可执行文件)、MH_DYLIB(动态库)、MH_BUNDLE(插件)等。
- 命令数量与大小:告诉系统后面有多少个Load Commands。
小技巧:用otool -f可以快速查看一个二进制是胖文件(FAT)还是瘦文件。胖文件里包着多个架构的Mach-O,iOS逆向时经常遇到。
Load Commands:施工图纸
这部分是dyld(动态链接器)的“操作手册”。每个命令以LC_开头,常见的有:
| 命令 | 作用 | 个人备注 |
|---|---|---|
| LC_SEGMENT_64 | 定义段在内存中的布局 | 最常用,几乎每个文件都有 |
| LC_LOAD_DYLIB | 声明依赖的动态库 | 逆向时看这里就知道用了哪些三方库 |
| LC_MAIN | 指定程序入口地址 | 我经常改这个做hook实验 |
| LC_CODE_SIGNATURE | 代码签名信息 | 越狱设备上可以跳过校验 |
| LC_ENCRYPTION_INFO | 加密信息(App Store下载的包) | 解密的第一步就是看这个 |
我曾经在分析一个金融App时,发现它通过LC_LOAD_DYLIB加载了一个自定义的加密库。顺着这个线索,我找到了它的加解密函数。
Segment:真正的血肉
Segment是Mach-O里存放实际内容的区域。最核心的三个段:
__TEXT 段
只读,包含可执行代码和常量。里面的__text节就是你的Objective-C/Swift代码编译后的机器指令。__cstring节存着硬编码字符串——逆向时经常在这里找API Key或者URL。
__DATA 段
可读写,存全局变量、Objective-C的类信息、方法列表等。我特别喜欢看__objc_classlist和__objc_methname,因为class-dump就是从这里提取类结构的。
__LINKEDIT 段
链接信息,包括符号表、字符串表等。dyld靠它做符号绑定。
注意:App Store发布的包,__TEXT段是加密的。你用class-dump直接dump会得到一堆乱码。必须先砸壳(解密),再分析。
class-dump:把Objective-C的底裤扒出来
class-dump是个神器。它能从Mach-O文件里提取出Objective-C的类声明、方法、属性、协议。说白了,它把编译后的二进制还原成头文件的样子。
用法很简单:
class-dump -H 你的App二进制文件 -o 输出目录
举个例子,我dump一个微信的旧版本,直接看到了MMNewSessionViewController和它的方法列表。嗯,这比猜接口快多了。
但要注意:
- class-dump只对Objective-C有效。Swift的类名会被mangle,看起来像
_TtC7AppName11ViewController。 - 必须砸壳后才能dump。我刚开始不知道,对着加密文件dump了半天,全是空的。
Hopper Disassembler:反汇编的瑞士军刀
class-dump只能看到类结构,看不到具体实现。想看代码逻辑?得上Hopper。
Hopper能把机器码反汇编成伪代码(类似C语言)。我常用的功能:
- 导航:通过左侧的标签列表快速跳转到函数
- 重命名:把
sub_1001234改成loginButtonTapped,方便阅读 - 注释:在关键代码行加中文备注
- 修改:直接改汇编指令,然后导出新的二进制(越狱设备上常用)
举个例子,我想分析一个App的登录逻辑:
- 先用class-dump找到
LoginViewController的方法列表 - 在Hopper里搜索
loginButtonClicked:的地址 - 看伪代码,找到它调用了哪个网络请求函数
- 顺着调用链,找到加密参数是怎么生成的
我曾经用这个流程,三天内逆向了一个金融App的签名算法。当然,仅供学习研究哈。
实战小贴士
我的分析流程:
- 先用
file命令确认架构(arm64还是x86_64) - 用
otool -L看依赖库,判断用了哪些三方SDK - 砸壳(如果是App Store包)
- class-dump导出头文件,了解类结构
- Hopper打开,定位关键函数,分析逻辑
避坑指南:我曾经在Hopper里修改了一个App的跳转逻辑,结果没注意代码签名,安装后闪退。后来才知道,修改后必须重新签名或者用越狱设备+ldid绕过签名校验。
Mach-O分析是iOS逆向的基石。Header告诉你“这是什么”,Load Commands告诉你“怎么加载”,Segment告诉你“数据在哪”。class-dump和Hopper则是你的左右手,一个扒结构,一个看逻辑。多练几次,你看到二进制文件就不会发怵了。