- 什么是靜態(tài)鏈接
- 如何實現(xiàn)靜態(tài)鏈接
- 靜態(tài)鏈接的優(yōu)點與缺點
- 什么是動態(tài)鏈接
- 如何實現(xiàn)動態(tài)鏈接
- 動態(tài)鏈接的優(yōu)點與缺點
- SO文件格式簡析
- 根據(jù)SO文件格式進行靜態(tài)反編譯
靜態(tài)鏈接
一段代碼從文本編輯器上產(chǎn)生到最終能夠在機器上運行恳邀,經(jīng)歷了非常多的階段,概括而言蕾殴,至少包含了以下幾個階段:
- 編譯: 編譯器通過詞法分析肥败,語法分析谭确,語義分析等,將一段代碼翻譯成匯編語言
- 匯編:將匯編語言翻譯成機器指令
- 鏈接:解決符號之間的重定位問題
- 裝載:將可執(zhí)行文件加載到內(nèi)存
靜態(tài)鏈接就是在裝載之前,就完成所有的符號引用的一種鏈接方式于樟。靜態(tài)鏈接的處理過程分為2個步驟:
- 空間與地址的分配艘希。掃描所有的目標文件硼身,合并相似段硅急,收集當中所有的符號信息。
- 符號解析與重定位佳遂。調(diào)整代碼位置营袜。
如何可得,在完成靜態(tài)鏈接之后丑罪,可執(zhí)行文件中代碼段荚板、數(shù)據(jù)段等的虛擬地址已經(jīng)確定,即當此可執(zhí)行文件被載入到內(nèi)存后吩屹,代碼段的起始位置就在0000000000400400
的位置跪另。
靜態(tài)鏈接的優(yōu)缺點
- 優(yōu)點: 簡單
- 缺點:
- 浪費內(nèi)存空間。在多進程的操作系統(tǒng)下煤搜,同一時間免绿,內(nèi)存中可能存在多個相同的公共庫函數(shù)。
- 程序的開發(fā)與發(fā)布流程受模塊制約擦盾。 只要有一個模塊更新嘲驾,那么就需要重新編譯打包整個代碼。
為了解決以上2個問題迹卢,就誕生了動態(tài)鏈接辽故。
動態(tài)鏈接
基本思想就是將對符號的重定位推遲到程序運行時才進行。
只要推遲到運行時進行符號的重定位腐碱,就能解決靜態(tài)鏈接的兩個缺點誊垢。
對于第一個缺點:在運行時重定位,如果在運行過程中調(diào)用了公共庫函數(shù)或者其他模塊的函數(shù)症见,系統(tǒng)只需要在內(nèi)存中維護一份公共庫代碼即可彤枢,只要將不同應用程序?qū)矌旌瘮?shù)的調(diào)用地址設置成相同即可。
對于第二個缺點:理論上只要將需要替換的模塊更新筒饰,無需將整個應用程序打包缴啡。
動態(tài)鏈接的實現(xiàn)
對于靜態(tài)鏈接來說,系統(tǒng)只需要加載一個文件(可執(zhí)行文件)到內(nèi)存即可瓷们,但是在動態(tài)鏈接下业栅,系統(tǒng)需要映射一個主程序和多個動態(tài)鏈接模塊,因此谬晕,相比于靜態(tài)鏈接碘裕,動態(tài)鏈接使得內(nèi)存的空間分布更加復雜。
不同模塊在內(nèi)存中的裝載位置一定要保證不一樣攒钳。
裝載時重定位
對于每一個模塊帮孔,對于代碼中符號的絕對引用,都需要加上一個基地址偏移量。(比如一個模塊中存在一個絕對地址的引用A文兢,它假定的是模塊可以被加載到0x1000的地方晤斩,但是系統(tǒng)在加載該位置已經(jīng)被其他模塊占用了,系統(tǒng)選擇將它加載到0x4000的地方姆坚,那么對絕對地址A的引用就需要被修改為A+0x3000澳泵,即修改代碼)。
但是它也有缺點兼呵,因為不同進程可能使用同一個模塊兔辅,但是對于不同進程,代碼段是不能共享的击喂,因此维苔,多個進程還是沒有辦法共享一個模塊。因為不能修改模塊當中的代碼懂昂,一旦一個進程將共享模塊的絕對地址修改了介时,其他進程此時調(diào)用就一定會報錯。
解決這個問題也有辦法忍法,在Windows中使用的就是裝載時重定位,但是在Linux下使用的是另外一種方式榕吼。
地址無關(guān)代碼
一個模塊的代碼部分是共享的饿序,但是數(shù)據(jù)部分是每個進程一個副本的。因此羹蚣,地址無關(guān)代碼的基本思想就是將代碼段中的絕對地址引用剝離出來放到數(shù)據(jù)段中原探,以保證代碼指令不變。在Android系統(tǒng)下進行SO文件的編譯顽素,默認就是產(chǎn)生地址無關(guān)代碼咽弦。
因此程序執(zhí)行的流程就變成了:當模塊A調(diào)用模塊B的某個方法的時候,會從模塊A的數(shù)據(jù)段部分找到模塊B中函數(shù)地址胁出,然后進行函數(shù)調(diào)用型型。
動態(tài)鏈接的優(yōu)點與缺點
優(yōu)點: 解決了靜態(tài)鏈接的缺陷,更適應現(xiàn)代的大規(guī)模的軟件開發(fā)
缺點:1. 結(jié)構(gòu)復雜 2.引入了安全問題全蝶,這也是我們能夠進行PLT HOOK的基礎
SO文件格式
ELF頭表
ELF頭表記錄了ELF文件的基本信息闹蒜,包括魔數(shù),目標文件類型(可執(zhí)行文件抑淫,共享庫文件或者目標文件)绷落,文件的目標體系結(jié)構(gòu),程序入口地址(共享庫文件為此值為0)始苇,然后是section表大小和數(shù)目砌烁,程序頭表的大小和數(shù)目,分別對應的是鏈接視圖和執(zhí)行視圖催式。
Section表
Section表記錄了每一個Section的基本信息函喉,名稱避归,類型,字節(jié)數(shù)函似,虛擬地址偏移和文件偏移槐脏。文件偏移指的是在ELF文件中,Section距離ELF文件起始位置的字節(jié)數(shù)撇寞,而虛擬地址偏移指的是當此section被加載到內(nèi)存中后顿天,該Section距離ELF起始位置的字節(jié)數(shù)。由于有些section只存在于文件中蔑担,而不會被系統(tǒng)加載到內(nèi)存中牌废,因此虛擬地址偏移可能為0.
程序頭表
程序頭表是裝載視圖下,系統(tǒng)進行segment解析的入口啤握,它給出了每一個segment的類型鸟缕,文件偏移,虛擬地址偏移和對齊等排抬。通過對程序頭表的遍歷懂从,我們可以得到ELF文件所有的segment。
重定位表
靜態(tài)鏈接下需要對符號的引用進行重定位蹲蒲,并且ELF文件中對符號的引用可能出現(xiàn)在代碼段番甩,也可能出現(xiàn)在數(shù)據(jù)段,因此重定位表分為了代碼段重定位表和數(shù)據(jù)段重定位表届搁,分別記錄引用的符號名和所在的偏移地址缘薛。因此,重定位表的格式就是記錄符號名和重定位地址的數(shù)組卡睦。
.dynamic段
.dynamic段是動態(tài)鏈接中最重要的段宴胧,它記錄了和動態(tài)鏈接有關(guān)的段的類型,地址或者數(shù)值表锻。指向了與動態(tài)鏈接相關(guān)的段
.got段
位于數(shù)據(jù)區(qū)恕齐,就是上文所述為了實現(xiàn)位置無關(guān)代碼將對絕對地址的引用抽離出來存放的集合
.plt段
在介紹.rel.plt段之前,對比靜態(tài)鏈接與動態(tài)鏈接我們需要知道.plt段的作用瞬逊。我們會發(fā)現(xiàn)檐迟,動態(tài)鏈接將所有的重定位操作延遲到加載時處理,那么就難以避免的會降低程序執(zhí)行的效率码耐,試想追迟,如果有1000個對外部模塊的函數(shù)引用,動態(tài)鏈接器就需要先解決這1000個函數(shù)引用骚腥,然后才開始執(zhí)行程序敦间。為此,鏈接器為了提升效率,采取了這樣一種策略:僅當函數(shù)被調(diào)用時廓块,才會喚醒動態(tài)鏈接器解決重定位問題厢绝。
.plt段就是為了實現(xiàn)這種策略增加的段。增加了.plt段之后带猴,代碼段中的地址就不再指向.got段而是指向了.plt段昔汉,再由.plt段指向.got段,具體過程如下:
.plt段是包含了若干數(shù)目的代碼片段組成的段拴清,代碼段中的地址指向?qū)瘮?shù)的.plt代碼段靶病,代碼片段的第一行代碼就是間接調(diào)用.got表中對應函數(shù)地址,但是此時口予,.got表中的地址指向的是.plt中代碼片段的第二行代碼娄周,而第二行以后的代碼作用就是調(diào)用動態(tài)鏈接器處理.got表中的地址。當再次調(diào)用此函數(shù)時沪停,.plt中代碼段第一行代碼就可以正確的跳入函數(shù)地址執(zhí)行相應的函數(shù)了煤辨。
但是,在Android平臺下木张,由于兼容性的限制众辨,并沒有采用這種"延遲加載"的特性,所以一開始加載舷礼,.got表中的地址就是真實的函數(shù)調(diào)用地址鹃彻。但是,Android下的ELF文件仍然保留了.plt表這種結(jié)構(gòu)且轨。
根據(jù)SO文件結(jié)構(gòu)進行靜態(tài)反編譯
靜態(tài)反編譯是指對未載入內(nèi)存控件的SO文件進行的反編譯浮声,在這種情況下虚婿,主要是針對SO文件的鏈接視圖進行關(guān)鍵參數(shù)上的修改旋奢,使得靜態(tài)反編譯工具不能夠正常的工作。
根據(jù)SO文件的格式然痊,在ELF頭表中會表明Section表的位置和Section表格的大小至朗。絕大部分靜態(tài)反編譯工具是依據(jù)SO文件的鏈接視圖還原出SO文件內(nèi)容的,那么在這種情況下剧浸,我們只需要修改ELF頭表中與SECTION表相關(guān)的2個參數(shù)锹引,就能使得工具反編譯SO文件失效。但是SO文件在系統(tǒng)載入內(nèi)存之后唆香,是使用的執(zhí)行視圖對SO文件進行解析嫌变,因此不會影響SO文件運行時。