Mach-O:iOS 二进制的解剖图
iOS/macOS 的可执行文件是 Mach-O 格式,结构分三段:
Mach Header(头部:架构、加载命令数量)
↓
Load Commands(加载命令:告诉系统怎么加载)
↓
Segments/Sections(数据段:代码、数据、符号)逆向必知的几个 Load Command:
LC_SEGMENT_64:定义内存段(__TEXT代码段、__DATA数据段)LC_ENCRYPTION_INFO_64:FairPlay 加密信息,cryptid=1表示加密,砸壳就是把它清 0 并替换明文段LC_CODE_SIGNATURE:代码签名位置LC_LOAD_DYLIB:依赖的动态库列表——MonkeyDev 注入就是在这里加一条
分析工具:otool -l 看加载命令,MachOView 图形化查看(强烈推荐,结构一目了然)。
代码签名:iOS 安全的地基
iOS 的"所有代码必须签名"机制,是理解越狱、砸壳、重打包一切问题的前提:
- 苹果签名:App Store 的包由苹果私钥签名,系统信任
- 开发者签名:开发调试包由开发者证书 + 描述文件签名,描述文件绑定设备 UDID
- 企业签名:企业证书签的包任意设备可装(苹果监管之外的灰色地带,常被吊销)
验证签名:
codesign -dv --verbose=4 Target.app
# 看签名者、team id、entitlements重打包必须重签名:改了二进制或注入 dylib 后原签名失效,系统拒绝加载。重签名流程:
# 1. 删掉原签名
rm -rf Target.app/_CodeSignature
# 2. 先签所有 framework 和 dylib(从内到外)
codesign -fs "Apple Development: xxx" Target.app/Frameworks/*.framework
# 3. 最后签主包,带上 entitlements
codesign -fs "Apple Development: xxx" --entitlements ent.plist Target.appFairPlay 加密的实现位置
理解砸壳原理需要知道 FairPlay 加密的是什么:
- 加密的只是
__TEXT段的代码页(不是整个文件) - 密钥在苹果的 FairPlay 体系里,设备加载时由内核/AMFID 解密
LC_ENCRYPTION_INFO_64里的cryptoff/cryptsize标记加密范围,cryptid标记是否加密
砸壳工具做的事:等系统解密后从内存抠明文,写回 cryptoff 指定的文件偏移,清 cryptid。所以砸壳产物在非越狱设备上也能静态分析——它已经是普通未加密二进制了。
精髓:三个实战认知
__RESTRICT段的反注入。二进制里若有__RESTRICT/__restrict段,DYLD_INSERT_LIBRARIES注入会被系统拒绝。砸壳后重打包时可以顺手删掉这个段(MachOView 或 optool),为后续注入扫清障碍。- Fat Binary 的架构拆分。App Store 包常含 arm64/arm64e 多架构,分析前用
lipo -thin arm64抽单架构,IDA/Hopper 加载更快,也避免架构混淆。 - Swift 二进制的符号特征。Swift 方法名是 mangled 格式(
$s7TargetApp11SignManagerC7getSign...),用swift-demangle还原可读;class-dump 对纯 Swift 类效果差,要配合swift-dump或 IDA 的 Swift 插件。
总结
- Mach-O 三段结构:头部、加载命令、数据段,MachOView 是查看神器
- 代码签名是 iOS 安全地基,重打包必须重签名(从内到外)
- FairPlay 只加密 __TEXT 段,砸壳 = 内存抠明文 + 清 cryptid
- __RESTRICT 段防注入,重打包时顺手删除
交流微信:run1255