iOS 應(yīng)用加固方法

前提

眾所周知,iOS系統(tǒng)安全性非常高,很少出現(xiàn)漏洞,幾乎不會(huì)中毒。大家認(rèn)為蘋果系統(tǒng)的封閉性會(huì)使iOS APP安全性比較高,但是實(shí)際上iOS應(yīng)用本身被破解的難度并不高,一旦在越獄設(shè)備上,ipa被分析就會(huì)變得很容易。對(duì)于iOS開發(fā)者來說,有必要了解一些APP加固的方法,用以提高破解的難度,特別是針對(duì)一些金融、游戲類APP的開發(fā)。

iOS代碼保護(hù)

在大多數(shù)iOS應(yīng)用中,一些工具,比如Clutch,class-dump,cycript,lldb,theos.對(duì)應(yīng)用程序的結(jié)構(gòu),代碼邏輯,運(yùn)行流程,可以做到很容易的分析。然后進(jìn)行應(yīng)用的破解,篡改,重簽名??梢詮哪嫦蚍治龅姆绞阶龃a保護(hù)的思路:

  • 1.靜態(tài)分析:針對(duì)這種情況可以把字符串加密,類名方法名混淆,代碼混淆
  • 2.調(diào)試 :反調(diào)試
  • 3.注入 :反注入
  • 4.中間人攻擊 :https, 證書驗(yàn)證, 數(shù)據(jù)加密

一,靜態(tài)分析

靜態(tài)分析是指用工具對(duì)程序結(jié)構(gòu),代碼邏輯的分析。很大程度上取決關(guān)鍵字,通過關(guān)鍵字找到敏感代碼,進(jìn)行破解。所以靜態(tài)分析的防護(hù)主要是代碼混淆。

1,混淆硬編碼的明文字符串

明文字符串可直接在二進(jìn)制包搜索到,常常是作為逆向分析的切入口,隱藏明文字符串可有效提升靜態(tài)分析的難度。在源代碼中將字符串加密,運(yùn)行時(shí)先解密在使用,如果直接在代碼中寫加密后字符串,代碼的可讀性會(huì)變得非常差。網(wǎng)上又一個(gè)方法不是很優(yōu)雅,但有效,私以為不錯(cuò):

  • 將源代碼中的字符串通過函數(shù)宏手動(dòng)標(biāo)記
  • 打包的時(shí)候拷貝源代碼副本
  • 執(zhí)行腳本,將副本代碼中所有標(biāo)記過的字符串,替換成decrypt("密文")的形式
  • 在適當(dāng)?shù)奈恢茫迦雂ecrypt函數(shù)的實(shí)現(xiàn)(或者事先在源代碼中寫好)
  • 編譯

通過函數(shù)宏手動(dòng)標(biāo)記字符串:


執(zhí)行加密腳本后:


這里的加密僅僅是和0xAA做了一個(gè)簡(jiǎn)單的異或運(yùn)算,解密函數(shù)內(nèi)聯(lián)編譯到代碼中。

上圖是未做混淆前的反匯編代碼,可以直接看到明文字符串。
下面是經(jīng)過混淆的反匯編代碼,已經(jīng)看不到明文字符串了。

2,objective C代碼混淆

網(wǎng)上方法較多,其中尤以念茜大神的方法為佳。大抵是在編譯前執(zhí)行混淆腳本,對(duì)OC函數(shù)(消息)進(jìn)行混淆。但是又不能全部混淆,有一些是SDK的代理回掉,如tableView的UITableViewDataSource,混淆后將不會(huì)調(diào)用,如繼承子類的init,混淆后也不會(huì)調(diào)用。解決辦法有很多,這里介紹兩個(gè)匹配的辦法:

  • 1,建立一個(gè)索引文件,將需要混淆的函數(shù)寫入索引文件,混淆腳本讀取索引文件的函數(shù)名進(jìn)行匹配。
  • 2,在代碼中以前綴或后綴的方式標(biāo)識(shí)需要混淆的函數(shù),混淆腳本通過這些標(biāo)識(shí)進(jìn)行自動(dòng)混淆。

匹配函數(shù)名后,將混淆過的函數(shù)寫入數(shù)據(jù)庫(kù)中記錄下來,以便在后續(xù)在分析app Crash的時(shí)候找對(duì)應(yīng)的函數(shù),快速定位到對(duì)應(yīng)的問題代碼.
未做混淆的代碼經(jīng)過class-dump或者直接在hopper中可直接看到函數(shù)名,逆向者以此猜測(cè)程序功能,快速切入,找到hock點(diǎn)。



混淆腳本設(shè)置

混淆后的代碼,無法通過函數(shù)名猜測(cè)到程序功能,可大大增加逆向難度。

代碼混淆除了函數(shù)名的混淆,還有類名,協(xié)議名,文件名的混淆等。此外,敏感代碼可以用C函數(shù)來實(shí)現(xiàn),可以避免在class-dump等工具中倒出,但是在nm等工具中還是可以看到一些符號(hào)表的信息。

小結(jié),靜態(tài)分析的防護(hù)手段主要是作代碼的混淆,已到達(dá)提升逆向的難度。此外還有代碼邏輯的混淆,通過在代碼中加入大量無用的邏輯判斷,增加程序結(jié)構(gòu)的復(fù)雜性,以此提升程序在ida,hopper等工具分析中的難度。但是此方法對(duì)源代碼的改動(dòng)性較大,使代碼的可讀性變得極差。

二,動(dòng)態(tài)分析 反調(diào)試

逆向者不僅可以靜態(tài)分析程序,也可以通過debugserver,lldb等工具動(dòng)態(tài)分析程序,通過在程序中打斷點(diǎn),修改內(nèi)存中的變量等方式分析,改變程序的行為。以此達(dá)到分析,hock程序的目的。

1,反調(diào)試之 ptrace

談到debug,首先會(huì)想到的一個(gè)系統(tǒng)調(diào)用是ptrace,它主要用于實(shí)現(xiàn)斷點(diǎn)調(diào)試和系統(tǒng)調(diào)用跟蹤。 PT_DENY_ATTACH是蘋果增加的一個(gè)ptrace選項(xiàng),用以防止gdb等調(diào)試器依附到某進(jìn)程。代碼如下:

#ifndef PT_DENY_ATTACH
#define PT_DENY_ATTACH 31
#endif

typedef int (*ptrace_ptr_t)(int _request, pid_t _pid, caddr_t _addr, int _data);

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];


    void *handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW);
    ptrace_ptr_t ptrace_ptr = (ptrace_ptr_t)dlsym(handle, "ptrace");
    ptrace_ptr(PT_DENY_ATTACH, 0, 0, 0);

}

手邊沒有越獄機(jī)器,直接通過xcode debug,應(yīng)用會(huì)Crash掉,安裝到手機(jī)后可正常打開。
針對(duì)這種ptrace的反反調(diào)試方法其實(shí)很簡(jiǎn)單,通過下斷點(diǎn),然后修改ptrace的參數(shù)或者用hook函數(shù)去掉反調(diào)試保護(hù)就可以搞定。也可在程序多個(gè)處調(diào)用來增加crash,提高逆向難度。

2,反調(diào)試之 sysctl

思路是通過sysctl查看信息進(jìn)程里的標(biāo)記,判斷自己是否正在被調(diào)試。sysctl是用以查詢內(nèi)核狀態(tài)的接口,并允許具備相應(yīng)權(quán)限的進(jìn)程設(shè)置內(nèi)核狀態(tài)。其定義如下:

 int sysctl(int *name, u_int namelen, void *old, size_t *oldlen, void *newp, size_t newlen);
 name參數(shù)是一個(gè)用以指定查詢的信息數(shù)組;
 namelen用以指定name數(shù)組的元素個(gè)數(shù);
 old是用以函數(shù)返回的緩沖區(qū);
 oldlen用以指定oldp緩沖區(qū)長(zhǎng)度;
 newp和newlen在設(shè)置時(shí)使用;
    當(dāng)進(jìn)程被調(diào)試器依附時(shí),kinfo_proc結(jié)構(gòu)下有一個(gè)kp_proc結(jié)構(gòu)域,kp_proc的p_flag的被調(diào)試標(biāo)識(shí)將被設(shè)置,即會(huì)進(jìn)行類似如下的設(shè)置:
    kinfo_proc. kp_proc. p_flag & P_TRACED
   其中P_TRACED的定義如下:
   #define P_TRACED        0x00000800  /* Debugged process being traced */

我們可以通過sysctl查詢進(jìn)程相應(yīng)的kinfo_proc信息,查詢函數(shù)的實(shí)現(xiàn)可以這樣:

static int is_debugged() __attribute__((always_inline));

@implementation ViewController


- (void)viewDidLoad {
    [super viewDidLoad];

    if (is_debugged() == YES) {
        self.label.text = @"is_debugged ";
        exit(-1);
    }else{
        self.label.text = @"not is_debugged";
    }

}

static int is_debugged(){
    int name[4] = {CTL_KERN,KERN_PROC,KERN_PROC_PID,getpid()};
    struct kinfo_proc Kproc;
    size_t kproc_size = sizeof(Kproc);
    
    memset((void*)&Kproc, 0, kproc_size);
    
    if (sysctl(name, 4, &Kproc, &kproc_size, NULL, 0) == -1) {
        perror("sysctl error \n ");
        exit(-1);
    }
    
    return (Kproc.kp_proc.p_flag & P_TRACED) ? 1 : 0;
}

針對(duì)sysctl的反反調(diào)試的思路其實(shí)很簡(jiǎn)單,只需要在函數(shù)返回時(shí)清除p_flag標(biāo)識(shí)位即可,根據(jù)sysctl.h文件中的定義:

#define CTL_KERN    1
#define KERN_PROC 14
#define KERN_PROC_PID 1

以及sysctl的第二個(gè)參數(shù)為4,對(duì)sysctl下條件斷點(diǎn),在sysctl返回后,根據(jù)反編譯二進(jìn)制文件找到kproc的首地址,接下來找到p_flag相對(duì)kproc首地址的偏移,最后修改對(duì)應(yīng)內(nèi)存地址的值就OK了。

文中混淆腳本 以及Dome地址

動(dòng)態(tài)分析的防護(hù)還有很多,如:dylib注入檢測(cè),越獄檢測(cè)等。當(dāng)然,也有相應(yīng)的反反調(diào)試手段,且許多檢測(cè)方法涉及到iOS的私有API,存在一定的上架風(fēng)險(xiǎn)。

三,總結(jié)

總體來說,iOS系統(tǒng)安全性是很高的,且大多數(shù)iOS應(yīng)用都沒做混淆,反調(diào)試等。對(duì)于金融類,游戲類的應(yīng)用防護(hù)套路來一點(diǎn)還是能增加逆向破解的難度的。當(dāng)然,要完全防止程序被調(diào)試或者被逆向,理論上來說是不可能的。

參考文章

iOS App的加固保護(hù)原理 http://www.cocoachina.com/ios/20170324/18955.html
iOS代碼混淆 http://xelz.info/blog/2016/11/20/ios-code-obfuscation/
阿里 iOS Anti-Debug https://jaq.alibaba.com/blog.htm?id=53
關(guān)于反調(diào)試&反反調(diào)試那些事 http://bbs.iosre.com/t/topic/8179
看雪論壇 iOS加固淺談之字符串加密 http://bbs.pediy.com/thread-217991.htm

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容