iOS安全攻防:static和被裁的符号表,Objective-C代码混淆,敏感逻辑的保护方案(1)
2014-06-20 10:04
746 查看
转自念茜的博客
本文是《iOS安全攻防》的22、23、24小结内容。此前内容可参看:《iOS安全攻防》
iOS安全攻防(二十二):static和被裁的符号表
为了不让攻击者理清自己程序的敏感业务逻辑,于是我们想方设法提高逆向门槛。本文就介绍一个防御技巧————利用static关键字裁掉函数符号。
原理
如果函数属性为 static ,那么编译时该函数符号就会被解析为local符号。
在发布release程序时(用Xcode打包编译二进制)默认会strip裁掉这些函数符号,无疑给逆向者加大了工作难度。
验证
写个demo验证一下上述理论,以一段创建Button的代码为例,对应补充一个static版本。
id createBtn() { UIButton *btn = [[UIButton alloc]initWithFrame:CGRectZero]; [btn setFrame:CGRectMake(200, 100, 100, 100)]; [btn setBackgroundColor:[UIColor redColor]]; btn.layer.cornerRadius = 7.0f; btn.layer.masksToBounds = YES; return btn; } static id static_createBtn() { UIButton *btn = [[UIButton alloc]initWithFrame:CGRectZero]; [btn setFrame:CGRectMake(50, 100, 100, 100)]; [btn setBackgroundColor:[UIColor blueColor]]; btn.layer.cornerRadius = 7.0f; btn.layer.masksToBounds = YES; return btn; }
再来看一下反编的结果,对于createBtn()方法,我们可以得到它的伪代码:
函数名虽然面目全非,但是基本操作还是清晰的。
对于static_createBtn()方法呢,我们已经无法看到它任何直观的有价值信息了。
局限
当然这种方法也有局限性。正如你所知道的,static函数,只在本文件可见。
打破局限
怎么让别的文件也能调到本文件的static方法呢?
在本文件建造一个结构体,结构体里包含函数指针。把static函数的函数指针都赋在这个结构体里,再把这个结构体抛出去。
这样做的好处是,既隐藏了函数代码也丰富了调用方式。
iOS安全攻防(二十三):Objective-C代码混淆
class-dump可以很方便的导出程序头文件,不仅让攻击者了解了程序结构方便逆向,还让着急赶进度时写出的欠完善的程序给同行留下笑柄。所以,我们迫切的希望混淆自己的代码。
混淆的常规思路
混淆分许多思路,比如:
1)花代码花指令,即随意往程序中加入迷惑人的代码指令
2)易读字符替换
等等
防止class-dump出可读信息的有效办法是易读字符替换。
Objective-C的方法名混淆
混淆的时机
我们希望在开发时一直保留清晰可读的程序代码,方便自己。
同时,希望编译出来的二进制包含乱七八糟的混淆后的程序代码,恶心他人。
因此,我们可以在Build Phrase 中设定在编译之前进行方法名的字符串替换。
混淆的方法
方法名混淆其实就是字符串替换,有2个方法可以,一个是#define,一个是利用tops。
利用#define的方法有一个好处,就是可以把混淆结果合并在一个.h中,在工程Prefix.pch的最前面#import这个.h。不导入也可以编译、导入则实现混淆。
单段的selector,如func: ,可以通过#define func 来实现字符串替换。
多段的selector,如a:b: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/codeObfuscation.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
操作步骤
1.将混淆脚本confuse.sh放到工程目录下
mv confuse.sh your_proj_path/
2.修改Prefix.pch
打开Xcode,修改XXX-Prefix.ch ,添加混淆头文件:
#ifdef __OBJC__ #import #import //添加混淆作用的头文件(这个文件名是脚本confuse.sh中定义的) #import "codeObfuscation.h" #endif
3.配置Build Phase
在工程Build Phase中添加执行脚本操作,执行confuse.sh脚本,如图:
4.创建函数名列表func.list,写入待混淆的函数名,如:
-(void)sample; -(void)seg1:(NSString *)string seg2:(NSUInteger)num;
就这样写:
sample
seg1
seg2
并将文件放置于与confuse.sh脚本同级
mv func.list your_proj_path/
5.编译查看结果
直接build,混淆脚本会在编译前运行,进行字符随机替换,并且每次build的随机字符不同,如图:
iOS安全攻防(二十四):敏感逻辑的保护方案(1)
Objective-C代码容易被hook,暴露信息太赤裸裸,为了安全,改用C来写吧!
当然不是全部代码都要C来写,我指的是敏感业务逻辑代码。
本文就介绍一种低学习成本的,简易的,Objective-C逻辑代码重写为C代码的办法。
也许,程序中存在一个类似这样的类:
@interface XXUtil : NSObject + (BOOL)isVerified; + (BOOL)isNeedSomething; + (void)resetPassword:(NSString *)password; @end
被class-dump出来后,利用Cycript很容易实现攻击,容易被hook,存在很大的安全隐患。
想改,但是不想大改程序结构,肿么办呢?
把函数名隐藏在结构体里,以函数指针成员的形式存储。
这样做的好处是,编译后,只留了下地址,去掉了名字和参数表,提高了逆向成本和攻击门槛。
改写的程序如下:
//XXUtil.h #import typedef struct _util { BOOL (*isVerified)(void); BOOL (*isNeedSomething)(void); void (*resetPassword)(NSString *password); }XXUtil_t ; #define XXUtil ([_XXUtil sharedUtil]) @interface _XXUtil : NSObject + (XXUtil_t *)sharedUtil; @end
//XXUtil.m #import "XXUtil.h" static BOOL _isVerified(void) { //bala bala ... return YES; } static BOOL _isNeedSomething(void) { //bala bala ... return YES; } static void _resetPassword(NSString *password) { //bala bala ... } static XXUtil_t * util = NULL; @implementation _XXUtil +(XXUtil_t *)sharedUtil { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ util = malloc(sizeof(XXUtil_t)); util->isVerified = _isVerified; util->isNeedSomething = _isNeedSomething; util->resetPassword = _resetPassword; }); return util; } + (void)destroy { util ? free(util): 0; util = NULL; } @end
最后,根据Xcode的报错指引,把以前这样的调用
[XXUtil isVerified];
对应改成:
XXUtil->isVerified();
就可以了。
是的,绝不费一点脑子。
相关文章推荐
- iOS安全攻防(二十四):敏感逻辑的保护方案(1)
- iOS安全攻防(二十四):敏感逻辑的保护方案(1)
- iOS安全攻防(二十四):敏感逻辑的保护方案(1)
- iOS攻防 - (二)iOS应用敏感逻辑的保护方案
- iOS安全攻防(二十三):Objective-C代码混淆
- iOS安全攻击和防御(24):敏感的保护方案逻辑(1)
- iOS安全攻防(二十三):Objective-C代码混淆
- iOS安全攻防(二十三):Objective-C代码混淆
- iOS安全攻防(二十三):Objective-C代码混淆
- iOS安全攻防(二十三):Objective-C代码混淆
- iOS安全攻防(二十二):static和被裁的符号表
- [ios]安全攻防之代码混淆的一个小工具
- iOS安全攻防(二十二):static和被裁的符号表
- 安全攻防——Objective-C代码混淆
- 关于HTML加密混淆、源码保护、代码安全,防止解压直接看源码
- 迷你播放器--第一阶段(7)--安全攻防第一战--对抗反编译,代码混淆和对抗动态调试
- iOS安全——代码混淆&反编译
- iOS程序安全-代码混淆
- 关于HTML加密混淆、源码保护、代码安全,防止解压直接看源码
- iOS安全攻防(十八):数据保护API