前言
一個(gè)項(xiàng)目中的明文字符串不計(jì)其數(shù),但是有一些是程序的敏感信息的話如果不進(jìn)行加密和混淆處理,反編譯者就會很容易找到我們的敏感信息。拿到這些敏感信息之后就很容易分析我們的程序,業(yè)務(wù)邏輯進(jìn)而做一些可能對我們不是很友好的事情,所以一些敏感明文字符串還是有必要做一下加密,讓敏感信息更加安全。特別是對于我們金融類的APP開發(fā)。
反編譯工具
這里介紹幾個(gè)常用的:
(1)class-dump
主要用來反編譯一個(gè)庫文件或者app的方法名、屬性等聲明(即.h文件,強(qiáng)大的是反編譯出來的.h不僅僅包含頭文件中的聲明,.m中的function方法名稱也同樣能夠反編譯出來)。
(2)IDA
主要用來反編譯庫文件的實(shí)現(xiàn)(當(dāng)然方法聲明同樣能夠反編譯出來,用class-dump主要是更加形象,有針對性),這個(gè)反編譯工具非常強(qiáng)大能夠?qū)⒑瘮?shù)的實(shí)現(xiàn)及邏輯關(guān)系、流程統(tǒng)統(tǒng)顯示出來,弊端就是反編譯出來的內(nèi)容為匯編語言,需要一定匯編基礎(chǔ)的才能看懂??雌饋肀容^頭疼。
(3)Hopper Disassembler
與IDA功能相似,主要功能都是反編譯查看方法的實(shí)現(xiàn),這個(gè)軟件的功能相對于IDA來說,可讀性要強(qiáng)很多,反編譯出來的內(nèi)容類似于匯編與OC的運(yùn)行時(shí)的結(jié)合體,相對比較容易看出方法的具體實(shí)現(xiàn)。本次也是采用此工具進(jìn)行驗(yàn)證。
代碼混淆
在探索明文字符串加密之前先來了解一下代碼混淆:
- 方法名混淆(對關(guān)鍵的類、方法,命名成與真實(shí)意圖無關(guān)的名稱)
- 創(chuàng)建shell腳本
- 聲明要替換的方法名列表
- 生成對應(yīng)的轉(zhuǎn)義之后的無序字符串
- 邏輯混淆(添加無用又不影響邏輯的代碼片段,迷糊逆向人員)
- 類名方法名混淆(例如:ios-class-guard)
Warning!!! 前方高能
Guideline 2.3.1 - Performance
We discovered that your app contains hidden features. Specifically, It would be appropriate to remove all code obfuscation and selector mangling or to explain in detail the purpose of its inclusion before resubmitting for review.
據(jù)說從17年開始蘋果拒審代碼混淆的APP。所以項(xiàng)目整體代碼的混淆目前感覺意義不大。
第三方加固
第三方的服務(wù)(例如:網(wǎng)易云易盾)這類的加固成效如何不太清楚,這里拿出來給個(gè)參考,畢竟沒用過,可能是否選用要看公司的決策了。

第三方明確說明了加固中使用了代碼混淆,不知道是使用什么黑科技混淆的,肯定不是跑腳本的方式,好奇寶寶可以研究一下~~
字符串加密調(diào)研和嘗試
對于被砸殼的二進(jìn)制文件,逆向分析人員分析代碼有一條重要線索,也就是被硬編碼的明文字符串。比如說,你的 app 被人抓包了,某些數(shù)據(jù)請求接口也被人發(fā)現(xiàn)了,那么很簡單,逆向人員可以直接拷貝特征比較明顯的字符串到 hopper 中搜索,通過查看該字符串被引用的地方,可以很快的找到相應(yīng)的邏輯代碼。對于這一步的防范,需要做的就是對硬編碼的明文進(jìn)行加密。
使用靜態(tài)內(nèi)連 C 函數(shù)
- inline函數(shù)避免了普通函數(shù)的,在匯編時(shí)必須調(diào)用call的缺點(diǎn)。
- 取消了函數(shù)的參數(shù)壓棧,減少了調(diào)用的開銷,提高效率。所以執(zhí)行速度確比一般函數(shù)的執(zhí)行速度要快。
定義:
UIKIT_STATIC_INLINE NSString *testScheme() {
return @"testtesttesttest";
}
static inline NSString * testName() {
return @"test";
}
使用:
@"appId": testRrd(),
@"scheme" : testScheme()
結(jié)果:

很明顯明文字符串還是很容易暴露出來~~
方案PASS!
Swift 安全性更高
- 越獄開源社區(qū)對反編譯 Swift 的支持也不是很即時(shí)。
- 一些反編譯工具并不支持反編譯含有 Swift 的二進(jìn)制文件(例如:class-dump)。
- 能夠反編譯 Swift 的工具也表現(xiàn)出 Swift 更安全的一面。
我對同樣邏輯的同樣代碼(只是語法不同)的 OC 和 Swift 方法(方法中包含明文字符串)進(jìn)行了測試:


上面對比明顯可以看出 OC 經(jīng)過反編譯之后暴露出來的信息更能多,并且明文字符串顯而易見。
但是如果 Swift 方法添加了對 OC 的支持(@objc)也會使安全性降低。

現(xiàn)階段 Swift 相對 OC 來說更安全一些,等 Swift 日趨穩(wěn)定,以及越獄開源社區(qū)的逐漸支持,可能這一點(diǎn)優(yōu)勢可能就不明顯了。
UAObfuscatedString
有個(gè)開源代碼可以用,UAObfuscatedString,這個(gè)開源混淆代碼寫出來的字符串是以點(diǎn)語法的方式連接起來。
語法:
@"url" : NSMutableString.string.w.w.w.dot.b.a.i.d.u.dot.c.o.m
測試混淆結(jié)果:

UAObfuscatedString 是通過 Category 和 Extension 來實(shí)現(xiàn)這種點(diǎn)語法拼接字符串,存在比較明顯的規(guī)律,外加它是開源的可能安全性不是很高,如單從加密的角度去使用這個(gè)開源庫感覺意義不大。
使用異或加密
原理:通過位運(yùn)算的 ^ 異或運(yùn)算符把字符串與一個(gè)指定的值進(jìn)行運(yùn)算,從而改變字符串中每個(gè)字符的值,這樣就可以得到一個(gè)加密后的字符串;當(dāng)把加密后的字符串作為程序輸入內(nèi)容后,異或運(yùn)算會把加密后的字符串還原為原有字符串的值。
使用:
@"key" : testMethod()
// Key 可以是0~255的Int
#define XORKEY 0xC9
static void XOREncrypt(unsigned char *str, unsigned char key) {
unsigned char *p = str;
while (((*p) ^= key) != '\0') {
p++;
}
}
static id testMethod(void) {
unsigned char str[] = {(XORKEY ^ 'e'), (XORKEY ^ 'n'), (XORKEY ^ 'c'), (XORKEY ^ 'r'),
(XORKEY ^ 'y'), (XORKEY ^ 'p'), (XORKEY ^ 't'), (XORKEY ^ '\0')};
XOREncrypt(str, XORKEY);
static unsigned char result[7];
memcpy(result, str, 7);
return [[NSString alloc] initWithFormat:@"%s", result];
}

這里將字符用函數(shù)指針成員的形式存儲,反編譯后,只留下了地址,去掉了明文字符串直接暴露的風(fēng)險(xiǎn)。