一麻削、認(rèn)識庫
庫是一種共享程序代碼的方式蒸痹,在計算機(jī)科學(xué)中,庫(英語:library)是用于開發(fā)軟件的子程序集合呛哟。庫和可執(zhí)行文件的區(qū)別是叠荠,庫不是獨(dú)立程序,他們是向其他程序提供服務(wù)的代碼扫责。
庫鏈接(英語:linking)是指把一個或多個庫包括到程序中榛鼎,有兩種鏈接形式:靜態(tài)鏈接和動態(tài)鏈接这嚣;相應(yīng)的淮逻,前者鏈接的庫叫做靜態(tài)庫,后者的叫做動態(tài)庫秽荤。
- 靜態(tài)庫
靜態(tài)鏈接是由鏈接器在鏈接時將庫的內(nèi)容加入到可執(zhí)行程序中的做法苏揣。鏈接器是一個獨(dú)立程序黄鳍,將一個或多個庫或目標(biāo)文件(先前由編譯器或匯編器生成)鏈接到一塊生成可執(zhí)行程序。靜態(tài)鏈接的最大缺點(diǎn)是生成的可執(zhí)行文件太大腿准,需要更多的系統(tǒng)資源际起,在裝入內(nèi)存時也會消耗更多的時間。
特點(diǎn)如下:
靜態(tài)庫的存在形式有.a和.framework
靜態(tài)庫由一個或多個object文件組成吐葱,可以將靜態(tài)庫拆分成多個object文件
- 動態(tài)庫
動態(tài)鏈接街望,在可執(zhí)行文件裝載時或運(yùn)行時,由操作系統(tǒng)的裝載程序加載庫弟跑。在iOS系統(tǒng)里面這個裝載程序就是dyld灾前。
特點(diǎn)如下:
動態(tài)庫的存在形式有.dylib、.framework及鏈接符號.tbd
動態(tài)庫的格式和普通二進(jìn)制文件沒有區(qū)別
動態(tài)庫的好處是可以只保留一份文件和內(nèi)存空間孟辑,可以被多個進(jìn)程使用哎甲,比如系統(tǒng)的動態(tài)庫
dyld_shared_cache_arm64
蔫敲、dyld_shared_cache_armv7s
可以減小可執(zhí)行文件的體積,不需要鏈接到目標(biāo)文件
二炭玫、動態(tài)庫注入
逆向修改三方應(yīng)用,讓三方應(yīng)用執(zhí)行我們的代碼奈嘿,這就是代碼注入,動態(tài)庫注入是一種方式吞加。其中動態(tài)庫注入分為framework注入與dylib注入裙犹。
學(xué)習(xí)動態(tài)庫注入之前,先簡單了解一下Mach-O文件
Mach-O為Mach Object文件格式的縮寫衔憨,它是一種用于可執(zhí)行文件叶圃,目標(biāo)代碼,動態(tài)庫践图,內(nèi)核轉(zhuǎn)儲的文件格式掺冠。
每個Mach-O文件包括一個Mach-O頭,然后是一系列的載入命令码党,再是一個或多個塊德崭,每個塊包括0到255個段。
現(xiàn)在我們要關(guān)注的是Mach-O的Load Command
段闽瓢,該段主要告訴操作系統(tǒng)(實(shí)質(zhì)就是dyld)如何加載文件中的數(shù)據(jù)以及動態(tài)鏈接系統(tǒng)的動態(tài)庫接癌,利用MachOView工具查看已解密的微信可執(zhí)行文件中的Load Command
段:
看到Load Command
段下的LC_LOAD_DYLIB
段心赶,該段表示應(yīng)用程序依賴的動態(tài)庫扣讼,包括動態(tài)庫名稱、當(dāng)前版本號缨叫、兼容版本號等椭符。此時我們會思考到是否可以通過一些騷操作給這個可執(zhí)行文件增加一段,加載我們自己編寫的動態(tài)庫耻姥?接下來我們嘗試一下這個騷操作
1. framework注入
上次學(xué)習(xí)到利用Shell腳本應(yīng)用重簽名三方應(yīng)用進(jìn)行調(diào)試销钝,這次我們可以使用它來動態(tài)注入framework讓三方應(yīng)用執(zhí)行我們寫的代碼。
-
在已經(jīng)重簽名三方應(yīng)用的工程下創(chuàng)建動態(tài)庫framework
在framework里簡單地創(chuàng)建一個類琐簇,并且在+load方法(類加載的時候執(zhí)行蒸健,先于main)里寫一些簡單代碼作為測試,如下:
Command + B編譯工程婉商,查看生成的app目錄
注意:
這里雖然編譯的時候會將我們創(chuàng)建的動態(tài)庫framework打包進(jìn).app文件夾內(nèi)似忧,但是,運(yùn)行的時候并不會加載我們的framework(讀者可以自行嘗試)丈秩,原因很簡單盯捌,因?yàn)檫@樣并不會修改增加Mach-O可執(zhí)行文件的LC_LOAD_DYLIB段,整個過程只是編譯->運(yùn)行重簽名腳本蘑秽,替換了我們當(dāng)前工程的.app包饺著,每次運(yùn)行都仍然是原來第三方ipa應(yīng)用里的原可執(zhí)行文件箫攀。因此我們需要對原可執(zhí)行文件下手。
方法一:
使用yololib工具將我們編譯好的動態(tài)庫framework注入原ipa包內(nèi)的可執(zhí)行文件,具體如下:
yololib 你的可執(zhí)行文件 Frameworks/xxx.framework/xxx
再次用MachOView查看可執(zhí)行文件,可以看到已經(jīng)增加了LC_LOAD_DYLIB
字段
此時再重新打包成ipa包幼衰,放到重簽名下的TargetApp文件夾進(jìn)行重簽靴跛,運(yùn)行工程就可以看到加載了我們注入的動態(tài)庫以及運(yùn)行了相關(guān)代碼,打包過程不再贅述,運(yùn)行結(jié)果如下:
方法二:
思考一下我們每次重簽完三方應(yīng)用后渡嚣,編寫完需要注入的動態(tài)庫之后再用工具注入的話汤求,如果我們下一次修改了動態(tài)庫,又再次需要手動注入一次严拒,顯得很麻煩沒必要扬绪,既然重簽名也可以通過腳本,舉一反三裤唠,我們將動態(tài)庫framework注入的操作也放到腳本挤牛,每次運(yùn)行工程的時候自動注入,我們只需要集中精力編寫我們的動態(tài)庫即可种蘸,修改重簽名腳本墓赴,在腳本末尾添加代碼,如下:
INJECT_FRAMEWORK_PATH="Frameworks/KenHookFramework.framework/KenHookFramework"
(這里命令參數(shù)寫死航瞭,需要替換成自己寫的framework)
yololib "$TARGET_APP_PATH/$APP_BINARY" "$INJECT_FRAMEWORK_PATH"
再次運(yùn)行工程诫硕,腳本就幫我們做了替換包內(nèi)容,重簽名刊侯,framework注入的操作
2.dylib注入
- 同樣地使用Shell腳本應(yīng)用重簽名新建一個空工程
-
新建一個Target
創(chuàng)建dylib之后有兩個注意點(diǎn):
1.創(chuàng)建的Library是屬于macOS下的章办,將Build Settings
下的Base SDK
更改為iOS,如下:
2.創(chuàng)建的Library簽名默認(rèn)使用Mac Developer滨彻,將其改成iOS Developer藕届,目的是讓它能在iOS平臺上使用
-
目標(biāo)工程引用、依賴dylib
工程Target切換到dylib亭饵,編譯休偶,show in Finder可以發(fā)現(xiàn)編譯出的dylib與.app文件夾在同級目錄,Xcode沒有將dylib編譯進(jìn)我們的目標(biāo)工程辜羊,因此我們需要添加依賴踏兜,如下:
再次編譯目標(biāo)工程,在xxx.app/Frameworks/目錄下就會看到對應(yīng)的dylib八秃,運(yùn)行目標(biāo)工程并不會執(zhí)行我們動態(tài)庫的代碼碱妆,原因與framework注入注意點(diǎn)同理,這里我們不使用手動注入原ipa包可執(zhí)行文件喜德,而是使用重簽名腳本替我們完成山橄,修改腳本,如下:
INJECT_FRAMEWORK_PATH="Frameworks/libKenHookDylib.dylib"
yololib "$TARGET_APP_PATH/$APP_BINARY" "$INJECT_FRAMEWORK_PATH"
-
運(yùn)行工程,檢驗(yàn)是否注入
-
查看Mach-O文件
用MachOView查看新編譯出來的可執(zhí)行文件航棱,可以看到已經(jīng)添加了對應(yīng)字段
三睡雇、總結(jié)
認(rèn)識靜態(tài)庫與動態(tài)庫
認(rèn)識應(yīng)用的Mach-O可執(zhí)行文件
通過增加Load Command的LC_LOAD_DYLIB字段,指定動態(tài)庫的路徑實(shí)現(xiàn)注入