Appearance
03 - 系统原理
目录
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 应用启动流程?
答:
- 内核加载 Mach-O 可执行文件
- dyld 加载依赖 framework
- 符号绑定(GOT/PLT)
- _objc_init 初始化 Runtime
- _objc_loadImages 加载类
- 调用 +load 方法
- main() → UIApplicationMain
- 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: 越狱检测方案?
答:
- 检查已知越狱文件(/Applications/Cydia.app 等)
- 检查权限(能否写入系统目录)
- 检查 dyld 注入(DYLD_INSERT_LIBRARIES)
- 检查二进制签名
- 运行时检测(su 命令、/dev/kmem)
- URL Scheme 检测(cydia://)
- 结合多种方案,单一方案不可靠