Objective-C 代码混淆
# 前言
终于开始做攻防相关的操作了,我会一边攻一边防,这篇文章先介绍一个简单的代码混淆的防备操作。文章会从为何要加密、加密市场市场现状、加密思路、方法、工具、操作等方面进行阐述,也作为我个人的记录以便日后的回顾。
# 为什么要做混淆加密
先说安卓,是一定要做混淆加密的。因为 apk 非常容易反编译,比如用 smail2java,apk 文件反编译过后都会变成 smail 文件,通过这个工具能看到里面的 Java 代码,请注意,看到的是源码!没有混淆的代码一旦经过反编译全部完完全全的展现给别人,知识产权一点保障也没有。如果写了什么敏感信息,也就全部暴露了。而加固后,比如用360加固保后,反编译出来基本什么也看不到了。
再说 iOS,使用 classdump 对原程序进行 dump,可以 dump 出所有源程序的函数所有信息,包括源程序所有函数类型,变量全部泄露。又比如用 IDA 或者 Hopper Disassembler, 虽然不能像安卓一样反编译出源代码,但还是可以看到汇编或者汇编与 OC 运行时的结合体,还是可以比较容易看出方法的具体实现,让攻击者,也就是黑客们了解了程序结构方便逆向,所以也需要做混淆加密。
# 加密市场
国内市场上的现状是安卓的加固烂大街,并且免费,比如360加固保。对于安卓来说,自己写混淆加密意义不大,当然基于了解与学习的角度出发,Get 一项新技能完全没毛病。
而 iOS 市场上很少有人做混淆,因为不能像安卓基于平台化的提交应用包,只能使用第三方提供的集成了加密插件的 Xcode,代替官方原版 Xcode 来打包。国内首家做 iOS 加密的爱加密,据我的深度了解购买授权需要八万块,这可是一笔很大的成本。对于安全级别本身就比较高的苹果应用来说,其实不需要再做太多太多的安全防范操作了。自己学会较简单的混淆加密原理与操作,就可以省下一笔钱了。接下来就只说 iOS 的混淆加密。
# iOS 混淆加密
# 混淆思路
- 花代码花指令
- 易读字符替换
花代码花指令即随意往程序中加入迷惑人的代码指令,但是个人觉得这个方法还是不太好,如果开发阶段加入迷惑人的代码,容易连开发者自己都被迷惑;如果上架前加入迷惑人的代码,以后的版本迭代也会比较恶心,所以个人不推荐用这个方法。
易读字符替换是防止 class-dump 出可读信息的非常有效的办法,例如将方法名混淆。
# 混淆的时机
- 开发时一直保留清晰可读的程序代码,方便自己。
- 发布时编译出来的二进制包含乱七八糟的混淆后的程序代码,恶心他人。
因此,我们可以在 Build Phrase 中设定在编译之前进行方法名的字符串替换。
# 混淆的方法
方法名混淆其实就是字符串替换,利用 #define 的方法来完成,可以把混淆结果合并在一个 .h 中,在工程 Prefix.pch 的最前面 #import 这个 .h。不导入也可以编译、导入则实现混淆。
单段的 selector,如 func: ,可以通过 #define func 来实现字符串替换。 多段的 selector,如 a🅱️c: ,可以通过分别 #define a 、b、c 来实现字符串替换。
# 混淆工具
写一个的混淆脚本,主要思路是把敏感方法名集中写在一个名叫 func.list 的文件中,逐一 #define 成随机字符,追加写入 .h。
#!/usr/bin/env bash
TABLENAME=symbols
SYMBOL_DB_FILE="symbols"
STRING_SYMBOL_FILE="func.list"
HEAD_FILE="$PROJECT_DIR/$PROJECT_NAME/Classes/Other/DKCodeObscure.h"
export LC_CTYPE=C
#维护数据库方便日后作排重
createTable()
{
echo "create table $TABLENAME(src text, des text);" | sqlite3 $SYMBOL_DB_FILE
}
insertValue()
{
echo "insert into $TABLENAME values('$1' ,'$2');" | sqlite3 $SYMBOL_DB_FILE
}
query()
{
echo "select * from $TABLENAME where src='$1';" | sqlite3 $SYMBOL_DB_FILE
}
ramdomString()
{
openssl rand -base64 64 | tr -cd 'a-zA-Z' |head -c 16
}
rm -f $SYMBOL_DB_FILE
rm -f $HEAD_FILE
createTable
touch $HEAD_FILE
echo '#ifndef Demo_codeObfuscation_h
#define Demo_codeObfuscation_h' >> $HEAD_FILE
echo "//confuse string at `date`" >> $HEAD_FILE
cat "$STRING_SYMBOL_FILE" | while read -ra line; do
if [[ ! -z "$line" ]]; then
ramdom=`ramdomString`
echo $line $ramdom
insertValue $line $ramdom
echo "#define $line $ramdom" >> $HEAD_FILE
fi
done
echo "#endif" >> $HEAD_FILE
sqlite3 $SYMBOL_DB_FILE .dump
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# 操作步骤
将混淆脚本 confuse.sh 放到工程目录下
mv confuse.sh to/project/path/
1创建混淆头文件 DKCodeObscure.h
可以直接在 xcode 中新建一个 Header 文件,根据项目架构分层找一个适当的位置放,我把它放在了
/Classes/Other/
下,要与 confuse.sh 里的 HEAD_FILE 保持一致。HEAD_FILE="$PROJECT_DIR/$PROJECT_NAME/Classes/Other/DKCodeObscure.h"
1修改 pch,添加混淆头文件
#ifdef __OBJC__ #import "DKCodeObscure.h" #endif
1
2
3配置 Build Phase,添加执行脚本操作
新建一个 Run Script,输入
$PROJECT_DIR/confuse.sh
创建函数名列表 func.list,根据规则写入待混淆的方法名
例如:
/** 获取立即支付前的准备数据 */ + (void)fetchPreDataWithProductId:(NSString *)productId quantity:(NSInteger)quantity callback:(void(^)(DKPayPreData *preData, NSError *error))callback;
1
2可写成:
fetchPreDataWithProductId quantity callback
1
2
3
4一般情况下,需要混淆加密的方法应该只是一些比较敏感的,比如与支付相关的 API Service 等。当然,如果需要写入混淆的方法名太多了,一个一个写也是很麻烦,后面我会写一个根据规则自动生成所有方法名列表的脚本。
将 func.list 文件放置于与 confuse.sh 脚本同级
mv func.list to/project/path/
编译查看结果
直接 build,混淆脚本会在编译前运行,进行字符随机替换,并且每次 build 的随机字符不同,如图:
同时也会将混淆记录写在 DKCodeObscure.h 中
# 补充
如果 Build 的时候报了如下一个错误:
Shell Script Invocation Error
/Users/bingo/Library/Developer/Xcode/DerivedData/SF-cprhxwzvzucuawewgvpjhoffdayp/Build/Intermediates/SF.build/Debug-iphoneos/SF.build/Script-D71BDF821E5C99F4003BF126.sh:
line 2: /Users/bingo/Desktop/iOS/iOS: No such file or directory
2
3
4
这是路径有问题,比如包含空格,我的 PROJECT_DIR 是 /Users/bingo/Desktop/iOS/iOS\ Project/SF
,“iOS Project” 中间有空格,但是 Script 不会自动转义,所以要保证路径不能有空格,例如我改成了 “iOS_Project”。(中文路径还没测试,有可能没问题,但我的看法作为一个有逼格的 developer,用中文你还是别 do 了吧。)
# 后话
至此,一个 iOS 方法名混淆加密就完成了,但还是很简单的,后面会再一步步优化,现在的 ramdomString() 方法每次都是生成随机字符,后面可能会考虑改成 md5 加密,还有写个脚本一键生成 func.list,还考虑将创建文件等操作也改成用脚本来生成等等。