之前一直觉得应用加固就是给代码做一遍混淆,直到有个外包项目被爬了资源文件、类名被 class-dump 全倒出来,才发现加固这事得分层做。这里把 iOS 应用加固的常见技术按防护层级梳理一下,每个层级做什么、用什么工具、效果怎么样。
源码层加固
源码层面的加固主要是在编译前对代码做处理。Obfuscator-LLVM 是这层比较常用的方案,它在编译时把函数名、变量名替换成无意义符号,同时插入垃圾代码增加反编译难度。好处是混淆强度高、和编译流程深度集成,class-dump 拿到的头文件全是乱码。局限性在于它只覆盖原生层,Flutter 的 Dart 层和 Unity 的 C# 层管不到,而且每次 Xcode 大版本更新后要等社区适配才能用上。
编译层加固
编译层防护主要是通过编译器选项和编译后处理来增强安全性。strip 把二进制符号表去掉、开启 PIE 地址随机化、ARMR 保护这些是基础操作。也可以写自定义 LLVM pass 做更细粒度的控制,比如插花指令或者加密特定段的数据。但这层的技术门槛比较高,需要理解 LLVM 的中间表示,做一次配置后维护不算轻松。
资源层加固
资源文件经常是攻击者最先下手的地方,图片解压出来直接用、plist 和 json 里写的 API 地址和密钥一览无余。资源层加固就是把文件名改成无意义字符串、修改文件的 MD5 值、给图片添加不可见水印。这三件事做了以后,攻击者解压 IPA 拿到一堆名字没有规律的文件,无法通过文件名判断哪个是配置文件、哪个是核心资源。IpaGuard 在这层可以直接处理编译后的 .ipa,把资源文件的名字全部改写,MD5 也重新生成,攻击者拿到文件也无法从名称判断用途。
IPA 层整体加固
对于拿不到源码的场景——比如接手外包项目或者要加固旧版本——IPA 层面的加固方案更实用。IpaGuard 不需要源码,直接把 .ipa 拖进工具,勾选要混淆的模块(类名、方法名、属性名),资源文件也一并处理。调试信息会被自动清理,class-dump 导出来的结果是一堆乱码符号。处理完后配置签名参数做重签名,装到真机上跑一轮测试确认功能正常。
加固不是做了就行,每次发版前要确认混淆配置和上个版本有差异,防止审核系统识别出相同模式的混淆特征。同时每次加固后的 IPA 要做一轮真机测试,避免混淆影响到关键功能。几个防护层级不是互斥的,源码层做了 Obfuscator-LLVM 后照样可以把 IPA 交给 IpaGuard 再做资源处理和调试信息清理,两层处理完 class-dump 和资源解压都拿不到有效信息。叠加效果比只做单层好得多,攻击者的成本成倍增加。