研究了大半年逆向工程了,沒在博客作記錄,最近看到篇,跟本身的想法不謀而合,摘要下:php
運行在越獄設備上的 iOS app,很是容易遭到破解分析,這裏我列舉一些能夠加大破解難度的方法,但願有所幫助。html
一些實用手段ios
防止 tweak 依附git
一般來講,咱們要分析一個 app,最開始通常是砸殼,github
$ DYLD_INSERT_LIBRARIES=dumpdecrypted.dylib /path/to/XXX.app/XXX
而後將解密以後的二進制文件扔給相似 hopper 這樣的反編譯器處理。直接將沒有砸殼的二進制文件扔個 hopper 反編譯出來的內容是沒法閱讀的(被蘋果加密了)。因此說砸殼是破解分析 app 的第一步。對於這一步的防範,有兩種方式。swift
1.限制二進制文件頭內的段數組
經過在 Xcode 裏面工程配置 build setting 選項中將緩存
-Wl,-sectcreate,__RESTRICT,__restrict,/dev/null
添加到 "Other Linker Flags"(注意這裏我在項目中碰到了一個 問題,在 iPod touch iOS 9.3 的設備上,使用了 swift 的項目會致使莫名奇妙的 swift 標準庫沒法找到,而在 iOS 10 的設備上沒有這個問題。以前並無覺得是由於添加了這個的緣由,直到網上搜了全部解決方案,好比這個 SO Post 都沒有效果的時候,我才發現是這個設置的緣由)安全
2.setuid 和 setgid (Apple 不接受調用這兩個函數的 app,由於它能夠經過查看符號表來判斷您的二進制運行文件是否包含這兩個函數)cookie
具體原理能夠查看參考資料1,2
檢測越獄設備上是否有針對性 tweak
通常來講在越獄手機上,咱們會使用 TheOS 建立 tweak 類型的工程。而後針對咱們要分析的類,使用提供的 logify.pl 命令生成的 mk 文件來打印該類全部方法的入參和出參。這對分析 app 的運行方式有很大的幫助。固然,咱們也能夠本身建立某個類的 mk,來 hook 某個函數,讓它以咱們想要的方式運行,好比說對於一些作了證書綁定的 app,若是它用的框架是 AFNetWorking 的話,那麼咱們能夠建立一個 mk 文件,hook AFSecurityPolicy 類的下列方法:
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain
讓這個方法永遠返回 YES,那麼大多數的應用所作的證書綁定也就失效了。用過 TheOS 的 tweak 模版的話,你會發現這種方式至關簡單快速。
對於這一步的防範,能夠在工程的 main 函數裏面加入一層判斷,首先讀取 /Library/MobileSubstrate/DynamicLibraries 下全部的 plist 文件的內容,查看是否某個 plist 含有你的 app 的 bundle id,是的話,能夠斷定有人想利用 tweak 攻擊你的 app,這時候你能夠採起好比說將 app 給 crash 掉,或者限制某些功能等方式來應對。
具體原理能夠查看參考資料4,簡單來講,就是 MobileSubstrate 在 app 加載到內存的時候會先去檢查 /Library/MobileSubstrate/DynamicLibraries 下面是否有須要加載的 tweak,有的話就加載,怎麼判斷有沒有?就是根據 plist 裏面的 bundle ID 判斷的。
代碼參考以下
static __inline__ __attribute__((always_inline)) int anti_tweak()
{
uint8_t lmb[] = {'S', 'u', 'b', 's', 't', 'r', 'a', 't', 'e', '/', 'D', 'y', 'n', 'a', 'm', 'i', 'c', 0, };
NSString *dir = [NSString stringWithFormat:@"/%@/%@%s%@", @"Library", @"Mobile", lmb, @"Libraries"];
NSArray *dirFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:dir error:nil];
NSArray *plistFiles = [dirFiles filteredArrayUsingPredicate:
[NSPredicate predicateWithFormat:
[NSString stringWithFormat:@"%@ %@%@ '.%@%@'",@"self", @"EN", @"DSWITH", @"pli", @"st"]]];
int cnt = 0;
for (NSString *file in plistFiles) {
NSString *filePath = [dir stringByAppendingPathComponent:file];
NSString *fileContent = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
if (fileContent && [fileContent rangeOfString:[[NSBundle mainBundle] bundleIdentifier]].location != NSNotFound) {
cnt ++;
}
}
// 返回有針對本 app 的 tweak 數量,爲 0 說明沒有
return cnt;
}
防 http 抓包
一般破解一個 app,咱們會抓包。這樣的話,咱們的 app 全部接口,接口數據都會暴露在逆向人員的眼皮底下。這時候,咱們能夠限制 http 抓包。方式很簡單,就是將 NSURLSessionConfiguration 的 connectionProxyDictionary 設置成空的字典,由於這個屬性就是用來控制會話的可用代理的。可用參見官方文檔,也就是參考資料5。下面是對於 AFNetWorking 的使用方法:
// 繼承 AFHTTPSessionManager,重寫下列方法
- (instancetype)initWithServerHost:(PDLServerHost*)serverHost {
#ifdef DEBUG
// debug 版本的包仍然可以正常抓包
self = [super initWithBaseURL:serverHost.baseURL];
#else
// 因爲使用 ephemeralSessionConfiguration session 發起的請求不帶 cookie 和使用緩存等
NSURLSessionConfiguration *conf = [NSURLSessionConfiguration ephemeralSessionConfiguration];
conf.connectionProxyDictionary = @{};
self = [super initWithBaseURL:serverHost.baseURL sessionConfiguration:conf];
#endif
return self;
}
可是因爲 OC 方法很容易被 hook,避免抓包是不可能的,因此,我的認爲最好的方式是對請求參數進行加密(最好是非對稱加密,好比 RSA)
混淆(或者加密)硬編碼的明文字符串
對於被砸殼的二進制文件,逆向分析人員分析代碼有一條重要線索,也就是被硬編碼的明文字符串。好比說,你的 app 被人抓包了,某些數據請求接口也被人發現了,那麼很簡單,逆向人員能夠直接拷貝特徵比較明顯的字符串到 hopper 中搜索,經過查看該字符串被引用的地方,能夠很快的找到相應的邏輯代碼。
對於這一步的防範,須要作的就是對硬編碼的明文進行加密或混淆。 有個開源代碼能夠用,UAObfuscatedString,可是這個開源混淆代碼寫出來的字符串是至關長的(也就是麻煩),同時不支持加密。最近我寫了一個工具,能夠在編譯期間加密全部代碼中的明文字符串,在 app 運行的時候解密字符串。這個工具的特色以下:
-
簡單,開發人員能夠硬編碼明文字符串,全部的加密會在編譯開始時自動處理
-
能夠自定義加密或者混淆方式,(爲了避免影響 app 運行效率,須要提供一個簡單快速的加密或混淆方式)提升解密難度
項目地址 MixPlainText
使用 Swift 開發
Swift 是目前比較新的開發 iOS 語言,因爲 Swift 目前還不是很穩定,越獄開源社區對這個的支持也不是很即時,好比說 class-dump 工具目前就不支持含有 Swift 的二進制文件。 TheOS 也是最近纔開始支持 Swift,可是尚未加到主分支上(能夠參見 Features)。因此目前來看,至少 Swift 可能比純 OC 的工程要安全一點點。固然,等 Swift 日趨穩定,以及越獄開源社區的逐漸支持,這一點優點可能就不明顯了。
使用靜態內連 C 函數
因爲 OC 語言的動態性,致使 OC 的代碼是最容易被破解分析的。在安全性上,更推薦使用 C 語言寫成的函數。可是 C 語言的函數也是能夠被 hook 的,主要有3種方式:
1.使用 Facebook 開源的 fishhook
2.使用 MobileSubstrate 提供的 hook C 語言函數的方法
void MSHookFunction(void* function, void* replacement, void** p_original);
3.使用 mach_override,關於 mach_override 和 fishhook 的區別請看 mach_override 和 fishhook 區別
因爲上面這三種方式能夠 hook C 函數。要想不被 hook 解決方法是使用靜態內聯函數,這樣的話須要被 hook 的函數沒有統一的入口,逆向人員想要破解只能去理解該函數的邏輯。
使用 block
嚴格來講使用 block 並不能很大程度提升安全性,由於逆向人員只要找到使用該 block 的方法,通常來講在其附近就會有 block 內代碼的邏輯。具體查找方法和原理能夠看參考資料6。
可是我的認爲使用 block 的安全性是比直接使用 oc 方法是要高的。在個人逆向分析 app 的經驗中,對於使用了 block 的方法,目前我還不知道到怎麼 hook (有知道的話,能夠在 blog 上提個 issue 告訴我,先謝過)同時對於含有嵌套的 block 或者是做爲參數傳遞的 block,處理起來就更加複雜了。因此,若是能將內斂 C 函數,嵌套 block , block 類型參數組合起來的話,安全性應該是會有必定提高。
2018年3月2日更新:block 的 hook 操做比較麻煩,可是也是能夠的。請參見一下資料:
代碼混淆
代碼混淆的方式有幾種:
添加無用又不影響邏輯的代碼片斷,迷糊逆向人員
對關鍵的類、方法,命名成與真實意圖無關的名稱
對於第二種,目前有一些自動化工具,好比念茜提到的一個工具參見參考資料7。
我的認爲最好的一個加密混淆工具是 ios-class-guard,不過目前這個項目已經中止維護了。可是這種方式的混淆我以爲纔是最終極的方案。
其餘方法
好比 ptrace 反調試等(不過聽說已經能夠很容易被繞過)
// see http://iphonedevwiki.net/index.php/Crack_prevention for detail
static force_inline void disable_gdb() {
#ifndef DEBUG
typedef int (*ptrace_ptr_t)(int _request, pid_t _pid, caddr_t _addr, int _data);
#ifndef PT_DENY_ATTACH
#define PT_DENY_ATTACH 31
#endif
// this trick can be worked around,
// see http://stackoverflow.com/questions/7034321/implementing-the-pt-deny-attach-anti-piracy-code
void* handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW);
ptrace_ptr_t ptrace_ptr = dlsym(handle, [@"".p.t.r.a.c.e UTF8String]);
ptrace_ptr(PT_DENY_ATTACH, 0, 0, 0);
dlclose(handle);
#endif
}
注意事項
經過了解 OC 的運行時特性和 mach-o 二進制文件的結構,藉助現有的工具,你會發現 hook 方法 是很簡單就能完成的。雖然上面我提到了一些提升安全性的幾個方案,可是,全部這些方式只是增長了逆向人員的逆向難度,並不能讓 app 變的堅如盤石。不過採起必定的措施確定比什麼措施都不採起來的安全,你說呢?
參考資料