1. 需求背景
App越獄機(jī)存在反編譯的情景。存在刷金幣色迂,等類似外掛的行為瘟忱。App加固奥额。使越獄者不能hookApp的方法。
2. 舊方案問題
玩吧App越獄可逆向访诱。
3. iOS上線版本
v10.5
4. 影響范圍/ 風(fēng)險(xiǎn)預(yù)警
需要測試詳細(xì)測試垫挨。改動(dòng)全局。 需要越獄測試機(jī)触菜,我這邊做加固完模擬攻防九榔。
開發(fā)周期 3-5天,測試周期:3-5天涡相。
5. 測試注意點(diǎn)
測開需要也做下模擬逆向攻防哲泊。并且做全局測試回歸。
建議逐步改進(jìn)漾峡。
6.技術(shù)方案
6.1.使用NSFileManager判斷設(shè)備是否安裝了如下越獄常用工具:
eg:
/Applications/Cydia.app
/Library/MobileSubstrate/MobileSubstrate.dylib
/bin/bash
/usr/sbin/sshd
/etc/apt
if ([[NSFileManager defaultManager] fileExistsAtPath:@"/Applications/Cydia.app"]){
return YES;
}
6.1.1 嘗試打開cydia應(yīng)用注冊(cè)的URL scheme:
if([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"cydia://package/com.example.package"]]){
NSLog(@"Device is jailbroken");
}
6.1.2 嘗試讀取下應(yīng)用列表攻旦,看看有無權(quán)限獲取:
if ([[NSFileManager defaultManager] fileExistsAtPath:@"/User/Applications/"]){
NSLog(@"Device is jailbroken");
NSArray *applist = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:@"/User/Applications/"
error:nil];
NSLog(@"applist = %@",applist);
}
攻擊者可能會(huì)hook NSFileManager 的方法生逸,讓你的想法不能如愿牢屋。可以回避 NSFileManager槽袄,
6.1.3 嘗試打開cydia應(yīng)用注冊(cè)的URL scheme:使用stat系列函數(shù)檢測Cydia等工具:
import <sys/stat.h>
void checkCydia(void)
{
struct stat stat_info;
if (0 == stat("/Applications/Cydia.app", &stat_info)) {
NSLog(@"Device is jailbroken");
}
}
6.1.4 如果攻擊者可能會(huì)利用 Fishhook原理 hook了stat烙无。
import <dlfcn.h>
void checkInject(void)
{
int ret ;
Dl_info dylib_info;
int (*func_stat)(const char *, struct stat *) = stat;
if ((ret = dladdr(func_stat, &dylib_info))) {
NSLog(@"lib :%s", dylib_info.dli_fname);
}
}
如果結(jié)果不是 /usr/lib/system/libsystem_kernel.dylib 的話,那就100%被攻擊了遍尺。
6.1.5 還可以檢索一下自己的應(yīng)用程序是否被鏈接了異常動(dòng)態(tài)庫截酷。
列出所有已鏈接的動(dòng)態(tài)庫:
import <mach-o/dyld.h>
void checkDylibs(void)
{
uint32_t count = _dyld_image_count();
for (uint32_t i = 0 ; i < count; ++i) {
NSString *name = [[NSString alloc]initWithUTF8String:_dyld_get_image_name(i)];
NSLog(@"--%@", name);
}
}
通常情況下,會(huì)包含越獄機(jī)的輸出結(jié)果會(huì)包含字符串: Library/MobileSubstrate/MobileSubstrate.dylib 乾戏。
攻擊者可能會(huì)給MobileSubstrate改名迂苛,但是原理都是通過DYLD_INSERT_LIBRARIES注入動(dòng)態(tài)庫三热。
通過檢測當(dāng)前程序運(yùn)行的環(huán)境變量:
void printEnv(void)
{
char *env = getenv("DYLD_INSERT_LIBRARIES");
NSLog(@"%s", env);
}
未越獄設(shè)備返回結(jié)果是null,越獄設(shè)備就各有各的精彩了三幻,尤其是老一點(diǎn)的iOS版本越獄環(huán)境就漾。
6.2 為了不讓攻擊者理清自己程序的敏感業(yè)務(wù)邏輯,于是我們想方設(shè)法提高逆向門檻念搬。利用static關(guān)鍵字裁掉函數(shù)符號(hào)抑堡。
如果函數(shù)屬性為 static ,那么編譯時(shí)該函數(shù)符號(hào)就會(huì)被解析為local符號(hào)朗徊。
在發(fā)布release程序時(shí)(用Xcode打包編譯二進(jìn)制)默認(rèn)會(huì)strip裁掉這些函數(shù)符號(hào)首妖,無疑給逆向者加大了工作難度。
eg:
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;
}
怎么讓別的文件也能調(diào)到本文件的static方法呢爷恳?
在本文件建造一個(gè)結(jié)構(gòu)體有缆,結(jié)構(gòu)體里包含函數(shù)指針。把static函數(shù)的函數(shù)指針都賦在這個(gè)結(jié)構(gòu)體里舌仍,再把這個(gè)結(jié)構(gòu)體拋出去妒貌。
這樣做的好處是通危,既隱藏了函數(shù)代碼也豐富了調(diào)用方式铸豁。
6.2.1 為了不讓攻擊者理清自己程序的敏感業(yè)務(wù)邏輯,于是我們想方設(shè)法提高逆向門檻菊碟。利用static關(guān)鍵字裁掉函數(shù)符號(hào)节芥。為了安全,將敏感的方法改寫為C函數(shù)逆害。
改寫方法如下:
@interface XXUtil : NSObject
- (BOOL)isVerified;
- (BOOL)isNeedSomething;
- (void)resetPassword:(NSString *)password;
-
@end
改寫為:
//XXUtil.h
import <Foundation/Foundation.h>
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
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
調(diào)用 XXUtil->isVerified();
- (XXUtil_t *)sharedUtil;
6.3 Objective-C代碼混淆,阻止 class-dump出可讀的信息有效方法是易讀字符替換头镊。
利用#define的方法有一個(gè)好處,就是可以把混淆結(jié)果合并在一個(gè).h中魄幕,在工程Prefix.pch的最前面#import這個(gè).h相艇。不導(dǎo)入也可以編譯、導(dǎo)入則實(shí)現(xiàn)混淆纯陨。
以下腳本坛芽。放進(jìn)Xcode 的Build Phrase 中設(shè)定在編譯之前進(jìn)行方法名的字符串替換。 結(jié)合外部plist要替換的方法使用翼抠。
!/usr/bin/env bash
TABLENAME=symbols
SYMBOL_DB_FILE="symbols"
STRING_SYMBOL_FILE="func.list"
HEAD_FILE="PROJECT_NAME/codeObfuscation.h"
export LC_CTYPE=C
維護(hù)數(shù)據(jù)庫方便日后作排重
createTable()
{
echo "create table SYMBOL_DB_FILE
}
insertValue()
{
echo "insert into 1' ,'
SYMBOL_DB_FILE
}
query()
{
echo "select * from 1';" | sqlite3 $SYMBOL_DB_FILE
}
ramdomString()
{
openssl rand -base64 64 | tr -cd 'a-zA-Z' |head -c 16
}
rm -f HEAD_FILE
createTable
touch $HEAD_FILE
echo '#ifndef Demo_codeObfuscation_h
define Demo_codeObfuscation_h' >> $HEAD_FILE
echo "http://confuse string at date
" >> STRING_SYMBOL_FILE" | while read -ra line; do
if [[ ! -z "line
line
line
HEAD_FILE
fi
done
echo "#endif" >> $HEAD_FILE
使用:
//添加混淆作用的頭文件(這個(gè)文件名是腳本confuse.sh中定義的)
-
import "codeObfuscation.h"
效果:
[圖片上傳失敗...(image-7b2a62-1591782762772)]
網(wǎng)上也有一些工具咙轩。混淆方法比較多阴颖。