【iOS】iOS逆向系列十二 - 代码混淆

什么是加固?加固是为了增加应用的安全性,防止应用被破解、盗版、二次打包、注入、反编译等。

常见的加固方式有:

  • 数据加密(字符串、网络数据、敏感数据等)
  • 应用加壳(二进制加密)
  • 代码混淆(类名、方法名、代码逻辑等)
  • ……(不同平台还有不同的做法)

一、代码混淆

iOS程序可以通过class-dump、Hopper、IDA等工具获取类名、方法名、以及分析程序的执行逻辑。如果进行代码混淆,就可以加大别人的分析难度。

iOS的代码混淆方案:

  • 源码的混淆(宏定义、套无用代码等)

    • 类名

    • 方法名

    • 协议名

    • ……

  • 中间代码(LLVM IR)的混淆(容易产生BUG)

1.1. 通过宏定义混淆方法名、类名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#define DBPerson wewewe
#define run qewewe
#define setName fdafda
#define age ioiower

@interface DBPerson : NSObject
- (void)run;
- (void)setName:(NSString *)name age:(int)age;
@end

@implementation DBPerson

- (void)run {
NSLog(@"%s", __func__);
}

- (void)setName:(NSString *)name age:(int)age {
NSLog(@"%s - %@ - %d", __func__, name, age);
}

@end

// 使用
DBPerson *person = [[DBPerson alloc] init];
[person run];
[person setName:@"daben" age:10];

/*
输出:
[wewewe qewewe]
[wewewe fdafda:ioiower:] - daben 10
*/

注意:

  • 不能混淆系统方法
  • 不能混淆init开头的等初始化方法(比如,OC规定类初始化方法中使用self,初始化方法必须使用init开头)
  • 混淆属性时需要额外注意set方法(比如,混淆age属性为abc,原来重写age的set方法就需要修改为setAbc)
  • 如果xib、storyboard中用到了混淆的内容,需要手动修正Name
  • 可以考虑把需要混淆的符号都加上前缀,跟系统自带的符号进行区分
  • 混淆过多可能会被AppStore拒绝上架,需要说明用途

建议给需要混淆的符号加上了一个特定的前缀。

1.2. 工具

第三方工具:https://github.com/Polidea/ios-class-guard,它是基于class-dump的扩展,用class-dump扫描出可执行文件中的类名、方法名、属性名等并做替换,会更新xib和storyboard的名字等等。

用法:

1
2
$ brew install ios-class-guard
$ ios-class-guard [options] <mach-o-file>

常用参数:

  • --sdk-root <path>:用于指定SDK路径,如果是模拟器SDK,一般路径就是/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk
  • --sdk-ios:相当于指定SDK路径为真机设备SDK(相当于--sdk-root iPhoneOS<version>.sdk
  • -X <path>:用于指定xib、storyboard所在目录,它会递归搜索
  • -O <path>:生成的混淆头文件路径
  • -m <path>:符号映射表(默认是symbols.json,和生成的混淆头文件内容差不多)

1.3. 字符串加密

很多时候,可执行文件中的字符串信息,对破解者来说,非常关键,是破解的捷径之一。为了加大破解、逆向难度,可以考虑对字符串进行加密。

字符串的加密技术有很多种,可以根据自己的需要自行制定算法。例如,对每个字符进行异或^处理,需要使用字符串时,对异或^过的字符再进行一次异或^就可以获得原字符。