前言
網(wǎng)上有的混淆是為了安全或做馬甲包像屋,是可以上 App Store 的輕度混淆。本篇文章說的是公司需要通過一些渠道的過審奏甫,而做的強(qiáng)度混淆凌受,沒試過上線。
(PS:由于項(xiàng)目老舊龐大挠进,本篇文章記錄遇到問題及解決方式誊册,應(yīng)該是很詳細(xì)的了??解虱。)
強(qiáng)度混淆一般重要的是代碼混淆、字符串加密、其他(例如:防越獄于宙、防截屏、防錄屏等)至会。
一谱俭、逆向工具
逆向的方式有很多昆著,最基礎(chǔ)的就是 class-dump(可以查看所有頭文件),深度的可以用 Hopper煤痕。
可以在開始之前逆向一下看看接谨,混淆后對比一下。
二巷帝、代碼混淆
2.1 宏替換方案
宏替換指的是類名
扫夜、方法名
历谍、屬性名
的混淆,即將這些生成混淆宏印蔬,在預(yù)編譯時(shí)期進(jìn)行替換脱衙。
2.2 STCObfuscator
STCObfuscator 是用來進(jìn)行 Object-C 代碼混淆的工具,在模擬器 DEBUG 環(huán)境下運(yùn)行生成混淆宏退唠,混淆的宏可以在其他環(huán)境下進(jìn)行編譯荤胁,支持 Cocoapod 代碼混淆。
使用方式很簡單垢油,作者步驟很詳細(xì)滩愁。但是我的項(xiàng)目比較復(fù)雜,在使用中遇到些問題硝枉,分享一下妻味。
2.3 篩選需要混淆的類名
/** 不進(jìn)行混淆的類 */
@property (nonatomic, strong) NSArray *unConfuseClassNames;
/** 不進(jìn)行混淆的帶特性前綴的類 */
@property (nonatomic, strong) NSArray *unConfuseClassPrefix;
/** 不進(jìn)行混淆的帶有特定前綴的符號名 */
@property (nonatomic, strong) NSArray *unConfuseMethodPrefix;
如上,首先工具提供了不進(jìn)行混淆的類或前綴蔑匣,看起來很簡單棕诵,把所有第三方的前綴加進(jìn)去就可以了校套。但是我們的項(xiàng)目很大很舊牧抵,找出所有第三方前綴,實(shí)驗(yàn)了幾次還是很多錯(cuò)誤妹孙,尤其是有些沒有前綴或
.a
根本看不到的一些获枝,總之是錯(cuò)誤很多省店。換個(gè)思路,我們來添加需要混淆的類雹舀,當(dāng)然需要改源碼粗俱,改動不大,可以接受签财。如果你的項(xiàng)目結(jié)構(gòu)和代碼足夠好,恭喜你寫上你們的統(tǒng)一前綴就行了模庐。但是如果你和我一樣遇到項(xiàng)目很舊油宜,沒有統(tǒng)一的前綴就需要
篩選出所有要進(jìn)行混淆的類名
慎冤。
我們采用的方式:
- 我們的項(xiàng)目結(jié)構(gòu)目錄結(jié)構(gòu)是整理過的,所以明確的知道哪些是項(xiàng)目代碼(項(xiàng)目目錄整理見之前文章)醉者;
- 使用腳本把所有要混淆的類名輸出在
.txt
文檔中(待補(bǔ)充)披诗。
簡單腳本教程:
- 新建文件夾
getClassName
呈队; - 在改文件夾下新建
job.sh
腳本文件; - 腳本文件內(nèi)容為
#!/bin/bash
; - 依次執(zhí)行下面的命令:
? ~ /Users/zhangmaomao/Desktop/getClassName
? getClassName chmod +x job.sh
? getClassName ./job.sh
? getClassName find ./Classes -name "*.h">path.txt
-
path.txt
中得到所有.h
文件的路徑名如下:
./Classes/Home/EHIHomeDefines.h
./Classes/Home/Activity/Models/FocusModel.h
./Classes/Home/Activity/ViewControllers/HomeActivityViewController.h
./Classes/Home/Activity/Views/HomeActivityCell.h
...
- 刪掉類名前面路徑:
? getClassName book=/Users/zhangmaomao/Desktop/getClassName/path.txt
? getClassName while read line
while> do
while> echo ${line##*/} >>result.txt
while> done <$book
2.4 篩選不混淆的model類名
經(jīng)過上面的過程粒竖,混淆后終于運(yùn)行起來
了H锩纭(注意該工具是 Debug 下生成 #define 混淆宏沿彭,然后使用宏替換。仔細(xì)看下代碼锅移,直接運(yùn)行的時(shí)候注釋相關(guān)代碼)
但是饱搏,數(shù)據(jù)不正常顯示
推沸。排查了以后發(fā)現(xiàn)又是老項(xiàng)目的坑??券坞。排查原因:model 類接收數(shù)據(jù)沒有一次性解析肺素,第二層數(shù)據(jù)獲取問題倍靡。因?yàn)?model 的屬性已經(jīng)混淆了,再去去數(shù)據(jù)自然是沒有的他挎,代碼例如:
self.storeStockResponse = [StoreStockResponse objectWithKeyValues:chauffeurResult.Result];
self.filterTitleList = [[CarFilterType objectArrayWithKeyValuesArray:self.storeStockResponse.CarFilterTypeList] mutableCopy];
現(xiàn)在就需要篩選出不混淆的 model 類名捡需,嗯站辉。。如果你有基類或統(tǒng)一前綴的話就 happy 了殊霞,不幸的是我沒有汰蓉。。。需要的話操作如下:
- 腳本找出調(diào)用數(shù)據(jù)解析的類名岩齿,這是必須要篩掉的(待補(bǔ)充)苞俘;
- 除了獲取到的數(shù)據(jù)解析吃谣,還會有 model 類轉(zhuǎn) json 數(shù)據(jù)請求接口的情況(代碼例如下),對象調(diào)用的就沒有辦法像上面那樣拿到調(diào)用者肃晚,所以保險(xiǎn)起見我們的情況需要篩選出所有 model 類仔戈;
[contactModel keyValues]
- 不幸的是我們也沒有統(tǒng)一的后綴。晋修。墓卦。只能先篩選多數(shù)的 model、Response睁本、Request著榴、Dto 結(jié)尾的類脑又,還有熟悉重要的挑幾個(gè);
- 結(jié)合上面腳本篩選出來的往衷,就是一堆不需要篩選的類了严卖。
2.5 特殊字符
還有一點(diǎn)是我篩選掉了failHandler
哮笆,因?yàn)楹芏嗟谌嚼锇诖耍煜湍貌坏交卣{(diào)了福铅。
[STCObfuscator obfuscatorManager].unConfuseMethodPrefix = @[@"failHandler"];
2.6 開始混淆
從原來要篩選的文檔中刪除掉不混淆的 model 類名项阴,清空STCDefination.h
文件环揽,重新在 Debug 環(huán)境下在模擬器上運(yùn)行。
使用宏替換運(yùn)行汛兜,終于跨扮,程序運(yùn)行正常
!
(運(yùn)行時(shí)看不出來混淆效果帝嗡,這時(shí)候 class-dump 出來的頭文件已經(jīng)明顯混淆過了)
三哟玷、字符串加密(Hikari)
直到這里是還是不夠的,項(xiàng)目中難免有明文字符串喉脖,比如加密鑰字符串等抑月,敏感字符的混淆是必須的G酢!性锭!
所以又使用了這個(gè)工具:Hikari叫胖。
這個(gè)工具很強(qiáng)大瓮增,有以下幾種配置可以選擇:
-mllvm -enable-bcfobf 啟用偽控制流
-mllvm -enable-cffobf 啟用控制流平坦化
-mllvm -enable-splitobf 啟用基本塊分割
-mllvm -enable-subobf 啟用指令替換
-mllvm -enable-acdobf 啟用反class-dump
-mllvm -enable-indibran 啟用基于寄存器的相對跳轉(zhuǎn)绷跑,配合其他加固可以徹底破壞IDA/Hopper的偽代碼(俗稱F5)
-mllvm -enable-strcry 啟用字符串加密
-mllvm -enable-funcwra 啟用函數(shù)封裝
-mllvm -enable-allobf 依次性啟用上述所有標(biāo)記
-mllvm -enable-strcry
是必須的,問題也不大的。如果你的項(xiàng)目很大讳苦,建議不要貪多带膜,加上以后打包會慢,打出來的包先安裝后看一下是不是可以的<否則就白話時(shí)間試這么配置了>
鸳谜,你的項(xiàng)目是否可以支持這么多的選項(xiàng)配置膝藕。如果不行的話需要再排查下具體原因了。
四咐扭、其他
其他的就很簡單了芭挽,看你要過的審核具體要求了滑废,大概說幾個(gè)重要的。
4.1 防越獄袜爪、防截屏
SavetyDetectionTool.h
:
#import <Foundation/Foundation.h>
@interface SavetyDetectionTool : NSObject
+ (instancetype)sharedSavetyDetection;
- (void)detect;
@end
SavetyDetectionTool.m
:
#import "SavetyDetectionTool.h"
#import <UMMobClick/MobClick.h>
#import <ReplayKit/ReplayKit.h>
static SavetyDetectionTool *savetyDetect = nil;
@implementation SavetyDetectionTool
+ (instancetype)sharedSavetyDetection {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
savetyDetect = [SavetyDetectionTool new];
});
return savetyDetect;
}
- (void)detect {
if ([MobClick isJailbroken]) {
UIAlertView *aleter = [[UIAlertView alloc] initWithTitle:@"提示" message:@"您的設(shè)配已越獄,請注意保護(hù)隱私安全" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"知道了", nil];
[aleter show];
}
//注冊通知
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(userDidTakeScreenshot)
name:UIApplicationUserDidTakeScreenshotNotification object:nil];
}
- (void)userDidTakeScreenshot {
UIAlertView *aleter = [[UIAlertView alloc] initWithTitle:@"提示" message:@"監(jiān)測到設(shè)備正在截屏辛馆,請注意安全防護(hù)" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"知道了", nil];
[aleter show];
}
@end
使用俺陋,AppDelegate.m
:
[[SavetyDetectionTool sharedSavetyDetection] detect];
4.2 防錄屏
基類UIViewController.m
:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:YES];
// 監(jiān)測當(dāng)前設(shè)備是否處于錄屏狀態(tài)
UIScreen *screen = [UIScreen mainScreen];
if (@available(iOS 11.0, *)) {
if (screen.isCaptured) {
[self screenshots];
}
[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(screenshots) name:UIScreenCapturedDidChangeNotification object:nil];
}
}
- (void)screenshots {
UIAlertView *aleter = [[UIAlertView alloc] initWithTitle:@"提示" message:@"監(jiān)測到設(shè)備正在錄屏,請注意安全防護(hù)" delegate:nil cancelButtonTitle:nil otherButtonTitles:@"知道了", nil];
[aleter show];
}
- (void)dealloc {
if (@available(iOS 11.0, *)) {
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIScreenCapturedDidChangeNotification object:nil];
}
}
4.3 只保留驗(yàn)證碼登錄方式
看審核需求昙篙。
五腊状、總結(jié)
最后有大佬幫忙逆向了下,這次的混淆還是不錯(cuò)的苔可,但愿順利過審缴挖。后面可以再研究下更好的加密方式,但是焚辅,更重要的是我們必須優(yōu)化好我們的項(xiàng)目映屋!
根據(jù)這次的混淆,大概總結(jié)重點(diǎn)注意一下幾點(diǎn):
5.1 統(tǒng)一前綴
組件庫:SEED
法焰;
自駕:EHI
秧荆;
專車:EHIC
;
國際租車:EHII
埃仪;
出租車:EHIT
;
之后改動統(tǒng)一修改乙濒。
5.2 統(tǒng)一類名規(guī)范
UIViewController:以ViewController
結(jié)尾;
ViewModel:以ViewModel
結(jié)尾(ViewModel 的擴(kuò)展類名稱不用再加EHI
)卵蛉;
UIView:以View
結(jié)尾颁股;
Model:以Model
結(jié)尾;
工具類擴(kuò)展傻丝,除大功能外甘有,主功能用+EHICategory
格式。例如:
NSString+EHICategory
NSString+EHIAES
5.3 統(tǒng)一數(shù)據(jù)解析方式
統(tǒng)一使用EHIModel.h
葡缰。
5.4 統(tǒng)一圖片加載方式
統(tǒng)一使用EHIWebImage.h
亏掀。
5.5 統(tǒng)一目錄結(jié)構(gòu)、第三方的使用
無論是自己寫的泛释,還是第三方放進(jìn)去的滤愕,明確文件的位置,具體參考之前的目錄規(guī)范怜校。