Skip to content

03 - 系统原理

目录

  1. 启动原理:dyld 加载流程
  2. Mach-O 文件格式深度分析
  3. 动态链接机制
  4. 进程管理与调度
  5. 安全机制:代码签名与沙盒
  6. 越狱检测
  7. 面试题汇总

1. 启动原理:dyld 加载流程

1.1 iOS 应用启动全链路(源码级)

iOS 应用启动流程(完整链路):
┌──────────────────────────────────────────────────────────────────────┐
│                                                                      │
│  第 1 阶段:内核加载(Kernel Phase)                                  │
│  ─────────────────────────────────────────────────────────────      │
│                                                                      │
│  1. 用户点击 App 图标                                               │
│  ↓                                                                   │
│  2. 内核启动新进程(fork + exec)                                    │
│  ↓                                                                   │
│  3. 加载 Mach-O 可执行文件到内存                                      │
│  ↓                                                                   │
│  4. 初始化 Mach-O 段(__TEXT/__DATA/__LINKEDIT)                     │
│  ↓                                                                   │
│  5. 设置进程内存布局                                                  │
│  ↓                                                                   │
│  6. 跳转到 dyld(动态链接器)                                        │
│                                                                      │
│  ─────────────────────────────────────────────────────────────      │
│                                                                      │
│  第 2 阶段:dyld 加载(Dynamic Linker Phase)                        │
│  ─────────────────────────────────────────────────────────────      │
│                                                                      │
│  7. dyld 加载主可执行文件                                             │
│  ┌──────────────────────────────────────────────────────────┐       │
│  │  dyld 加载流程:                                               │
│  │  ├─ 解析依赖(依赖 framework)                                │
│  │  ├─ 加载依赖(按依赖图顺序)                                  │
│  │  ├─ 符号绑定(动态链接)                                      │
│  │  ├─ 初始化(调用 +load 方法)                                │
│  │  └─ 准备入口点                                                │
│  └──────────────────────────────────────────────────────────┘       │
│                                                                      │
│  8. dyld 调用 _objc_init                                             │
│  ┌──────────────────────────────────────────────────────────┐       │
│  │  _objc_init 工作:                                              │
│  │  ├─ 初始化 Class Hash Table(类缓存表)                        │
│  │  ├─ 初始化 Method Caches(方法缓存)                           │
│  │  ├─ 初始化 Protocol Table(协议表)                            │
│  │  ├─ 初始化 Weak Reference Table(弱引用表)                    │
│  │  └─ 初始化 Associate Objects(关联对象)                       │
│  └──────────────────────────────────────────────────────────┘       │
│                                                                      │
│  9. dyld 调用 _objc_loadImages                                         │
│  ┌──────────────────────────────────────────────────────────┐       │
│  │  _objc_loadImages 工作:                                         │
│  │  ├─ 读取 MH_OBJECT 段的 __objc_classlist 段                    │
│  │  ├─ 为每个类创建 Class 结构体                                   │
│  │  ├─ 解析 Class 的 Ivar/Method/Protocol                         │
│  │  ├─ 创建 metaclass                                              │
│  │  ├─ 调用所有 +load 方法(类 → 分类 → 协议)                    │
│  │  └─ 注册类到 class hash table                                    │
│  └──────────────────────────────────────────────────────────┘       │
│                                                                      │
│  10. dyld 调用 main()                                                │
│  ┌──────────────────────────────────────────────────────────┐       │
│  │  main() 函数(UIApplicationMain):                            │
│  │  ├─ 初始化 UIApplication                                       │
│  │  ├─ 加载 Info.plist                                             │
│  │  ├─ 创建 AppDelegate(初始化)                                   │
│  │  └─ 启动 RunLoop(无限循环)                                     │
│  └──────────────────────────────────────────────────────────┘       │
│                                                                      │
│  11. RunLoop 开始运行                                                 │
│  ┌──────────────────────────────────────────────────────────┐       │
│  │  RunLoop 循环:                                                  │
│  │  • 等待事件(UI 事件、定时器、网络回调)                        │
│  │  • 处理事件                                                      │
│  │  • 回到等待                                                      │
│  │  • (永远不会退出)                                             │
│  └──────────────────────────────────────────────────────────┘       │
│                                                                      │
└───────────────────────────────────────────────────────────────────┘

启动时序图:
┌───────────────────────────────────────────────────────────────────┐
│                                                                    │
│  Time 0ms    →    内核加载 Mach-O 文件                              │
│  Time 1ms    →    dyld 开始加载                                      │
│  Time 5ms    →    依赖 framework 加载                                │
│  Time 10ms   →    符号绑定完成                                       │
│  Time 15ms   →    +load 方法调用完成                                │
│  Time 20ms   →    _objc_init 完成                                   │
│  Time 25ms   →    main() 执行                                       │
│  Time 30ms   →    UIApplicationMain 创建                            │
│  Time 35ms   →    RunLoop 启动                                      │
│  Time 40ms   →    应用可用                                            │
│                                                                    │
│  冷启动总时间:< 100ms(理想情况)                                    │
│  冷启动总时间:200-500ms(典型应用)                                 │
│  冷启动总时间:> 1s(启动慢的应用)                                 │
│                                                                    │
└───────────────────────────────────────────────────────────────────┘

启动优化策略:
┌──────────────────────────────────────────────────────┐
│ 优化手段                  │ 效果           │ 复杂度   │
├────────────────────────────────────┼─────────────┼───────┤
│ 延迟加载                    │ 减少 30-50%  │ ⭐        │
│ 懒加载资源                  │ 减少 20-40%  │ ⭐       │
│ 优化 +load 方法              │ 减少 10-20%  │ ⭐⭐     │
│ 预链接(Pre-linking)        │ 减少 10-15%  │ ⭐       │
│ 减少 Framework 数量          │ 减少 5-10%   │ ⭐⭐     │
│ 符号剥离(Dead Code Strip)  │ 减少包体积    │ ⭐       │
│ 使用 Bitcode                 │ 构建优化      │ ⭐⭐⭐    │
└───────────────────────────────────────────────────┘
*/

1.2 dyld 动态链接器详解

objc
/*
dyld(Dynamic Link Editor)详解:

核心职责:
• 加载 Mach-O 可执行文件和 framework
• 解析动态符号(动态链接)
• 调用初始化函数(_init 段)
• 调用 +load 方法
• 创建 RunLoop
• 调用 main()

dyld 加载流程:
┌─────────────────────────────────────────────────────────────┐
│                                                                │
│  1. 加载主可执行文件                                             │
│     • 读取 Mach-O 头部                                            │
│     • 验证签名                                                     │
│     • 映射到内存                                                    │
│                                                                │
│  2. 解析依赖(依赖图)                                              │
│     • 读取 __LINKEDIT 段的依赖列表                                  │
│     • 按依赖图顺序递归加载(BFS 广度优先)                           │
│     • 系统 framework 优先(/System/Library/Frameworks)         │
│     • 第三方 framework 后加载(@rpath/@loader_path)              │
│                                                                │
│  3. 符号绑定(Dynamic Binding)                                   │
│     • 读取 __LINKEDIT 段的符号表                                    │
│     • 解析每个 symbol 的地址                                       │
│     • 更新 GOT(Global Offset Table)                           │
│     • 更新 PLT(Procedure Linkage Table)                       │
│                                                                │
│  4. 初始化(Initialization)                                      │
│     • 调用 _init 段中的初始化函数                                  │
│     • 调用 ObjC 的 +load 方法(按加载顺序)                        │
│     • 调用 C++ 的静态构造函数                                      │
│                                                                │
│  5. 准备入口点                                                      │
│     • 设置 main() 作为入口                                        │
│     • 启动 RunLoop                                                │
│                                                                │
│ Mach-O 加载方式:                                                │
│ • MH_EXECUTE(可执行文件)                                           │
│ • MH_DYLIB(动态库)                                                │
│ • MH_DYLIB_STUB(动态库存根)                                      │
│ • MH_BUNDLE(插件)                                                │
│                                                                │
│ @rpath / @loader_path / @executable_path 的区别:                │
│ • @executable_path:可执行文件所在路径                              │
│ • @loader_path:加载当前库的模块所在路径                            │
│ • @rpath:运行时搜索路径(LD_RUNPATH_SEARCH_PATHS)             │
│                                                                │
└──────────────────────────────────────────────────────────────┘

符号绑定流程:
┌───────────────────────────────────────────────────────┐
│                                                          │
│  未解析符号(undefined symbol):                          │
│  ├─ 链接时解析(静态链接)                                  │
│  ├─ 运行时解析(动态链接)                                  │
│                                                          │
│  GOT(Global Offset Table):                              │
│  ├─ 存储动态链接符号的实际地址                               │
│  ├─ 首次访问时填充                                          │
│  └─ 后续访问直接读取                                        │
│                                                          │
│  PLT(Procedure Linkage Table):                         │
│  ├─ 函数调用入口                                             │
│  ├─ 首次调用时触发 dyld 解析                                │
│  └─ 解析后直接跳转到函数地址                                │
│                                                          │
│  性能影响:                                                │
│  • GOT/PLT 增加二进制文件体积                                │
│  • 首次访问有延迟(符号解析)                               │
│  • 减少动态依赖可以减少启动时间                              │
│                                                          │
└───────────────────────────────────────────────────────────┘
*/

2. Mach-O 文件格式深度分析

2.1 Mach-O 文件结构

objc
/*
Mach-O(Mach Object)文件结构:
┌────────────────────────────────────────────────────────────┐
│                                                                │
│  Mach-O 头部(Mach-O Header)                                  │
│  ┌──────────────────────────────────────────────────────┐    │
│  │  magic(魔数)                                          │    │
│  │  cputype(CPU 类型)                                   │    │
│  │  cpusubtype(CPU 子类型)                              │    │
│  │  filetype(文件类型)                                   │    │
│  │  ncmds(命令数量)                                     │    │
│  │  sizeofcmds(命令总大小)                               │    │
│  │  flags(标志位)                                       │    │
│  │  reserved(保留字段)                                   │    │
│  └──────────────────────────────────────────────────────┘    │
│                                                                │
│  Load Commands(加载命令)                                      │
│  ┌──────────────────────────────────────────────────────┐    │
│  │  LC_SEGMENT_64:内存段定义                              │    │
│  │  LC_LOAD_DYLIB:加载动态库                              │    │
│  │  LC_MAIN:入口点                                        │    │
│  │  LC_FUNCTION_STARTS:函数起始地址                       │    │
│  │  LC_CODE_SIGNATURE:代码签名                            │    │
│  │  LC_ENCRYPTION_INFO:加密信息                           │    │
│  └──────────────────────────────────────────────────────┘    │
│                                                                │
│  Segments(内存段)                                             │
│  ┌──────────────────────────────────────────────────────┐    │
│  │  __PAGEZERO(页零段,空段,防止空指针访问)             │    │
│  │  __TEXT(代码段)                                      │    │
│  │  ├─ __text(代码)                                    │    │
│  │  ├─ __const(常量)                                   │    │
│  │  ├─ __cstring(字符串常量)                            │    │
│  │  ├─ __objc_methname(方法名)                         │    │
│  │  ├─ __objc_methtype(方法类型)                        │    │
│  │  ├─ __objc_classname(类名)                           │    │
│  │  ├─ __objc_selrefs(选择器引用)                      │    │
│  │  ├─ __objc_protorefs(协议引用)                      │    │
│  │  └─ __objc_classrefs(类引用)                        │    │
│  │                                                       │    │
│  │  __DATA(数据段)                                     │    │
│  │  ├─ __got(全局偏移表)                               │    │
│  │  ├─ __nl_symbol_ptr(名称链接符号指针)              │    │
│  │  ├─ __la_symbol_ptr(延迟符号指针)                   │    │
│  │  ├─ __objc_classlist(类列表)                        │    │
│  │  ├─ __objc_catlist(分类列表)                        │    │
│  │  ├─ __objc_protolist(协议列表)                      │    │
│  │  ├─ __objc_const(常量)                              │    │
│  │  ├─ __objc_ivar(实例变量)                           │    │
│  │  ├─ __objc_data(数据)                              │    │
│  │  └─ __bss(未初始化数据)                            │    │
│  │                                                       │    │
│  │  __LINKEDIT(链接编辑段)                              │    │
│  │  ├─ 符号表                                             │    │
│  │  ├─ 依赖列表                                           │    │
│  │  └─ 代码签名                                           │    │
│  └──────────────────────────────────────────────────────┘    │
│                                                                │
│  关键理解:                                                     │
│  • Mach-O 是 iOS/macOS 的可执行文件格式                        │
│  • 类似 ELF(Linux)或 PE(Windows)                         │
│  • 包含代码、数据、符号表、动态依赖等所有信息                    │
│  • 通过 otool / nm / size 等工具可以查看 Mach-O 文件            │
│                                                                │
│  Mach-O 工具命令:                                             │
│  • otool -fv Mach-O 查看依赖                                    │
│  • otool -L Mach-O 查看动态链接                                │
│  • nm Mach-O 查看符号                                          │
│  • size Mach-O 查看大小                                        │
│  • dwarfdump Mach-O 查看 DWARF 调试信息                        │
│  • lipo -info Mach-O 查看架构                                  │
│                                                                │
└───────────────────────────────────────────────────────────────┘
*/

2.2 Mach-O 架构与 Fat Binary

objc
/*
Fat Binary(胖二进制)结构:
┌─────────────────────────────────────────────┐
│                                                    │
│  Fat Binary(通用二进制)                             │
│  ┌──────────────────────────────────────────────┐
│  │  Fat Header                                    │
│  │  ├─ magic(Fat Magic Number)                 │
│  │  └─ nfat(架构数量)                           │
│  └────────────────────────────────────────────┘
│                                                    │
│  Fat Archive(架构列表)                              │
│  ┌──────────────────────────────────────────────┐
│  │  CPU 1(ARM64)                               │
│  │  ├─ offset(偏移)                            │
│  │  ├─ size(大小)                              │
│  │  ├─ align(对齐)                              │
│  │  └─ Mach-O 二进制                             │
│  │                                                │
│  │  CPU 2(ARM64e)                              │
│  │  ├─ offset(偏移)                            │
│  │  ├─ size(大小)                              │
│  │  ├─ align(对齐)                              │
│  │  └─ Mach-O 二进制                             │
│  │                                                │
│  │  CPU 3(x86_64)(模拟器)                      │
│  │  ├─ offset(偏移)                            │
│  │  ├─ size(大小)                              │
│  │  ├─ align(对齐)                              │
│  │  └─ Mach-O 二进制                             │
│  └─────────────────────────────────────────────┘
│                                                    │
│  特点:                                               │
│  • 一个 Fat Binary 包含多个架构的 Mach-O 文件        │
│  • iOS 发布时拆分为单架构(strip 去除未用的架构)     │
│  • Xcode Archive 时生成 Fat Binary                   │
│  • lipo 工具可以操作 Fat Binary                      │
│  • 模拟器用 x86_64,真机用 arm64/arm64e              │
│                                                    │
│  架构类型:                                           │
│  • ARM64(iPhone 5s 及以后)                          │
│  • ARM64e(iPhone XS 及以后,含 RNDKey)             │
│  • x86_64(模拟器)                                 │
│  • armv7(旧设备,已废弃)                            │
│  • armv7s(iPhone 5,已废弃)                        │
│                                                    │
│  代码签名与 Fat Binary:                              │
│  • 每个架构都需要独立的代码签名                        │
│  • __LINKEDIT 段中包含签名数据                         │
│  • 签名验证在进程启动时进行                            │
│                                                    │
└───────────────────────────────────────────────────────┘
*/

3. 动态链接机制

3.1 动态链接原理

objc
/*
动态链接机制(Dynamic Linking):
┌────────────────────────────────────────────────────────────┐
│                                                                │
│  动态链接流程:                                                 │
│  ┌──────────────────────────────────────────────────────┐    │
│  │  1. 程序启动,dyld 加载依赖 framework                     │    │
│  │  ↓                                                    │    │
│  │  2. 遍历 __LINKEDIT 段的依赖列表                           │    │
│  │  ↓                                                    │    │
│  │  3. 对每个依赖 framework:                               │    │
│  │     • 查找路径(@rpath/@loader_path/@executable_path)    │
│  │     • 加载到进程内存                                     │
│  │     • 解析符号                                             │
│  │     • 更新全局符号表                                       │
│  │  ↓                                                    │    │
│  │  4. 符号绑定(Symbol Binding):                           │
│  │     • 读取 GOT/PLT                                     │
│  │     • 解析未解析的符号                                    │
│  │     • 填充 GOT(首次访问时)                               │
│  │  ↓                                                    │    │
│  │  5. 调用 _init 段中的初始化函数                             │
│  │  ↓                                                    │
│  │  6. 调用 ObjC 的 +load 方法                              │
│  │  ↓                                                    │
│  │  7. 进入 main()                                          │
│  └──────────────────────────────────────────────────────┘    │
│                                                                │
│  链接方式对比:                                                 │
│  ┌──────────┬──────────────────┬────────────────┬─────────┐  │
│  │ 链接方式   │ 优点              │ 缺点              │ 适用场景  │  │
│  ├──────────┼──────────────────┼────────────────┼─────────┤  │
│  │ 静态链接   │ 无运行时开销      │ 包体积大           │ 内库  │  │
│  │ 动态链接   │ 共享库、减小体积  │ 首次加载慢        │ 系统库 │  │
│  │ 延迟绑定   │ 按需加载          │ 首次访问有延迟    │ 默认   │  │
│  └──────────┴──────────────────┴────────────────┴─────────┘  │
│                                                                │
│  性能优化:                                                     │
│  • 减少动态依赖数量                                             │
│  • 使用 -dead_strip 剥离无用代码                                │
│  • 使用 Bitcode 优化                                            │
│  • 使用 LTO(Link Time Optimization)                         │
│                                                                │
└────────────────────────────────────────────────────────┐   │
*/

3.2 符号查找与绑定

objc
/*
符号查找与绑定(Symbol Lookup & Binding):
┌───────────────────────────────────────────────────────┐
│                                                          │
│  符号查找顺序:                                           │
│  ┌─────────────────────────────────────────────┐        │
│  │  1. 全局符号表(dyld 维护)                     │        │
│  │  2. 依赖 framework 的符号表                     │        │
│  │  3. 系统框架的符号表                            │        │
│  │  4. 未解析符号 → 动态解析(runtime)           │        │
│  └─────────────────────────────────────────────┘        │
│                                                          │
│  符号类型:                                               │
│  ├─ UND(未定义)— 需要动态链接                            │
│  ├─ ABS(绝对地址)— 链接时已知                           │
│  ├─ SECT(段内地址)— 链接时已知                           │
│  ├─_PBOTH(预绑定)— 预绑定符号                           │
│  └─ W_LSYM(局部符号)— 链接器内部使用                    │
│                                                          │
│  符号绑定机制:                                            │
│  ├─ GOT(Global Offset Table):存储动态链接符号的地址      │
│  ├─ PLT(Procedure Linkage Table):函数调用入口            │
│  ├─ GOT 首次访问时触发 dyld 解析                           │
│  └─ PLT 跳转到 GOT,GOT 中存储实际地址                    │
│                                                          │
│  性能影响:                                               │
│  ├─ 符号解析在运行时进行,有延迟                            │
│  ├─ 减少动态依赖可以减少符号解析时间                        │
│  └─ 使用 bitcode 可以在 App Store 优化符号                 │
│                                                          │
└────────────────────────────────────────────────────────┘
*/

4. 进程管理与调度

4.1 iOS 进程模型

objc
/*
iOS 进程模型:
┌───────────────────────────────────────────────────────┐
│                                                          │
│  进程状态机:                                             │
│  ┌─────────────────────────────────────────────┐        │
│  │  New → Running → Waiting → Terminated       │        │
│  │    │         ↓             ↑                 │        │
│  │    └───── Suspended ←────────────────────────┘        │
│  │                                                         │
│  │  Running → 进程正在 CPU 执行                              │
│  │  Waiting → 进程等待资源(I/O、网络等)                   │
│  │  Suspended → 进程挂起(内存管理)                       │
│  │  Terminated → 进程终止                                    │
│  │  New → 进程刚创建                                       │
│  └─────────────────────────────────────────────┘        │
│                                                          │
│  iOS 进程状态转换:                                       │
│  ├─ Active(活跃)→ 前台运行                              │
│  ├─ Inactive(非活跃)→ 前台但被阻塞(来电等)              │
│  ├─ Background(后台)→ 后台运行                          │
│  ├─ Suspended(挂起)→ 系统暂停,冻结内存                  │
│  └─ Not Running(未运行)→ 进程已终止                      │
│                                                          │
│  进程优先级:                                             │
│  ┌─────────────────────────────────────────────┐        │
│  │  优先级(QoS 类别)                             │        │
│  ├─ User Interactive(用户交互)— 最高             │        │
│  ├─ User Initiated(用户发起)— 高                │        │
│  ├─ Utility(工具)— 中                           │        │
│  ├─ Background(后台)— 低                        │        │
│  └─ Accessibility(无障碍)— 最低                  │        │
│  └─────────────────────────────────────────────┘        │
│                                                          │
│  进程调度策略:                                           │
│  ├─ 优先级抢占调度                                       │
│  ├─ 时间片轮转调度                                       │
│  └─ 实时优先级调度                                       │
│                                                          │
└────────────────────────────────────────────────────────┘
*/

4.2 内存管理

objc
/*
iOS 内存模型:
┌───────────────────────────────────────────────────────┐
│                                                          │
│  内存区域划分:                                           │
│  ┌─────────────────────────────────────────────┐        │
│  │  栈(Stack)                                │        │
│  │  • 局部变量                                  │        │
│  │  • 函数参数                                  │        │
│  │  • 自动释放池                                │        │
│  │  • 线程局部存储(TLS)                         │        │
│  │  • 大小固定,增长方向:高地址 → 低地址           │        │
│  │                                               │        │
│  │  堆(Heap)                                 │        │
│  │  • malloc/calloc/realloc/new                 │        │
│  │  • ARC 管理的对象                            │        │
│  │  • 大小动态,增长方向:低地址 → 高地址           │        │
│  │  • 由 ARC/内存管理器管理                        │        │
│  │                                               │        │
│  │  __TEXT 段(代码段)                          │        │
│  │  • 可执行代码                                  │        │
│  │  • 常量数据                                    │        │
│  │                                               │        │
│  │  __DATA 段(数据段)                          │        │
│  │  • 静态变量                                    │        │
│  │  • 全局变量                                    │        │
│  │  • GOT/PLT 表                                  │        │
│  │                                               │        │
│  │  __LINKEDIT 段(链接编辑段)                     │        │
│  │  • 符号表                                      │        │
│  │  • 依赖列表                                    │        │
│  │  • 代码签名                                    │        │
│  └─────────────────────────────────────────────┘        │
│                                                          │
│  内存管理对比:                                           │
│  ├─ ARC:编译器自动插入 retain/release                    │
│  ├─ MRC:手动管理内存(OC 旧项目)                        │
│  ├─ GC:Apple 已废弃                                     │
│  └─ malloc/free:底层内存分配                            │
│                                                          │
│  内存优化策略:                                           │
│  ├─ 减少堆分配次数                                       │
│  ├─ 使用对象池/缓存                                       │
│  ├─ 及时释放不再需要的引用                                │
│  ├─ 使用 Weak 引用避免循环                              │
│  └─ 使用 Instruments 检测泄漏                              │
│                                                          │
└────────────────────────────────────────────────────────┘
*/

5. 安全机制:代码签名与沙盒

5.1 代码签名

objc
/*
代码签名(Code Signing):
┌───────────────────────────────────────────────────────┐
│                                                          │
│  代码签名的目的:                                         │
│  • 验证应用的完整性和来源                                  │
│  • 防止应用被篡改                                        │
│  • 确保只运行签名过的代码                                  │
│                                                          │
│  签名流程:                                               │
│  ┌─────────────────────────────────────────────┐        │
│  │  1. 开发者生成密钥对                           │        │
│  │  ↓                                           │        │
│  │  2. 向 Apple 申请证书(Provisioning Profile)   │        │
│  │  ↓                                           │        │
│  │  3. Xcode 使用证书对 Mach-O 文件签名            │        │
│  │  ↓                                           │        │
│  │  4. 签名数据存储在 __LINKEDIT/CodeSignature 段  │        │
│  │  ↓                                           │        │
│  │  5. 安装时验证签名                              │        │
│  │  ↓                                           │        │
│  │  6. 启动时内核验证签名                          │        │
│  └─────────────────────────────────────────────┘        │
│                                                          │
│  签名验证阶段:                                           │
│  ├─ 安装验证(codesign 工具)                            │
│  ├─ 启动验证(内核 dyld 验证)                           │
│  └─ 运行时验证(可选)                                   │
│                                                          │
│  签名相关文件:                                           │
│  ├─ Entitlements(权限配置)                             │
│  ├─ Provisioning Profile(描述文件)                    │
│  ├─ Certificate(证书)                                  │
│  └─ Keychain(密钥链)                                   │
│                                                          │
│  签名检查命令:                                           │
│  • codesign -dvv Mach-O 查看签名信息                     │
│  • codesign --verify Mach-O 验证签名                     │
│  • codesign -s - Mach-O 签名                            │
│                                                          │
└────────────────────────────────────────────────────────┘
*/

5.2 沙盒机制

objc
/*
沙盒(Sandbox)机制:
┌───────────────────────────────────────────────────────┐
│                                                          │
│  沙盒结构:                                               │
│  ┌─────────────────────────────────────────────┐        │
│  │  Documents/                                │        │
│  │  • 用户数据(持久化存储)                      │        │
│  │  • iTunes/iCloud 同步                        │        │
│  │                                               │        │
│  │  Library/                                  │        │
│  │  ├─ Caches/(缓存)                         │        │
│  │  ├─ Preferences/(偏好设置)                │        │
│  │  └─ Application Support/(应用数据)         │        │
│  │                                               │        │
│  │  tmp/                                      │        │
│  │  • 临时文件(系统可能清理)                   │        │
│  │                                               │        │
│  │  ⚠️ 沙盒限制:                               │        │
│  │  • 不能访问其他 App 的目录                     │
│  │  • 不能写入沙盒外目录                         │
│  │  • 需要权限请求(相机/相册/位置等)          │        │
│  └─────────────────────────────────────────────┘        │
│                                                          │
│  权限请求流程:                                           │
│  ┌─────────────────────────────────────────────┐        │
│  │  1. 在 Info.plist 声明权限                     │        │
│  │  ↓                                           │        │
│  │  2. 运行时请求权限(AVCapture、CLLocationManager)│        │
│  │  ↓                                           │        │
│  │  3. 系统弹窗提示用户                           │        │
│  │  ↓                                           │        │
│  │  4. 用户授权/拒绝                              │        │
│  │  ↓                                           │        │
│  │  5. 处理授权结果                              │        │
│  └─────────────────────────────────────────────┘        │
│                                                          │
│  Privacy Manifest(隐私清单):                           │
│  • iOS 17+ 强制要求                                     │
│  • 声明 App 使用的 API 和数据收集行为                      │
│  • App Store 审核时检查                                   │
│  • 不合规可能导致拒审                                      │
│                                                          │
└────────────────────────────────────────────────────────┘
*/

6. 越狱检测

6.1 常见越狱检测方式

objc
/*
越狱检测策略:
┌───────────────────────────────────────────────────────┐
│                                                          │
│  1. 检查已知越狱文件                                      │
│  ├─ /Applications/Cydia.app                            │
│  ├─ /Library/MobileSubstrate/MobileSubstrate.dylib      │
│  ├─ /usr/bin/ssh                                     │
│  ├─ /usr/sbin/sshd                                    │
│  └─ /etc/ssh/sshd_config                              │
│                                                          │
│  2. 检查可执行文件权限                                    │
│  ├─ 尝试写入 /var/mobile/                              │
│  ├─ 尝试创建 /usr/bin/                               │
│  └─ 尝试修改 /Applications/                            │
│                                                          │
│  3. 检查 dyld 注入                                       │
│  ├─ 检查 DYLD_INSERT_LIBRARIES 环境变量                 │
│  ├─ 检查已加载的 framework(MobileSubstrate)           │
│  └─ 检查异常的 dyld 行为                                │
│                                                          │
│  4. 检查二进制签名                                       │
│  ├─ 验证二进制文件的签名是否完整                          │
│  ├─ 检查是否有被篡改的 Mach-O 文件                       │
│  └─ 检查 Entitlements 是否被修改                        │
│                                                          │
│  5. 运行时检测                                            │
│  ├─ 尝试执行 su 命令                                    │
│  ├─ 尝试打开 /dev/kmem                                │
│  └─ 检查 /proc/self/maps 中是否有异常库                 │
│                                                          │
│  6. URL Scheme 检测                                     │
│  ├─ 检查 cydia:// URL Scheme                            │
│  └─ 检查 file:// URL Scheme(读取系统文件)             │
│                                                          │
│  检测方案对比:                                           │
│  ┌─────────┬────────────────────────────┬─────────┐   │
│  │ 方式      │ 可靠性                  │  误报率    │  适用场景  │   │
│  ├─────────┼────────────────────────────┼─────────┤   │
│  │ 文件检查  │ 高                      │  低        │ 简单检测  │   │
│  │ 权限检查  │ 中                      │  中        │ 基础检测  │   │
│  │ dyld 注入  │ 高                      │  低        │ 高级检测  │   │
│  │ 签名检查  │ 高                      │  低        │ 安全检测  │   │
│  │ 运行时检测  │ 高                      │  中        │ 深度检测  │   │
│  │ URL Scheme│ 中                      │  高        │ 辅助检测  │   │
│  └─────────┴────────────────────────────┴─────────┘   │
│                                                          │
│  ⚠️ 注意:                                               │
│  • 越狱检测只能提高逆向难度,不能完全阻止                     │
│  • 过度检测可能导致误判                                   │
│  • 建议结合多种方案                                         │
│                                                          │
└────────────────────────────────────────────────────────┘
*/

7. 面试题汇总

高频面试题

Q1: iOS 应用启动流程?

  1. 内核加载 Mach-O 可执行文件
  2. dyld 加载依赖 framework
  3. 符号绑定(GOT/PLT)
  4. _objc_init 初始化 Runtime
  5. _objc_loadImages 加载类
  6. 调用 +load 方法
  7. main() → UIApplicationMain
  8. RunLoop 启动(无限循环)

Q2: Mach-O 文件结构?

  • Header:文件类型、CPU 类型、命令数量
  • Load Commands:加载命令(SEGMENT_64、LOAD_DYLIB、MAIN 等)
  • Segments:__PAGEZERO、__TEXT(代码)、__DATA(数据)、__LINKEDIT(符号/签名)

Q3: 动态链接机制?

  • dyld 在运行时解析符号
  • GOT/PLT 存储动态链接符号的地址
  • 首次访问时触发 dyld 解析
  • 减少动态依赖可以减少启动时间
  • 使用 strip 剥离无用符号

Q4: 代码签名与沙盒机制?

  • 代码签名:开发者证书 + Provisioning Profile + 密钥链
  • 签名验证:安装时验证 + 启动时内核验证
  • 沙盒:Documents/Library/tmp 目录结构
  • 权限请求:Info.plist 声明 + 运行时弹窗

Q5: 越狱检测方案?

  1. 检查已知越狱文件(/Applications/Cydia.app 等)
  2. 检查权限(能否写入系统目录)
  3. 检查 dyld 注入(DYLD_INSERT_LIBRARIES)
  4. 检查二进制签名
  5. 运行时检测(su 命令、/dev/kmem)
  6. URL Scheme 检测(cydia://)
  • 结合多种方案,单一方案不可靠

8. 参考资源