張婉瑩,曹曉梅,陳 偉
(南京郵電大學 計算機學院,南京 210023)
近年來,綜合了模糊測試和Concolic執(zhí)行的白盒模糊測試技術引起研究人員的廣泛關注,其是一種漏洞檢測技術,可以自動生成測試用例來盡量覆蓋程序中的可執(zhí)行路徑,進而發(fā)現(xiàn)程序內(nèi)的漏洞[1]。研究人員基于白盒模糊測試技術設計了許多漏洞檢測工具,如KLEE[2]、SAGE[3]、Angr[4]和Driller[5]等。實驗結果表明,這些工具能夠有效提高漏洞檢測的效率。
從理論上而言,白盒模糊測試可以實現(xiàn)對目標程序中所有可執(zhí)行路徑的覆蓋,但在實際的漏洞檢測過程中,無法達到100%的路徑覆蓋率,這主要是由于白盒模糊測試無法提供被測程序運行所需的實際環(huán)境以及約束求解器難以求解復雜約束等問題。目前,大量應用軟件與環(huán)境密切相關,即在運行過程中需要與操作系統(tǒng)、網(wǎng)絡以及設備之間進行頻繁的交互,從而導致白盒模糊測試的漏洞檢測結果不僅取決于用戶輸入,還依賴于其運行的環(huán)境[6]。如果在白盒模糊測試中不能提供某些路徑執(zhí)行所需要的環(huán)境,則與之相關的路徑可能無法被探索到,從而導致某些漏洞被遺漏,降低了檢測結果的準確性,這就是環(huán)境交互問題。例如,CH4[7]等著名病毒都是在指定的時間發(fā)作,即只有滿足了漏洞觸發(fā)的時間條件,才能檢測到該漏洞,而這對于白盒模糊測試而言是一個難度較高的挑戰(zhàn)。
針對環(huán)境交互問題,比較成熟的解決方案是斯坦福大學CADAR等人開發(fā)的KLEE和瑞士洛桑聯(lián)邦理工學院CHIPOUNOV等人開發(fā)的S2E[8]。在漏洞檢測之前,KLEE先對被測程序中調用的庫函數(shù)進行建模,在對程序進行漏洞檢測的過程中,如果遇到調用庫函數(shù)的路徑,則將其重定向到對應的模型,獲得所有合法的輸入值,從而驅動相關路徑的執(zhí)行[9]。由于建模需要耗費大量的人力和物力,因此KLEE只對庫函數(shù)進行建模,當遇到其他類型的外部函數(shù)時,無法驅動路徑的執(zhí)行,因此,其路徑覆蓋率偏低。S2E是基于虛擬機插樁的漏洞檢測工具,被檢測的目標程序和整個運行環(huán)境都運行在S2E上。S2E采用2種漏洞檢測模式,即SC-UE(Strictly Consistent Unit-level Execution)模式和SC-SE(Strictly Consistent System-level Execution)[10]模式。如果在漏洞檢測的過程中采用SC-UE模式,則需要與外部環(huán)境進行交互的函數(shù)不會被插樁,與之相關的路徑將無法被執(zhí)行,導致路徑覆蓋率偏低,部分漏洞可能被遺漏。如果采用SC-SE模式,需要與外部環(huán)境進行交互的函數(shù)將會被插樁,并且可以根據(jù)真實的運行環(huán)境獲得正確的測試用例,以驅動相關路徑的執(zhí)行,獲得較高的路徑覆蓋率并檢測到更多的漏洞,但是這會帶來比較嚴重的路徑爆炸問題。
近年來,國內(nèi)外學者針對上述問題進行了較多研究。文獻[11]提出了一種基于抽象內(nèi)存的針對外部函數(shù)調用的建模方法,該方法對每個外部函數(shù)建立抽象內(nèi)存模型,以保存其路徑約束條件并模擬外部函數(shù)的語義信息,從而獲取相應的測試用例,以保證待測程序能夠按照目標路徑執(zhí)行,最終提高路徑覆蓋率。但是,該技術難以對需要精確理解上下文的外部函數(shù)調用進行建模。
本文提出一種基于外部函數(shù)探測和校正的隱藏路徑搜索(Hidden Path Search Based on External Function,HPSBEF)方案。該方案利用約束求解和最弱前置條件推理獲得外部函數(shù)的輸出值,并將其記錄在已經(jīng)建立好的鏈表中,在執(zhí)行新路徑的過程中根據(jù)鏈表信息對外部函數(shù)的輸出進行動態(tài)修正,以驅動新路徑的執(zhí)行并提高路徑覆蓋率。為驗證HPSBEF方案的有效性和執(zhí)行效率,將該方案與KLEE、S2E以及文獻[11]提出的FMM方案作對比,從路徑覆蓋率、漏洞檢測能力和時間開銷3個方面進行分析。
HPSBEF方案在白盒模糊測試的基礎上實現(xiàn)了外部函數(shù)探測與校正,其框架如圖1所示,主要由外部函數(shù)預處理器、外部函數(shù)分析器和外部函數(shù)更新器三部分組成。
圖1 HPSBEF方案框架
外部函數(shù)預處理器針對每個被檢測的目標程序,創(chuàng)建與外部函數(shù)相關的鏈表結構,以記錄目標程序中所有調用的外部函數(shù)及其參數(shù)信息,包括位置信息、輸出參數(shù)類型和長度等。預處理器實現(xiàn)了外部函數(shù)的預處理,是檢測外部函數(shù)的前提和基礎。外部函數(shù)分析器檢測函數(shù)調用指令,并與外部函數(shù)預處理器建立的外部函數(shù)鏈表進行匹配,若發(fā)現(xiàn)外部函數(shù),則根據(jù)鏈表中記錄的詳細參數(shù)信息進行外部函數(shù)輸出值的修正,以達到訪問隱藏路徑的目的。分析器實現(xiàn)外部函數(shù)的探測、標記與校正,是檢測外部函數(shù)的關鍵階段。外部函數(shù)更新器負責更新鏈表中的信息,即外部函數(shù)的參數(shù)在下次路徑執(zhí)行時是否需要修正以及修正為何值。白盒模糊測試在每次路徑執(zhí)行結束后,都會按照一定的規(guī)則對約束條件進行取反,并由約束求解器求解產(chǎn)生新的測試用例[12],外部函數(shù)更新器將新的測試用例中與外部函數(shù)相關的信息更新到已有的鏈表中,以作為下次路徑執(zhí)行時外部函數(shù)進行修正的參考。
1.2.1 外部函數(shù)預處理
外部函數(shù)預處理器的主要作用是外部函數(shù)及其參數(shù)信息的收集與整理,這是進行后續(xù)探測與修正的前提和基礎。如果在預處理階段有外部函數(shù)被遺漏,或者其參數(shù)信息的收集存在誤差,則某些隱藏路徑將不能被訪問,從而導致某些漏洞被遺漏。
外部函數(shù)預處理器的執(zhí)行主要包含2個步驟:針對每個被檢測的目標程序建立外部函數(shù)鏈表結構,包含函數(shù)地址、函數(shù)名、輸出參數(shù)個數(shù)、類型長度、校驗標識以及校驗值,如圖2所示;找出目標程序中包含的所有外部函數(shù)及其輸出屬性的參數(shù)信息,并記錄在鏈表中。
AddressFunction NameParameter NameParameter SizeFlagReplacement
圖2 外部函數(shù)鏈表結構
Fig.2 Structure of external function chain
本文以Windows系統(tǒng)下的二進制程序為例對外部函數(shù)的預處理過程進行說明。Windows SDK中對于每個API函數(shù)參數(shù)的輸入輸出特性都有明確的定義,一般包括5種情況:[in],[out],[in,out],[in,optional],[out,optional]。對于可執(zhí)行PE文件而言,所有調用的外部函數(shù)信息都存儲在輸入地址表(Import Address Table,IAT)中[13],多數(shù)Windows下的常見外部函數(shù)都由Kernel32.dll導出,每一個從dll中被引入的外部函數(shù)在PE文件的IAT中都有相應的位置。PE文件在裝載過程中先搜索數(shù)據(jù)結構IMAGE_IMPORT_DESCRIPTOR中的OriginalFristThunk,若存在,則迭代搜索輸入名稱表(Import Name Table,INT)中的每個指針,這些指針指向了IMAGE_IMPORT_BY_NAME中的每個被載入函數(shù)的地址,然后裝載器將值填充到FristThunk指向的IAT表中,整個過程如圖3所示。因此,在PE文件被系統(tǒng)加載之后,提取外部函數(shù)及其在IAT表中的地址并且參照SDK中關于函數(shù)參數(shù)輸入輸出特性的定義,便可建立相應的外部函數(shù)鏈表結構。
圖3 PE文件加載后的IAT表
1.2.2 外部函數(shù)的探測、標記與校正
執(zhí)行外部函數(shù)分析器是檢測外部函數(shù)的關鍵階段,其主要作用是在漏洞檢測的過程中根據(jù)外部函數(shù)的鏈表結構對路徑中是否存在外部函數(shù)進行探測,若存在外部函數(shù),則對其輸出參數(shù)所在的內(nèi)存區(qū)域進行標記,并根據(jù)外部函數(shù)鏈表中的校驗標識以及校驗值選擇性地更改輸出參數(shù)的具體數(shù)值,以驅動隱藏路徑的執(zhí)行。
首先,外部函數(shù)分析器探測路徑中是否存在外部函數(shù)。Intel手冊定義了x86平臺下的3種調用指令[14],分別對應機器碼E8、9A和FF。在程序執(zhí)行過程中,外部函數(shù)分析器獲取指令指針寄存器EIP中存儲的下一指令的機器碼,通過與E8、9A和FF 3種調用指令機器碼進行比較,判斷是否為函數(shù)調用指令。如果下一條指令為函數(shù)調用指令,則進一步獲取調用函數(shù)中的地址,并通過與外部函數(shù)鏈表中記載的地址進行比較,判斷是否為外部函數(shù),如果是外部函數(shù),則進行下一步,否則程序繼續(xù)執(zhí)行。
其次,在確定了外部函數(shù)之后,外部函數(shù)分析器即對其輸出參數(shù)所在內(nèi)存區(qū)域進行標記。由于Windows系統(tǒng)常采用cdecl和stdcall 2種調用約定,因此參數(shù)按照從右往左的順序進行傳遞[15],所以最后一個入棧的參數(shù)就是函數(shù)的第一個參數(shù)。如果指令指針寄存器EIP指向的call指令調用的函數(shù)被判定為外部函數(shù),則根據(jù)當前的指令地址可以獲得該外部函數(shù)每個參數(shù)的入棧地址,然后根據(jù)外部函數(shù)鏈表中存儲的該外部函數(shù)的輸出參數(shù)序號和參數(shù)類型大小這2個參數(shù),逆序推導出該函數(shù)中所有輸出參數(shù)所在的內(nèi)存區(qū)域。
最后,當外部函數(shù)輸出參數(shù)被標記之后,還需要根據(jù)鏈表中存儲的校驗標識Flag判定該外部函數(shù)的輸出是否需要被更改,如果需要更改,則將其更改為Replacement中存儲的數(shù)據(jù)。如果該外部函數(shù)的輸出參數(shù)是結構體變量,則輸出參數(shù)值修正需要具體到結構體中的每一個成員變量。
本文外部函數(shù)探測、標記與校正的過程如算法1所示。語句1將指令指針寄存器EIP指向的指令機器碼轉換成匯編代碼,語句2判斷當前指令是否為函數(shù)調用call指令,如果不是call指令,調用語句13,將控制權交回目標程序,正常執(zhí)行該指令;反之,則調用語句3查詢外部函數(shù)鏈表,并調用語句4判定該函數(shù)是否為外部函數(shù)。如果鏈表中該外部函數(shù)存在,則調用語句5循環(huán)查找其輸出參數(shù),直到所有輸出參數(shù)查詢完畢,即IEF→PN=end時查詢結束,其中,PN代表輸出參數(shù)的名稱。然后,算法利用語句6計算輸出參數(shù)的內(nèi)存區(qū)域,可由函數(shù)參數(shù)的入棧地址和參數(shù)大小相加得出,即APN+IEF→PS,其中,APN代表參數(shù)的入棧地址,PS代表參數(shù)的大小。算法在獲得參數(shù)的內(nèi)存區(qū)域后則利用語句7將語句6的計算結果進行標記;反之,則調用語句9,將控制權交回目標程序,執(zhí)行函數(shù)調用。語句10根據(jù)鏈表中記載的該外部函數(shù)的校驗標識,判斷其輸出值是否需要校正,如果需要,則調用語句11將輸出參數(shù)值修改為鏈表中存儲的數(shù)據(jù),即ModifyOutput(Memory,IEF→Replacement)。
算法1外部函數(shù)探測、標記與校正算法
輸入MEIP
輸出校正后的外部函數(shù)
MEIP:machine code recorded in the register EIP
1.Iop←HextoAsm(MEIP)
2.if Iop∈Icallthen
3.IEF←Looktable(Iop,TEF);
4.if IEF≠? then
5.while (IEF→PN≠end) do
6.Memory←APN+IEF→PS;
7.MarkOutput(Memory);
8.else
9.Execue(Iop);
10.if IEF≠?∩IEF→Flag==1 then
11.ModifyOutput(Memory,IEF→Replacement);
12.end if
13.Execue(Iop);
14.end if
由上述過程可知,該算法的時間復雜度由外部函數(shù)及其輸出參數(shù)的數(shù)量決定,因此,若外部函數(shù)的數(shù)量為nEF,每個外部函數(shù)的輸出參數(shù)數(shù)量最大為k,則該算法在最壞情況下的時間復雜度可表示為O(knEF)。算法的空間復雜度由存儲外部函數(shù)的鏈表結構決定,可表示為O(6knEF)。
1.2.3 外部函數(shù)的更新
在白盒模糊測試中每條路徑執(zhí)行完成之后,會按照一定的路徑搜索策略對約束條件取反,并由約束求解器翻譯成最弱前置條件[16]進行求解,產(chǎn)生新的輸入用例集合,以在下次漏洞檢測時驅動新的路徑執(zhí)行。因此,外部函數(shù)更新器需要根據(jù)約束求解器的求解結果更新鏈表中的信息,以便在下次程序執(zhí)行時作為外部函數(shù)校正的參考和依據(jù)。
由于實驗部分是在KLEE、S2E的基礎上實現(xiàn)本文方案,因此路徑搜索策略采用KLEE、S2E提供的方法。KLEE和S2E均提供4種搜索方法:深度優(yōu)先搜索,隨機狀態(tài)搜索,隨機路徑搜索以及NURS搜索,搜索方法的選擇取決于參數(shù)的設定[17],本文采用深度優(yōu)先搜索策略。
外部函數(shù)更新器需要將新生成的輸入集合中與外部函數(shù)相關的信息更新到相應的鏈表項中,即該外部函數(shù)在下次執(zhí)行時,輸出參數(shù)值是否需要校正以及需要校正的具體數(shù)值為多少。
為驗證HPSBEF方案的實際效果并降低路徑搜索策略與約束求解能力等因素對實驗結果的影響,本文分別在KLEE和S2E的基礎上實現(xiàn)HPSBEF方案以及文獻[11]提出的FMM方案,即在處理外部函數(shù)調用時,將KLEE、S2E原有的方案替換為HPSBEF以及FMM方案,其余都不變。其中,HPSBEF方案以H-KLEE和H-S2E作為標識,FMM方案以M-KLEE和M-S2E作為標識。
實驗選用開源軟件庫[18]中包含較多外部函數(shù)的10個應用程序作為漏洞檢測對象,程序的名稱、路徑數(shù)量和外部函數(shù)數(shù)量如表1所示。本次實驗分別使用H-KLEE、M-KLEE、KLEE、H-S2E、M-S2E和S2E這6種工具對表2中的被測程序進行檢測,并從路徑覆蓋率、漏洞發(fā)現(xiàn)能力以及時間開銷3個方面進行對比分析。
表1 目標程序的路徑數(shù)量與外部函數(shù)數(shù)量
Table 1 Number of target program paths and external functions
程序名稱路徑數(shù)量外部函數(shù)數(shù)量doublecmd882123metafile2eps70184processcecker1 125168pdftotext72790miniweb53568ipsacn974111kkplayer1 385104hardseed81572mpush1 09688vnote78693
實驗分別使用6種工具對被測程序進行檢測,統(tǒng)計各自的路徑覆蓋率,對比結果如圖4所示。
圖4 路徑覆蓋率結果對比
由圖4可以看出,H-KLEE和M-KLEE、KLEE以及H-S2E和M-S2E、S2E相比,路徑覆蓋率均有所提升。其中,H-KLEE比M-KLEE平均增加了4.2%,H-KLEE比KLEE平均增加了8.1%,H-S2E比M-S2E平均增加了5.3%,H-S2E比S2E平均增加了5.9%,結果表明,本文方案可以探測到更多路徑。此外,實驗過程中的路徑覆蓋率沒有達到白盒模糊測試在理論上的100%覆蓋率,分析發(fā)現(xiàn)主要原因如下:約束求解器不支持浮點類型和指針運算[19],不能理解某些包含浮點類型或指針運算的復雜函數(shù)的運算規(guī)則,在實驗過程中,當STP求解耗盡內(nèi)存或者求解時間超過5 min時,將主動終止求解過程,這也是眾多測試過程中部分路徑無法訪問的主要原因之一。
實驗過程中對6種工具所檢測出的漏洞數(shù)量進行統(tǒng)計,結果如表2、表3 所示。
表2 H-KLEE、M-KLEE和KLEE檢測出的漏洞數(shù)量
Table 2 Number of vulnerabilities detected by H-KLEE,M-KLEE and KLEE
被測程序H-KLEEM-KLEEKLEEdoublecmd212019metafile2eps111010processcecker151413pdftotext191716miniweb131211ipsacn1098kkplayer121010hardseed987mpush1098vnote887
表3 H-S2E、M-S2E和S2E檢測出的漏洞數(shù)量
Table 3 Number of vulnerabilities detected by H-S2E,M-S2E and S2E
被測程序H-S2EM-S2ES2Edoublecmd212020metafile2eps111010processcecker151314pdftotext191816miniweb131313ipsacn1099kkplayer121111hardseed987mpush1099vnote888
由表2可知,H-KLEE和M-KLEE、KLEE相比,檢測出的漏洞數(shù)量有所增加,其中,H-KLEE比M-KLEE平均增加了9.4%,比KLEE平均增加了17.4%;由表3可知,H-S2E與M-S2E 、S2E相比,漏洞數(shù)量也有所增加,H-S2E比M-S2E平均增加了7.6%,比S2E平均增加了16.4%。因此,本文方案的漏洞檢測能力較高。
實驗過程中對6種工具的平均漏洞檢測消耗時間進行統(tǒng)計,結果如圖5所示。其中,H-KLEE的平均時間為45 028 s,M-KLEE的平均時間為45 343 s,KLEE的平均時間為45 811 s,H-KLEE所用時間最少;H-S2E的平均時間為45 078 s,M-S2E的平均時間為45 879 s,S2E的平均時間為46 526 s,H-S2E所用時間最少。因此,本文方案的時間開銷明顯降低。
圖5 各方案平均消耗時間對比
Fig.5 Comparison of average consumption time of each scheme
本文針對白盒模糊測試中環(huán)境交互問題引起的路徑覆蓋率偏低、某些潛在漏洞可能被遺漏的問題,提出一種基于外部函數(shù)探測和校正的隱藏路徑搜索方案HPSBEF。該方案闡述了外部函數(shù)探測和校正的實現(xiàn)過程,并針對虛擬機級別插樁和用戶態(tài)級別插樁2種方式分別進行了驗證。實驗結果表明,HPSBEF方案能有效提升路徑覆蓋率和漏洞檢測能力,同時降低時間開銷。目前的白盒模糊測試技術經(jīng)常遭遇如路徑爆炸、復雜約束求解等問題,下一步將針對這些問題進行研究,以使該技術更好地應用于漏洞檢測領域。