999精品在线视频,手机成人午夜在线视频,久久不卡国产精品无码,中日无码在线观看,成人av手机在线观看,日韩精品亚洲一区中文字幕,亚洲av无码人妻,四虎国产在线观看 ?

基于全局偏移表進行通用動態鏈接庫函數跟蹤的方法

2020-06-11 13:46:44張木梁王國慶張磊
電子技術與軟件工程 2020年3期
關鍵詞:進程

張木梁 王國慶 張磊

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

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

1 簡介

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

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

2 動態鏈接庫函數跟蹤設計

2.1 簡要分析

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

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

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

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

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

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

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

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

2.2 與本發明相關的現有技術一

2.2.1 現有技術一的技術方案

在現有環境下,跟蹤庫函數主要的技術包括兩種,分別是基于PRELOAD的同名庫函數跟蹤與基于ptrace的斷點式程序調試跟蹤。

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

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

2.2.2 現有技術一的缺點

使用PRELOAD方法進行動態鏈接庫函數的跟蹤無法解決通用性問題。

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

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

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

2.3 與本發明相關的現有技術二

2.3.1 現有技術二的技術方案

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

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

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

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

3 本發明技術方案的詳細闡述

3.1 本發明所要解決的技術問題

本發明解決了Linux操作系統下動態鏈接庫函數跟蹤的下列技術問題:缺乏通用的、高性能的方法進行動態鏈接庫函數跟蹤。

3.2 本發明提供的完整技術方案

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

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

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

圖1

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

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

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

(6)將上述內存空間填滿指令后,記錄下總循環次數total,并調用mprotect將上述內存空間的屬性修改為可讀可執行,以使得上述代碼可以被執行。

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

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

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

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

1.調用ELF64_R_INFO宏通過重定位表項的r_info成員獲得動態鏈接符號表中對應的下標,并根據上述.dynsym節的地址得到相應的動態鏈接符號;

2.通過動態鏈接符號的st_name成員,以及上述.dynstr節的地址得到此庫函數的名稱fn_name;

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

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

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

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

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

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

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

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

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

根據上述流程對被跟蹤進程進行修改后,被跟蹤程序在調用庫函數時的流程如圖2所示。

其中的虛線表示的是當沒有本專利的程序運行時,庫函數調用發生的路徑。

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

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

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

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

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

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

圖2

圖3

3.3 本發明技術方案帶來的有益效果

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

猜你喜歡
進程
債券市場對外開放的進程與展望
中國外匯(2019年20期)2019-11-25 09:54:58
改革開放進程中的國際收支統計
中國外匯(2019年8期)2019-07-13 06:01:06
快速殺掉頑固進程
社會進程中的新聞學探尋
民主與科學(2014年3期)2014-02-28 11:23:03
我國高等教育改革進程與反思
教育與職業(2014年7期)2014-01-21 02:35:04
Linux僵死進程的產生與避免
講效率 結束進程要批量
電腦迷(2012年24期)2012-04-29 00:44:03
男女平等進程中出現的新矛盾和新問題
俄羅斯現代化進程的阻礙
論文萊的民族獨立進程
主站蜘蛛池模板: 国产毛片网站| 亚洲欧美另类日本| 久久精品国产精品青草app| 免费观看成人久久网免费观看| 国产美女精品人人做人人爽| 91小视频在线播放| 精品国产三级在线观看| 91av成人日本不卡三区| 国产91在线免费视频| 亚洲专区一区二区在线观看| 99草精品视频| 真实国产精品vr专区| 在线一级毛片| 欧美精品成人| 亚洲av无码成人专区| 一区二区三区成人| 亚洲人成高清| 日韩在线2020专区| 亚洲无限乱码| 99精品福利视频| 国产成人无码久久久久毛片| 日韩欧美国产另类| 亚洲AV无码久久天堂| 亚洲男人的天堂在线观看| 精品福利网| 1024国产在线| 亚洲精品成人福利在线电影| 精品自窥自偷在线看| 日本成人不卡视频| 狠狠ⅴ日韩v欧美v天堂| 波多野结衣一二三| 97人妻精品专区久久久久| 中文字幕亚洲精品2页| 国产自在线拍| 精品伊人久久大香线蕉网站| 亚洲午夜国产片在线观看| 国产成人a在线观看视频| 91高清在线视频| 国产精品视频第一专区| 国产亚洲欧美日韩在线一区二区三区 | 国产99视频在线| 91精品视频播放| 99精品影院| 国产亚洲精品无码专| 91小视频在线观看免费版高清| 黄色网在线免费观看| 韩日无码在线不卡| 青青草国产在线视频| 国产不卡在线看| 一区二区三区高清视频国产女人| 免费看美女毛片| 无码AV高清毛片中国一级毛片| yjizz国产在线视频网| 久青草国产高清在线视频| 国产精品19p| 国产精品私拍在线爆乳| 精品丝袜美腿国产一区| 亚洲国产看片基地久久1024| 亚洲国产精品不卡在线| 欧美亚洲国产视频| 亚洲免费黄色网| 精品国产香蕉在线播出| 欧美一区二区精品久久久| 国产亚洲精品97AA片在线播放| 色婷婷国产精品视频| 91网址在线播放| 久久人搡人人玩人妻精品一| 精品91在线| 自偷自拍三级全三级视频 | 久久精品无码专区免费| 国产亚洲第一页| www亚洲精品| 久久精品无码国产一区二区三区 | 久久国产精品夜色| 免费Aⅴ片在线观看蜜芽Tⅴ| 日韩免费中文字幕| 婷婷六月综合网| 国产欧美视频在线观看| 国产无码制服丝袜| 久久精品一品道久久精品| 无码一区18禁| 国产日本视频91|