方案一:ios-class-guard
原理
解析 ObjC 的 __DATA,__objc_classlist 和 __DATA,__objc_methnames 段,生成一份映射表,在编译期通过 -D
宏把类名/方法名替换成随机短字符串。
接入方式
# Build Phase 脚本
ios-class-guard --sdk-root "$SDKROOT" \
--symbols-map "$SRCROOT/symbols.json" \
"$TARGET_BUILD_DIR/$EXECUTABLE_PATH"
优点
- 开箱即用,成本最低
- 支持黑白名单,可以排除第三方库
缺点
- 项目已停止维护(最后更新 2015 年)
- Swift 代码无效,只针对 ObjC
- 偶发与 Category、Protocol 冲突导致崩溃
- 映射表文件泄露后等于白做
适合场景: 纯 ObjC 老项目,快速接入
---
方案二:手写混淆脚本
原理
Build Phase 里跑 Python/Shell
脚本,用正则扫描源文件,把类名/方法名批量改写为无意义字符串,同时维护一份映射表用于还原崩溃日志。
典型实现
# 扫描所有 .h/.m,把 CX 前缀的类名替换
# CXOrderViewController → Bx7kQm3ViewController
# 维护 symbols_map.json 用于 dSYM 还原
优点
- 完全可控,针对自己业务代码定制
- 可以同时处理 ObjC + Swift
- 崩溃日志可通过映射表还原,不影响排查问题
- 不依赖第三方工具
缺点
- 需要自己开发和维护脚本
- 正则替换容易误伤(注释、字符串里的类名)
- 需要处理好 Storyboard/XIB 里的类名引用
适合场景: ObjC/Swift 混编项目,想要长期维护的方案
---
方案三:obfuscator-llvm(OLLVM)
原理
在编译器层面对 IR(中间表示)做变换,包括:
- 控制流平坦化:把 if/else/for 变成一个巨大的 switch-case 状态机
- 虚假控制流:插入永远不会执行的分支迷惑分析
- 指令替换:把 a+b 替换成等价但更复杂的位运算
// 原始逻辑
if (x > 0) { doA(); } else { doB(); }
// 平坦化后
int state = 0x3F2A;
while(1) {
switch(state) {
case 0x3F2A: state = (x>0) ? 0x1B4C : 0x7E9D; break;
case 0x1B4C: doA(); state = 0xFFFF; break;
case 0x7E9D: doB(); state = 0xFFFF; break;
case 0xFFFF: goto end;
}
}
优点
- 混淆强度最高,对抗 IDA Pro 等逆向工具效果好
- 作用于所有语言(ObjC/Swift/C/C++)
缺点
- 需要替换 Xcode 的 Clang 编译器,接入复杂
- 包体积增大 20%~50%
- 编译时间大幅增加
- 控制流平坦化可能引入性能问题
- Apple 审核也可能因为异常的控制流结构触发人工审查
适合场景: 对安全要求极高的场合(金融/支付核心模块),不适合全量使用
---
横向对比
┌────────────┬─────────────────┬──────────┬─────────┐
│ │ ios-class-guard │ 手写脚本 │ OLLVM │
├────────────┼─────────────────┼──────────┼─────────┤
│ 接入难度 │ 低 │ 中 │ 高 │
├────────────┼─────────────────┼──────────┼─────────┤
│ 维护成本 │ 低(但已废弃) │ 中 │ 高 │
├────────────┼─────────────────┼──────────┼─────────┤
│ 混淆强度 │ 低 │ 中 │ 高 │
├────────────┼─────────────────┼──────────┼─────────┤
│ 包体积影响 │ 无 │ 无 │ +20~50% │
├────────────┼─────────────────┼──────────┼─────────┤
│ Swift 支持 │ 否 │ 是 │ 是 │
├────────────┼─────────────────┼──────────┼─────────┤
│ 崩溃可还原 │ 是 │ 是 │ 较难 │
└────────────┴─────────────────┴──────────┴─────────┘
实际推荐组合:
▎ 字符串混淆(你已有)+ 手写脚本混淆类名/方法名 = 覆盖 90% 的静态分析场景,成本可控。OLLVM
▎ 只在核心支付/加密模块单独开启。