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:段,存放代码、数据、符号等
Mach-O 文件结构总览 Header(文件头) 魔数、CPU类型、文件类型、命令数量 Load Commands(加载指令) LC_SEGMENT_64、LC_LOAD_DYLIB、LC_MAIN 等 指定段布局、依赖库、入口点 Segment & Section(段与节) __TEXT(代码段)、__DATA(数据段)、__LINKEDIT(链接信息) 每个段包含多个Section,如 __text、__cstring、__objc_methname

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的登录逻辑:

  1. 先用class-dump找到LoginViewController的方法列表
  2. 在Hopper里搜索loginButtonClicked:的地址
  3. 看伪代码,找到它调用了哪个网络请求函数
  4. 顺着调用链,找到加密参数是怎么生成的

我曾经用这个流程,三天内逆向了一个金融App的签名算法。当然,仅供学习研究哈。

实战小贴士

我的分析流程:

  1. 先用file命令确认架构(arm64还是x86_64)
  2. otool -L看依赖库,判断用了哪些三方SDK
  3. 砸壳(如果是App Store包)
  4. class-dump导出头文件,了解类结构
  5. Hopper打开,定位关键函数,分析逻辑

避坑指南:我曾经在Hopper里修改了一个App的跳转逻辑,结果没注意代码签名,安装后闪退。后来才知道,修改后必须重新签名或者用越狱设备+ldid绕过签名校验。

Mach-O分析是iOS逆向的基石。Header告诉你“这是什么”,Load Commands告诉你“怎么加载”,Segment告诉你“数据在哪”。class-dump和Hopper则是你的左右手,一个扒结构,一个看逻辑。多练几次,你看到二进制文件就不会发怵了。


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