国产日韩欧美一区二区三区三州_亚洲少妇熟女av_久久久久亚洲av国产精品_波多野结衣网站一区二区_亚洲欧美色片在线91_国产亚洲精品精品国产优播av_日本一区二区三区波多野结衣 _久久国产av不卡

?

基于全局偏移表進行通用動態(tài)鏈接庫函數(shù)跟蹤的方法

2020-06-11 13:46:44張木梁王國慶張磊
電子技術與軟件工程 2020年3期
關鍵詞:調(diào)用寄存器本發(fā)明

張木梁 王國慶 張磊

(1.武漢深之度科技有限公司技術部 北京市 100080)

(2武漢深之度科技有限公司研發(fā)中心 北京市 100080)

1 簡介

為了能夠向用戶提供一個通用的、高性能的方法進行動態(tài)鏈接庫函數(shù)跟蹤。本文引入了“一種基于全局偏移表進行通用動態(tài)鏈接庫函數(shù)跟蹤的方法”技術。

本發(fā)明技術受到基于PRELOAD的同名庫函數(shù)跟蹤與基于ptrace的斷點式程序調(diào)試跟蹤的啟發(fā),通過新的方法對進程執(zhí)行過程中GOT表的修改與掛鉤,能提供高性能的、通用的動態(tài)鏈接庫函數(shù)的跟蹤分析。

2 動態(tài)鏈接庫函數(shù)跟蹤設計

2.1 簡要分析

Linux操作系統(tǒng)下的可執(zhí)行程序普遍使用ELF格式,為了提高軟件的模塊化、運行性能與可維護性,減少軟件啟動時間與整體系統(tǒng)的內(nèi)存占用,動態(tài)鏈接的技術被廣泛使用。動態(tài)鏈接指的是軟件運行所依賴的功能被存儲在軟件以外的動態(tài)鏈接庫中,動態(tài)鏈接庫暴露出字符串形式的函數(shù)接口,在運行期間,軟件、動態(tài)鏈接器與動態(tài)鏈接庫都被載入到軟件的進程空間中。軟件在調(diào)用某個動態(tài)鏈接庫中的某個函數(shù)前,動態(tài)鏈接器會根據(jù)此函數(shù)的字符串名稱解析得到動態(tài)鏈接庫中函數(shù)的具體位置,并跳轉到此函數(shù)的位置處執(zhí)行代碼,從而開始函數(shù)調(diào)用。由于此函數(shù)名解析定位以及調(diào)用的整個過程是在軟件運行期間發(fā)生的,而不是在軟件編譯鏈接生成可執(zhí)行程序的過程中發(fā)生的,因此此項技術被稱為動態(tài)鏈接。

更具體的,在ELF文件中存在多個節(jié)(section),與動態(tài)鏈接密切相關的節(jié)包括全局偏移表(GOT,Global Offset Table)、過程鏈接表(PLT,Procedure Linkage Table)等。程序調(diào)用庫函數(shù)時實際上會調(diào)用PLT中的代碼,PLT中的代碼會跳轉到GOT表中存儲的地址處,而GOT表中的地址應該是程序所需要調(diào)用的外部動態(tài)鏈接庫函數(shù)的地址,由于在靜態(tài)鏈接時相應的地址是未知的,因此其初始值實際上此地址填寫的是指向PLT中的另一段代碼,這段代碼配合動態(tài)鏈接器負責在程序運行時解析對應的動態(tài)鏈接庫函數(shù)地址,并最終修改GOT表中相應的地址為此地址,然后再跳轉到此地址去執(zhí)行庫函數(shù)。這樣,當以后程序再次調(diào)用此庫函數(shù)時,即可直接跳轉到庫函數(shù)處執(zhí)行代碼,而不用再進行解析查找了。

一般GOT節(jié)的名稱為.got.plt,其它動態(tài)鏈接相關的節(jié)還包括.dynsym節(jié)(動態(tài)符號),里面包含了動態(tài)鏈接所需要的符號信息,.dynstr節(jié)(動態(tài)鏈接符號所對應的字符串),.rela.plt節(jié)(重定位表節(jié),里面包含了每個動態(tài)鏈接項在動態(tài)符號表中的下標、鏈接項的虛擬內(nèi)存地址等信息)等。

本發(fā)明基于ELF文件與進程中GOT表的修改與掛鉤的設計與實現(xiàn),以及動態(tài)生成跳轉代碼表(trampoline)的技術方案的設計與實現(xiàn)。

跟蹤庫函數(shù)主要的技術包括兩種,分別是基于PRELOAD的同名庫函數(shù)跟蹤與基于ptrace的斷點式程序調(diào)試跟蹤。

總的來說,本發(fā)明設計思路通用,性能高主要是因為:

(1)相比于基于PRELOAD的同名庫函數(shù)跟蹤的方法,針對每個待跟蹤的函數(shù),都需要進行手工函數(shù)聲明,無法以通用的方式對未知的動態(tài)鏈接庫函數(shù)進行跟蹤的問題進行改進。

(2)相比較于基于ptrace的斷點式程序調(diào)試跟蹤的方法,在Linux操作系統(tǒng)下非常復雜的系統(tǒng)調(diào)用,牽涉到進程間通信、信號處理、進程狀態(tài)查看與修改等,因此在國產(chǎn)處理器上的實現(xiàn)往往不完整或者有缺陷,會導致功能缺失或者性能下降的問題提高了性能。

2.2 與本發(fā)明相關的現(xiàn)有技術一

2.2.1 現(xiàn)有技術一的技術方案

在現(xiàn)有環(huán)境下,跟蹤庫函數(shù)主要的技術包括兩種,分別是基于PRELOAD的同名庫函數(shù)跟蹤與基于ptrace的斷點式程序調(diào)試跟蹤。

為了調(diào)整程序運行時的行為,動態(tài)鏈接器提供了PRELOAD機制,系統(tǒng)管理員或者用戶可以通過修改/etc/ld.so.preload文件或者設置LD_PRELOAD環(huán)境變量,以告之動態(tài)鏈接器,在軟件啟動之后首先載入上述文件或環(huán)境變量指定的動態(tài)鏈接庫L。如果希望跟蹤程序對動態(tài)鏈接庫函數(shù)(如func1)的調(diào)用,開發(fā)人員可以在此動態(tài)鏈接庫L中聲明一個外部可調(diào)用的,與func1完全一樣的函數(shù)簽名(function signature)即可。這樣,在程序調(diào)用func1函數(shù)時,實際上將調(diào)用L中的func1函數(shù),而不是原來的func1函數(shù)。通過此方法,即可對感興趣的動態(tài)鏈接庫函數(shù)進行跟蹤。

這種技術經(jīng)常被用在對特定程序的跟蹤上,而沒有通用工具可以支持。

2.2.2 現(xiàn)有技術一的缺點

使用PRELOAD方法進行動態(tài)鏈接庫函數(shù)的跟蹤無法解決通用性問題。

從上述PRELOAD的技術方案可以看出,如果需要跟蹤某個庫函數(shù),例如glibc中寫入文件數(shù)據(jù)的函數(shù)write,則需要首先開發(fā)一個動態(tài)鏈接庫,并在其中聲明與write完全一樣的函數(shù)簽名(ssize_t write(int fd,const void *buf,size_t count)),然后再在自行開發(fā)的write函數(shù)內(nèi)部實現(xiàn)函數(shù)的跟蹤處理,并最終調(diào)用glibc實現(xiàn)的write函數(shù)(不然程序的邏輯顯然會出現(xiàn)問題)。

因此,針對每個待跟蹤的函數(shù),都需要進行手工函數(shù)聲明,無法以通用的方式對未知的動態(tài)鏈接庫函數(shù)進行跟蹤,而在實際情況中,我們常常需要對一系列的庫函數(shù)進行分析,這樣帶來的開發(fā)量與維護量就會非常巨大。

這種缺陷是上述技術方案本身所無法避免的。因為此方案實際上是利用了動態(tài)鏈接器在解析庫函數(shù)的時候,同名函數(shù)在更先載入的動態(tài)鏈接庫中被首先解析到的原理,因此為了使用PRELOAD技術跟蹤庫函數(shù),就需要一一實現(xiàn)同樣名稱的庫函數(shù),并編譯生成待PRELOAD的動態(tài)鏈接庫。

2.3 與本發(fā)明相關的現(xiàn)有技術二

2.3.1 現(xiàn)有技術二的技術方案

Linux內(nèi)核提供了ptrace系統(tǒng)調(diào)用,第三方程序P可以通過ptrace調(diào)試應用軟件S,包括暫停S,查看S的狀態(tài)(內(nèi)存與寄存器等數(shù)據(jù)),修改S的狀態(tài),恢復S的運行等。

如果P希望跟蹤S的動態(tài)鏈接庫函數(shù)調(diào)用,P可以在調(diào)用ptrace的時候使用PTRACE_POKETEXT參數(shù),修改S的PLT中的代碼,加入軟中斷指令(在x86下為int 3),這樣在PLT解析動態(tài)鏈接庫函數(shù)之后會觸發(fā)這條軟中斷,從而使得內(nèi)核開始處理來自于S的中斷,并通知S的調(diào)試進程P,而P在收到了通知以后就可以查看S的進程狀態(tài),得知S將要調(diào)用的動態(tài)鏈接庫函數(shù),從而能對其進行跟蹤了。在跟蹤處理完畢后,P還需要再次調(diào)用ptrace,傳入PTRACE_POKETEXT參數(shù),刪除修改后的軟中斷指令,恢復原有指令,以便程序繼續(xù)正常運行。

這種技術在通用動態(tài)鏈接庫跟蹤軟件ltrace被使用到了。2.3.2 現(xiàn)有技術二的缺點

基于ptrace的斷點式程序調(diào)試跟蹤有三個問題,第一個問題是它在程序運行過程中需要不斷地中斷程序的運行,以修改程序的指令(加入軟中斷指令或者刪除軟中斷指令),并且中間還牽涉到內(nèi)核態(tài)到用戶態(tài)的狀態(tài)轉換,會帶來相當大的性能開銷。第二個問題是ptrace系統(tǒng)調(diào)用是Linux操作系統(tǒng)下非常復雜的系統(tǒng)調(diào)用,牽涉到進程間通信、信號處理、進程狀態(tài)查看與修改等,因此在國產(chǎn)處理器上的實現(xiàn)往往不完整或者有缺陷,會導致功能缺失或者性能下降。第三個問題同樣也是因為ptrace接口特別復雜,而且又牽涉到PLT表的修改,因此應用開發(fā)與調(diào)試跟蹤的人員很難使用此項技術定制自己的動態(tài)鏈接庫函數(shù)跟蹤,最常用的仍然是更為簡單,但是無法通用化的基于PRELOAD的同名函數(shù)替代跟蹤技術方案。

3 本發(fā)明技術方案的詳細闡述

3.1 本發(fā)明所要解決的技術問題

本發(fā)明解決了Linux操作系統(tǒng)下動態(tài)鏈接庫函數(shù)跟蹤的下列技術問題:缺乏通用的、高性能的方法進行動態(tài)鏈接庫函數(shù)跟蹤。

3.2 本發(fā)明提供的完整技術方案

在本發(fā)明中,庫函數(shù)跟蹤的模塊形式一般為動態(tài)鏈接庫,此動態(tài)鏈接庫通過修改/etc/ld.so.preload或者LD_PRELOAD環(huán)境變量的方式,在程序主體以及動態(tài)鏈接庫被內(nèi)核載入進程空間后,即被動態(tài)鏈接庫載入進程空間。被載入之后,即采取下列措施(下面匯編代碼示例均采用x86匯編,實際可以外推到其它處理器的指令集):

(1)聲明并實現(xiàn)一個函數(shù)trace_fn作為實際的動態(tài)鏈接庫函數(shù)跟蹤函數(shù),可以被用戶靈活定制,例如打印出當前時間戳與將要被調(diào)用的動態(tài)鏈接庫函數(shù)的名稱。它接受一個整數(shù)類型(int)的參數(shù),是庫函數(shù)的下標,返回長整數(shù)類型的值,應該是對應庫函數(shù)的地址。

(2)聲明并實現(xiàn)一個函數(shù)fn_hook作為跳板函數(shù)。此函數(shù)為匯編語言編寫,它首先彈出(pop)棧頂數(shù)據(jù)至rax寄存器中,再依次將rbx、rcx、rdx、rsi、rdi、r8、r9、r10等寄存器的值壓棧(push),繼而將rax的值復制到rdi寄存器中,接著調(diào)用(call)trace_fn,進而依次彈出棧頂數(shù)據(jù)因此至r10、r9、r8、rdi、rsi、rdx、rcx、rbx等寄存器,最后跳轉(jmp)到rax寄存器保存的地址處開始執(zhí)行。

圖1

(3)設置庫的初始化函數(shù)(constructor),動態(tài)鏈接器會首先載入PRELOAD庫,最后(與其它動態(tài)鏈接庫比較)運行PRELOAD的初始化函數(shù),因此庫初始化時其它庫的符號解析已經(jīng)完成了。

(4)在庫初始化時調(diào)用memalign函數(shù)分配一塊32KB大小,頁地址對齊頁邊界的內(nèi)存,對應全局變量trampoline。

(5)在上述內(nèi)存空間中,從0開始循環(huán)(循環(huán)變量為ndx),從前向后反復填充兩條匯編指令。第一條匯編指令為push ndx,即將當前的ndx值壓棧,第二條匯編指令為jmp fn_hook,即跳轉到fn_hook函數(shù)去,每次循環(huán)ndx加一。

(6)將上述內(nèi)存空間填滿指令后,記錄下總循環(huán)次數(shù)total,并調(diào)用mprotect將上述內(nèi)存空間的屬性修改為可讀可執(zhí)行,以使得上述代碼可以被執(zhí)行。

(7)聲明結構體struct lib_func {long addr; char* name;},并初始化長度為total的lib_func數(shù)組全局變量libfuncs為全零。

(8)讀取當前進程(即待跟蹤程序)的進程空間虛擬文件/proc/self/maps,獲得當前進程各個段(segment)的地址與范圍。

(9)由于進程的內(nèi)存內(nèi)容由對應程序ELF文件中各個段一一映射而來,而ELF文件中的段又由節(jié)組成,下面即可根據(jù)ELF的規(guī)范解析可以得到.got.plt、.rela.plt、.dynsym、.dynstr節(jié)在進程空間中的具體地址。

(10)接著對.rela.plt節(jié)中的每個重定位表項進行如下循環(huán),循環(huán)變量為i,從0開始:

1.調(diào)用ELF64_R_INFO宏通過重定位表項的r_info成員獲得動態(tài)鏈接符號表中對應的下標,并根據(jù)上述.dynsym節(jié)的地址得到相應的動態(tài)鏈接符號;

2.通過動態(tài)鏈接符號的st_name成員,以及上述.dynstr節(jié)的地址得到此庫函數(shù)的名稱fn_name;

3.通過重定位表項的r_offset成員獲得GOT表中的下標,并根據(jù)上述.got.plt節(jié)的地址得到GOT表中保存的對應庫函數(shù)的地址fn_addr;

4.設置libfuncs[i]的addr成員的值為fn_addr,設置name成員的值為fn_name(通過strdup復制一個值);

5.將GOT表中對應庫函數(shù)的地址從fn_addr修改為trampoline加上i乘以第5步中兩條匯編指令的長度(以字節(jié)為單位)。

(11)在程序運行時,若調(diào)用到了庫函數(shù),則由于庫函數(shù)的地址在GOT表中已經(jīng)被修改為了trampoline中相應的地址,因此對應的執(zhí)行流程會被修改為:

1.trampoline中相應的代碼會被執(zhí)行,即首先壓棧庫函數(shù)的下標,其次跳轉到fn_hook;

2.fn_hook從棧中獲得trampoline中壓入的庫函數(shù)下標,將其保存到rax寄存器中,繼而將各個寄存器的值保存到棧中,調(diào)用trace_fn,傳入的參數(shù)為rax的值(即庫函數(shù)的下標);

3.trace_fn根據(jù)傳入的庫函數(shù)下標在libfuncs中得到庫函數(shù)的名稱與真實地址,并進行相應的跟蹤分析處理,最后返回庫函數(shù)的真實地址;

4.fn_hook恢復各個寄存器的值,從rax得到trace_fn的返回值,并跳轉到庫函數(shù)的真實地址處開始執(zhí)行;

對應對被跟蹤進程進行修改的流程如圖1所示。

根據(jù)上述流程對被跟蹤進程進行修改后,被跟蹤程序在調(diào)用庫函數(shù)時的流程如圖2所示。

其中的虛線表示的是當沒有本專利的程序運行時,庫函數(shù)調(diào)用發(fā)生的路徑。

在上述步驟中,有幾個需要注意的技術點:

(1)第1步中的trace_fn函數(shù)中調(diào)用任何庫函數(shù)(如printf、malloc、strstr等)都需要要么查找libfuncs中的地址進行調(diào)用,要么自行實現(xiàn)。如果直接使用原函數(shù)調(diào)用,由于trace_fn是在庫函數(shù)被調(diào)用之前被調(diào)用的,則可能導致無限循環(huán)死鎖。

(2)第2步的fn_hook函數(shù)中需要將棧上的庫函數(shù)下標首先彈出到rax寄存器中,這是因為其它的寄存器,即rbx、rcx、…r10等在x86架構下都可能被函數(shù)調(diào)用用來保存調(diào)用參數(shù),因此不能破壞這些寄存器的內(nèi)容。但是由于調(diào)用trace_fn需要傳遞參數(shù),call指令調(diào)用C語言的函數(shù)缺省使用rdi寄存器保存第一個參數(shù)的值,而且在trace_fn運行的過程中可能會使用到不同的寄存器,因此需要將rdi等寄存器的值保存在棧上,再將rax寄存器的值(保存著庫函數(shù)的下標)復制給rdi寄存器,再調(diào)用trace_fn函數(shù)。而缺省情況下,函數(shù)的返回值(庫函數(shù)的真實地址)保存在rax寄存器中,因此需要最后跳轉到rax寄存器保存的值對應的地址處開始執(zhí)行。

(3)第3步中的時機很巧妙,PRELOAD庫是第一個被載入的動態(tài)鏈接庫,在所有的動態(tài)鏈接庫中,它的初始化函數(shù)也是最后一個被執(zhí)行的。在其初始化函數(shù)開始執(zhí)行的時候,主程序所依賴的動態(tài)鏈接庫函數(shù)應該已經(jīng)有一部分被解析填充了,因此可以獲得真實的庫函數(shù)地址。如果希望所有的庫函數(shù)在此階段都被解析填充,還可以設置環(huán)境變量LD_BIND_NOW進行完整的庫函數(shù)解析。

(4)在第4步中須使用memalign或者valloc函數(shù)進行trampoline內(nèi)存塊的內(nèi)存分配,不能使用常見的malloc或者mmap函數(shù)。這是因為malloc無法保證分配的內(nèi)存在內(nèi)存頁邊界上,但是第6步需要將此內(nèi)存通過mprotect函數(shù)設置為可執(zhí)行內(nèi)存,而mprotect要求內(nèi)存地址頁邊界對齊。而mmap分配的內(nèi)存距離程序的主體太遠,在64位操作系統(tǒng)上一般都會超出4G(即32位)的距離,從而導致jmp指令后面緊跟的32位地址溢出,memalign與valloc分配的內(nèi)存在堆(heap)中,距離程序主體最近,因此需要選擇這些函數(shù)。Linux下進程空間的內(nèi)存地址分布參見圖3。

(5)第10步中對庫函數(shù)的掛鉤處理能對所有的庫函數(shù)進行處理,也能根據(jù)要求僅跟蹤部分庫函數(shù),因此具有通用性,也具有可定制性。

圖2

圖3

3.3 本發(fā)明技術方案帶來的有益效果

本發(fā)明技術方案通過對進程執(zhí)行過程中GOT表的修改與掛鉤,能提供高性能的、通用的動態(tài)鏈接庫函數(shù)的跟蹤分析。此解決方案依賴性小,通用性強,運行性能高,可廣泛用于Linux操作系統(tǒng)下應用程序動態(tài)鏈接庫的跟蹤與分析中。

猜你喜歡
調(diào)用寄存器本發(fā)明
Lite寄存器模型的設計與實現(xiàn)
計算機應用(2020年5期)2020-06-07 07:06:44
核電項目物項調(diào)用管理的應用研究
LabWindows/CVI下基于ActiveX技術的Excel調(diào)用
測控技術(2018年5期)2018-12-09 09:04:46
分簇結構向量寄存器分配策略研究*
基于系統(tǒng)調(diào)用的惡意軟件檢測技術研究
一種鎂鹽制備高分散氫氧化鎂的方法
一種鎂鹽制備氫氧化鎂阻燃劑的方法
酵母硒或納米硒用于生產(chǎn)耐貯存富硒鵪鶉蛋的應用
家禽科學(2014年12期)2014-04-29 00:44:03
利用RFC技術實現(xiàn)SAP系統(tǒng)接口通信
一種電池級溴化鋅的制備方法
临沧市| 乐清市| 巴楚县| 河源市| 上蔡县| 尼木县| 丹巴县| 呼伦贝尔市| 柘城县| 武平县| 读书| 名山县| 高邮市| 通江县| 桐梓县| 南开区| 安吉县| 沐川县| 班玛县| 云林县| 达尔| 诸暨市| 修水县| 奉新县| 星子县| 临城县| 贵德县| 万山特区| 措勤县| 南充市| 麻城市| 丽水市| 宁强县| 英德市| 郎溪县| 天全县| 察雅县| 互助| 化德县| 安丘市| 西平县|