一、dyld簡介:
dyld全名為dynamic loader(動態(tài)鏈接器)(默認(rèn)路徑是/usr/lib/dyld)。
當(dāng)一個iOS應(yīng)用程序啟動時,系統(tǒng)會先讀取App的可執(zhí)行文件(Mach-O文件),從里面獲得dyld的路徑,然后加載dyld,dyld去初始化運(yùn)行環(huán)境,開啟緩存策略,加載程序相關(guān)依賴庫(其中也包含我們的可執(zhí)行文件),并對這些庫進(jìn)行鏈接,最后調(diào)用每個依賴庫的初始化方法,在這一步,runtime被初始化。當(dāng)所有依賴庫的初始化后,輪到最后一位(程序可執(zhí)行文件)進(jìn)行初始化,在這時runtime會對項目中所有類進(jìn)行類結(jié)構(gòu)初始化,然后調(diào)用所有的load方法。最后dyld返回main函數(shù)地址,main函數(shù)被調(diào)用,我們便來到了熟悉的程序入口。
從上述程序加載流程可大概得知,dyld就是負(fù)責(zé)將我們程序的可執(zhí)行文件和系統(tǒng)提供的動態(tài)庫進(jìn)行鏈接,并執(zhí)行一系列的程序初始化操作:
二、dyld執(zhí)行過程:
- 通過配置上下文信息,設(shè)置運(yùn)行環(huán)境,處理環(huán)境變量,獲取主機(jī)信息等。
2.檢查共享緩存是否已經(jīng)映射到共享區(qū)域
3.加載system frameWork
調(diào)用 instantiateFromLoadedImage 函數(shù)實例化一個 ImageLoader 對象。該函數(shù)先調(diào)用 isCompatibleMachO 來判斷文件的架構(gòu)是否和當(dāng)前的架構(gòu)兼容,然后調(diào)用 ImageLoderMachO::instantiateMainExecutable 來加載文件生成實例,并將 image 添加到全局 sAllImages 中。
4.加載所有插入的dylib
通過遍歷 DYLD_INSERT_LIBRARIES 環(huán)境變量,調(diào)用 loadInsertedDylib 加載。
在三方App的Mach-O文件中通過修改DYLD_INSERT_LIBRARIES的值來加入我們自己的動態(tài)庫,從而注入代碼,hook別人的App。
5.鏈接主程序
調(diào)用 link 鏈接主程序。內(nèi)核調(diào)用的是ImageLoader::link 函數(shù)。
6.通過link函數(shù)鏈接所有插入的庫,執(zhí)行符號替換
對 sAllimages (除了主程序的Image外)中的庫調(diào)用link進(jìn)行鏈接,然后調(diào)用 registerInterposing 注冊符號插入。
對插入的庫進(jìn)行鏈接。其主要做的事有對image進(jìn)行l(wèi)oad(加載), rebase(基地址復(fù)位),bind(外部符號綁定)
我們可以在程序中設(shè)置環(huán)境變量DYLD_PRINT_INITIALIZERS為1來打印出程序鏈接的各種依賴庫:
7.進(jìn)行初始化操作
initializeMainExecutable 執(zhí)行初始化方法,其中 +load 和 constructor 方法就是在這里執(zhí)行。 initializeMainExecutable 內(nèi)部先調(diào)用了動態(tài)庫的初始化方法,后調(diào)用主程序的初始化方法。
initializeMainExecutable依次調(diào)用了 runInitializers、processInitializers、recursiveInitialization、notifySingle。
8.尋找主程序入口并執(zhí)行
調(diào)用 getEntryFromLC_MAIN,從 Load Command 讀取LC_MAIN入口,如果沒有LC_MAIN入口,就讀取LC_UNIXTHREAD,然后跳到入口處執(zhí)行,這樣就來到了我們熟悉的main函數(shù)處
鏈接:http://www.itdecent.cn/p/73a99303cd91
鏈接:http://www.itdecent.cn/p/43db6b0aab8e