2019-03-18 10:45:37分類:IOS應(yīng)用設(shè)計(jì)7336
應(yīng)用程序加載過程
對于諸多逆向愛好者來說,給一個app脫殼是一項(xiàng)必做的事情。基于安全性的考慮,蘋果對上架到appstore的應(yīng)用都會進(jìn)行加密處理,所以如果直接逆向一個從appstore下載的應(yīng)用程序時,所能看到的“源代碼”將非常的晦澀難懂。為了能看懂應(yīng)用程序的“源代碼”,就必須對應(yīng)用程序進(jìn)行解密,也就是所謂的脫殼。脫殼后的目的是可以分析應(yīng)用程序的一些技術(shù)實(shí)現(xiàn)原理,或者利用一些漏洞進(jìn)行攻擊和測試。
這篇文章不是一篇介紹如何利用工具去進(jìn)行脫殼的教程,而只是簡單的分析這些常用脫殼工具的實(shí)現(xiàn)原理。要想了解脫殼原理,就要先去了解一個被加密的應(yīng)用程序是如何被運(yùn)行的。下面一張圖片簡單的介紹了一個被加殼后的應(yīng)用程序被加載和運(yùn)行的過程:
脫殼原理以及常見的工具
要對一個殼應(yīng)用進(jìn)行脫殼處理,無非就是采用靜態(tài)脫殼和動態(tài)脫殼兩種方法:靜態(tài)脫殼就是在已經(jīng)掌握和了解到了殼應(yīng)用的加密算法和邏輯后在不運(yùn)行殼應(yīng)用程序的前提下將殼應(yīng)用程序進(jìn)行解密處理。靜態(tài)脫殼的方法難度大,而且加密方發(fā)現(xiàn)應(yīng)用被破解后就可能會改用更加高級和復(fù)雜的加密技術(shù);動態(tài)脫殼就是從運(yùn)行在進(jìn)程內(nèi)存空間中的可執(zhí)行程序映像(image)入手,來將內(nèi)存中的內(nèi)容進(jìn)行轉(zhuǎn)儲(dump)處理來實(shí)現(xiàn)脫殼處理。這種方法實(shí)現(xiàn)起來相對簡單,且不必關(guān)心使用的是何種加密技術(shù)。從上面的殼應(yīng)用程序運(yùn)行的過程就可以看出無論殼程序如何被加密處理,最終運(yùn)行后在進(jìn)程中的代碼映像(image)始終是被解密后的原始程序二進(jìn)制。所以只要一個進(jìn)程內(nèi)存空間中的代碼映像(image)能被讀取和訪問就可以實(shí)現(xiàn)動態(tài)脫殼。下面要介紹的兩個工具就是巧妙的運(yùn)用了兩種不同的訪問技巧來實(shí)現(xiàn)動態(tài)脫殼的。
一、利用動態(tài)庫注入來實(shí)現(xiàn)脫殼的dumpdecrypted/frida-ios-dump
dumpdecrypted和frida-ios-dump都是在github上開源的項(xiàng)目,下載地址分別為:https://github.com/stefanesser/dumpdecrypted和https://github.com/AloneMonkey/frida-ios-dump。關(guān)于使用這兩個工具來進(jìn)行脫殼的文檔非常之多。我們知道一個應(yīng)用除了有一個可執(zhí)行程序外,還會鏈接非常多的動態(tài)庫。動態(tài)庫加載后和可執(zhí)行程序共享相同的進(jìn)程內(nèi)存空間,而且動態(tài)庫中的代碼是可以訪問整個進(jìn)程內(nèi)存空間中的有權(quán)限的區(qū)域的,包括可執(zhí)行程序的image被加載到進(jìn)程中的內(nèi)存區(qū)域。因此只要想辦法讓應(yīng)用程序加載某個特定的第三方動態(tài)庫,也就是讓這個第三方動態(tài)庫注入到應(yīng)用程序的進(jìn)程中去就可以實(shí)現(xiàn)將被解密過后的可執(zhí)行程序在進(jìn)程內(nèi)存中的image信息轉(zhuǎn)儲到文件中去從而實(shí)現(xiàn)脫殼處理。對于一個越獄后的設(shè)備來說主要可以通過兩種方法來實(shí)現(xiàn)第三方動態(tài)庫的注入:
設(shè)置環(huán)境變量DYLD_INSERT_LIBRARIES的值指向這個第三方動態(tài)庫的路徑。然后運(yùn)行要脫殼的應(yīng)用程序即可。 DYLD_INSERT_LIBRARIES環(huán)境變量的設(shè)置是一個操作系統(tǒng)提供的特性,所有運(yùn)行的程序都會加載這個環(huán)境變量中所指向的動態(tài)庫文件。
將第三方動態(tài)庫文件保存在越獄設(shè)備的/Library/MobileSubstrate/DynamicLibraries/目錄下并編寫對應(yīng)的庫的同名plist文件,所有plist中指定的可執(zhí)行程序一旦運(yùn)行就會加載對應(yīng)的動態(tài)庫(此目錄即Tweak插件所在的目錄)。
還有一種直接修改對應(yīng)mach-o格式的可執(zhí)行文件內(nèi)容來實(shí)現(xiàn)動態(tài)庫注入。
動態(tài)庫加載的問題解決后就需要解決動態(tài)庫中代碼運(yùn)行的時機(jī)問題了。要想讓一個被加載的動態(tài)庫在加載后自動運(yùn)行某一段代碼可以有四種方法:
建立一個C++全局對象,并在對象所屬類的構(gòu)造函數(shù)中添加特定代碼。
建立一個OC類,并在OC類的+load方法中添加特定的代碼。
生成動態(tài)庫時指定一個初始化init入口函數(shù),并在入口函數(shù)中添加特定的代碼。
在動態(tài)庫中定義一個帶有_attribute_((constructor))聲明的函數(shù),并在函數(shù)內(nèi)添加特定的代碼。
如果你想更進(jìn)一步的了解上述那些方法的加載的原理,請參考我的文章:深入解構(gòu)iOS系統(tǒng)下的全局對象和初始化函數(shù)
dumpdecrypted這個工具就是通過建立一個名為dumpdecrypted.dylib的動態(tài)庫并在庫內(nèi)部定義了一個
函數(shù)來實(shí)現(xiàn)脫殼的。這個函數(shù)的大體實(shí)現(xiàn)會在后面繼續(xù)介紹。
二、利用父子進(jìn)程關(guān)系來實(shí)現(xiàn)脫殼的Clutch
Clutch也是一個在github上開源的項(xiàng)目,下載地址為:
https://github.com/KJCracks/Clutch。關(guān)于這個工具的使用教程也非常之多。我們知道在unix系列的操作系統(tǒng)中父進(jìn)程可以通過fork或者posix_spawnp兩個函數(shù)來運(yùn)行或者建立一個子進(jìn)程的,這兩個函數(shù)都會返回對應(yīng)的子進(jìn)程ID(PID)。iOS系統(tǒng)則可以通過task_for_pid函數(shù)來從進(jìn)程ID獲取進(jìn)程在mach內(nèi)核子系統(tǒng)中的mach port標(biāo)識。得到mach port 標(biāo)識后,就可以借助mach_vm_read_overwrite函數(shù)來讀取指定進(jìn)程空間中的任意虛擬內(nèi)存區(qū)域中所存儲的內(nèi)容。因此Clutch內(nèi)部的實(shí)現(xiàn)就是Clutch這個程序?qū)⒁M(jìn)行脫殼的程序文件路徑調(diào)用posix_spawnp函數(shù)來運(yùn)行從而成為其子進(jìn)程,然后借助task_for_pid以及mach_vm_read_overwrite函數(shù)來讀取脫殼程序子進(jìn)程在內(nèi)存中已經(jīng)被解密后的可執(zhí)行程序的image所映射的內(nèi)存空間來達(dá)到脫殼的目的的。
一個思考:可能在實(shí)際中并不一定要求是父子進(jìn)程關(guān)系,是否只要某個具有特權(quán)的程序或者運(yùn)行在root用戶上的程序只要拿到了對應(yīng)進(jìn)程的PID就可以通過mach子系統(tǒng)提供的API來讀取其他進(jìn)程內(nèi)存空間中的信息呢?
上述的兩種方法中不管是dumpdecrypted還是Clutch最終都是將被解密后的可執(zhí)行程序的image在內(nèi)存中的映射寫入到一個文件中去來保存脫殼后的內(nèi)容。參考dumpdecrypted中的dumptofile函數(shù)實(shí)現(xiàn)以及Clutch中的Dumpers目錄下的實(shí)現(xiàn)代碼就可以看出:一個可執(zhí)行程序image在內(nèi)存中映射的內(nèi)容的結(jié)構(gòu)和mach-o格式的可執(zhí)行文件結(jié)構(gòu)基本上是保持一致的。都是有一個mach_header結(jié)構(gòu)體頭還有諸多的load_command結(jié)構(gòu)體組成。因此所謂的dump處理就是將內(nèi)存中的這些結(jié)構(gòu)和數(shù)據(jù)原封不動的寫入到文件中去即完成了脫殼中的最核心的部分。如果想仔細(xì)的閱讀這部分代碼的實(shí)現(xiàn),建議先了解一下mach-o文件格式的組成。
后記
當(dāng)你了解了這些內(nèi)部實(shí)現(xiàn)后,也許你會發(fā)覺其實(shí)它的原理很簡單。而且有可能你也能很快的去實(shí)現(xiàn)??蓡栴}的關(guān)鍵是為什么這些方法總是別人能想到,而我們卻想不到呢?這是否和國人的思維以及解決問題的方式相關(guān)呢?在我們的教育和實(shí)踐體系中更多的是拿來主義和實(shí)用主義,往往很少人會對問題進(jìn)行深入的探索研究以及進(jìn)行問題的關(guān)聯(lián)性思考。但愿這種情況在未來能夠得到改進(jìn),尤其作為一個程序員,更加應(yīng)該秉持探索求知的強(qiáng)烈意愿而不是簡單復(fù)制和應(yīng)用就滿足了。