iOS 逆向

Mach-O 文件格式解析:iOS App 逆向的第一步

2025-06-18 wangjun 18 min read
0x

为什么从 Mach-O 开始?

iOS 上的可执行文件、动态库、dylib 都是 Mach-O(Mach Object)格式。无论你想做 静态分析 还是 动态调试,第一步都是读懂这个二进制文件的结构。它决定了代码段在哪里、符号表在哪里、依赖哪些动态库。

Mach-O 的三层结构

一个 Mach-O 文件由三部分组成:

┌─────────────────────┐
│  Mach-O Header      │  -- 文件类型、CPU 架构、加载命令数量
├─────────────────────┤
│  Load Commands      │  -- 描述段布局、入口点、依赖库
├─────────────────────┤
│  Data (Segments)    │  -- __TEXT / __DATA / __LINKEDIT
└─────────────────────┘

Header

头部前 4 字节是魔数 0xFEEDFACE(32 位)或 0xFEEDFACF(64 位)。cputype 字段标识架构:0x0100000C 是 arm64。可以用 otool 直接读取:

$ otool -h App
Mach header
      magic  cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
 0xfeedfacf 16777228          0  0x00           2    50       5848 0x00200085

Load Commands:文件的"配置清单"

Header 之后是一系列 Load Command,每条命令以 LC_ 开头。最常用的几个:

  • LC_SEGMENT_64:声明一个段(__TEXT、__DATA、__LINKEDIT)的虚拟内存映射
  • LC_LOAD_DYLIB:声明依赖的动态库,如 FoundationUIKit
  • LC_MAIN:主程序入口(默认是 _main 符号)
  • LC_CODE_SIGNATURE:代码签名数据位置(逆向后必须重签名才能运行)

常用读取工具

# 查看依赖库
$ otool -L App

# 查看入口点
$ otool -l App | grep -A5 LC_MAIN

# 查看符号表
$ nm -nm App | head -50

__TEXT 段:代码与只读数据

最重要的 section:

  • __text:编译后的机器码,是反汇编与反编译的主要对象
  • __cstring:字符串常量。很多敏感信息(API Key、URL、提示语)会出现在这里
  • __objc_methname:Objective-C 方法名。OC 的方法名总是以字符串形式存在于二进制中,这大大降低了逆向门槛
# 提取字符串
$ strings App | grep -i "api" | head -20

# 用 Ghidra / IDA 打开 __text 进行反编译

符号表与 class-dump

iOS App 通常保留 Objective-C 类信息(__objc_classlist)。class-dump 可以把这些信息还原成近似头文件的接口声明:

$ class-dump App -H -o ./headers

# 输出示例
@interface LoginViewController : UIViewController
@property (nonatomic, copy) NSString *token;
- (void)loginWithAccount:(NSString *)account password:(NSString *)password;
@end

这一步往往几秒钟就能让你看到 App 的全部类结构,是逆向分析的"第一桶金"。

实战流程总结

  1. otool -h / -l 查看头部与加载命令,确认架构与依赖
  2. strings + nm 快速浏览字符串与符号
  3. class-dump 还原 OC 类头文件
  4. Ghidra / IDA 对 __text 做反编译,定位关键函数
  5. 如需动态验证,配合 fridalldb 在运行时观察行为
Mach-O 是 iOS 逆向的地图。看懂地图,才能知道该去哪里下刀。