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

?

基于數(shù)據(jù)結(jié)構(gòu)特征發(fā)現(xiàn)的腳本引擎內(nèi)置對象別名關(guān)系識別

2022-08-16 09:34張羿偉萬欣宇郭蘇越
信息安全學報 2022年4期
關(guān)鍵詞:腳本數(shù)據(jù)結(jié)構(gòu)內(nèi)置

張羿偉,游 偉,梁 彬,萬欣宇,郭蘇越

中國人民大學信息學院 北京 中國 100872

1 引言

腳本語言具有簡單易用的特性,越來越多的軟件支持通過腳本語言可編程式地調(diào)用各項程序功能。這些軟件通過內(nèi)置的腳本引擎對腳本代碼進行解析并執(zhí)行。內(nèi)置腳本引擎除了支持標準的腳本語言規(guī)范,還提供了一系列擴展的應(yīng)用程序編程接口(Application programming interface,API)和內(nèi)置對象。用戶可以通過調(diào)用擴展 API 來執(zhí)行由本地代碼(Native code)實現(xiàn)的程序功能。

腳本引擎在豐富軟件功能的同時,也引入了額外的攻擊面。攻擊者可以利用腳本引擎漏洞,編寫特定攻擊代碼,達到控制用戶設(shè)備、竊取用戶隱私等目的。根據(jù)漏洞庫中的信息,近年來頻繁出現(xiàn)腳本引擎相關(guān)的程序安全漏洞,已造成大量經(jīng)濟損失與安全危害[1]。通過對已公開的漏洞利用樣本進行分析[2],我們發(fā)現(xiàn)絕大多數(shù)的腳本引擎漏洞是由于擴展API和內(nèi)置對象使用不當產(chǎn)生的。

針對腳本引擎中的程序漏洞,目前主流的檢測技術(shù)為模糊測試(Fuzzing)[3],使用特定的生成/變異策略產(chǎn)生大量測試樣本,并監(jiān)測測試樣本的執(zhí)行是否觸發(fā)程序異常。然而,現(xiàn)有的模糊測試研究工作主要關(guān)注如何生成符合腳本語言語法規(guī)范的測試樣本。雖然取得了一定的成效,但是這些研究工作僅能檢測出腳本引擎淺層解析代碼中的漏洞,難以有效檢測出腳本引擎深層代碼中的漏洞。

腳本引擎中的深層次漏洞通常與擴展API 及內(nèi)置對象相關(guān)。以釋放后使用(Use-After-Free,UAF)漏洞為例,其本質(zhì)為通過懸掛指針非法訪問了被釋放的內(nèi)存區(qū)域。在腳本引擎中,通過調(diào)用特定的API序列,可以創(chuàng)建內(nèi)置對象內(nèi)部的共享內(nèi)存區(qū)域,進而產(chǎn)生不同對象在共享內(nèi)存區(qū)域上的別名關(guān)系。釋放其中一個對象內(nèi)部的共享內(nèi)存區(qū)域,將會在與其存在別名關(guān)系的另一對象內(nèi)部產(chǎn)生懸掛指針,訪問該懸掛指針會觸發(fā)釋放后使用漏洞?,F(xiàn)有的模糊測試技術(shù)沒有關(guān)注內(nèi)置對象內(nèi)部結(jié)構(gòu),因而在發(fā)現(xiàn)釋放后使用漏洞方面效果較差。

為了檢測由對象別名關(guān)系導(dǎo)致的釋放后使用漏洞,需要解決兩個關(guān)鍵的技術(shù)挑戰(zhàn)。其一,如何高效地識別內(nèi)置對象別名關(guān)系。腳本引擎中的內(nèi)置對象由腳本引擎負責維護,用戶可以通過使用API 的方式來創(chuàng)建、更改和刪除內(nèi)置對象。生成合理的API序列對不同對象進行有針對性的操作是首要解決的問題。除此之外,由于內(nèi)置對象別名關(guān)系與對象結(jié)構(gòu)緊密相關(guān),需要實現(xiàn)一種機制快速檢測API 序列的執(zhí)行是否建立了對象間的共享內(nèi)存區(qū)域。其二,如何利用識別出的對象別名關(guān)系檢測腳本引擎的釋放后使用漏洞。為了觸發(fā)腳本引擎釋放后使用漏洞,需要構(gòu)造性地利用對象別名關(guān)系以產(chǎn)生懸掛指針。別名關(guān)系代表不同內(nèi)置對象內(nèi)部存在共享內(nèi)存區(qū)域,因此需要考慮如何生成API 序列釋放對象內(nèi)部的共享內(nèi)存區(qū)域,以產(chǎn)生懸掛指針。進一步,利用API 序列針對性地訪問懸掛指針,觸發(fā)釋放后使用漏洞。

為了解決上述挑戰(zhàn),我們提出了一種基于數(shù)據(jù)結(jié)構(gòu)特征發(fā)現(xiàn)的腳本引擎內(nèi)置對象別名關(guān)系識別方法,并利用內(nèi)置對象別名關(guān)系輔助檢測腳本引擎中的釋放后使用漏洞。具體而言,通過生成特殊的腳本引擎API 調(diào)用序列,建立內(nèi)置對象別名關(guān)系,并使用對象數(shù)據(jù)結(jié)構(gòu)特征,將內(nèi)置對象別名關(guān)系識別轉(zhuǎn)化為圖連通性識別,加快了內(nèi)置對象別名關(guān)系識別速度。在此基礎(chǔ)上,我們提出了一種利用別名關(guān)系構(gòu)造式觸發(fā)釋放后使用漏洞的檢測方案。對于具有別名關(guān)系的兩個對象,通過調(diào)用特殊的腳本引擎API 序列,釋放其中一個對象內(nèi)部的共享內(nèi)存區(qū)域,從而在另一個對象內(nèi)部產(chǎn)生懸掛指針,隨后調(diào)用特殊的腳本引擎API 序列訪問另懸掛指針,以觸發(fā)釋放后使用漏洞。

本文的主要貢獻有:

(1) 提出了一種基于數(shù)據(jù)結(jié)構(gòu)特征的腳本引擎內(nèi)置對象別名關(guān)系識別方法。通過利用內(nèi)置對象數(shù)據(jù)結(jié)構(gòu)特征,自動化提取腳本引擎API 參數(shù)信息并生成合理API 序列,提高了對象別名關(guān)系的產(chǎn)生概率。同時,利用對象數(shù)據(jù)結(jié)構(gòu)特征,將內(nèi)置對象別名關(guān)系識別轉(zhuǎn)化為圖連通性識別。在保證識別準確率的前提下,顯著減小了性能開銷,加快了內(nèi)置對象別名關(guān)系識別速度。

(2) 創(chuàng)新性地將內(nèi)置對象別名關(guān)系應(yīng)用于檢測腳本引擎釋放后使用漏洞。通過使用內(nèi)置對象別名關(guān)系,搭配特殊API 序列,增加了釋放操作產(chǎn)生的懸掛指針數(shù)量。相比于傳統(tǒng)釋放后使用漏洞,由對象別名關(guān)系導(dǎo)致的釋放后使用漏洞更容易利用且更難修補。

(3) 設(shè)計并實現(xiàn)了別名關(guān)系識別方法的系統(tǒng)原型,成功提取出了284 組內(nèi)置對象別名關(guān)系,并設(shè)計評估實驗證明了方法的高效性與準確性。進一步,針對真實軟件中的腳本引擎進行了漏洞檢測,成功發(fā)現(xiàn)了4 個未知釋放后使用漏洞,幫助提高了軟件安全性。

2 相關(guān)工作

針對腳本引擎中的安全漏洞,目前主流的檢測技術(shù)為模糊測試(Fuzzing)技術(shù)。模糊測試技術(shù)根據(jù)特定的生成策略產(chǎn)生大量測試樣本,并監(jiān)測測試樣本的執(zhí)行能否觸發(fā)程序異常。按照技術(shù)細節(jié)不同,具體又可分為生成型模糊測試以及變異型模糊測試[4]。

生成型模糊測試技術(shù)基于給定的規(guī)范(如語言語法,樣本結(jié)構(gòu)等)生成測試樣本,部分生成型模糊測試框架也會借助語料庫信息[5],常見的測試框架有SPIKE[6]和jsfunfuzz[7]等。為了生成語法正確的測試樣本,通常使用上下文無關(guān)語法作為規(guī)范[8-9]。除此之外,HyungSeok 等人[10]和Soyeon 等人[11]提出利用現(xiàn)有正確測試樣本中的語法信息來完善樣本生成規(guī)范。進一步,Suyoung 等人[12]提出利用神經(jīng)網(wǎng)絡(luò)來訓(xùn)練模型以產(chǎn)生樣本生成規(guī)范,旨在通過完善規(guī)范來提升測試樣本質(zhì)量。然而,不同腳本語句之間存在依賴關(guān)系,生成型模糊測試技術(shù)無法捕獲此類依賴關(guān)系,因而無法避免由依賴關(guān)系導(dǎo)致的運行時異常。

變異型模糊測試技術(shù)基于給定的種子輸入,采用不同的變異策略(如比特翻轉(zhuǎn),隨機替換等)生成新的測試樣本,常見的測試框架有AFL[13]和SymFuzz[14]等。變異型模糊測試技術(shù)通常以代碼覆蓋率為指標評估測試樣本的質(zhì)量,認為代碼覆蓋率越高測試樣本質(zhì)量越好。為了提高測試樣本的質(zhì)量,常使用污染傳播[15-16],符號執(zhí)行[17]和約束求解[18]等技術(shù)。除此之外,Sanjay 等人[19]通過設(shè)置程序路徑權(quán)重,有針對性地進行變異以生成測試樣本,以及Caroline 等人[20]提出在變異過程中加入已知漏洞樣本(Proof of Concept)信息,以生成高質(zhì)量測試樣本。然而,變異型模糊測試容易破壞種子輸入的完整性,導(dǎo)致無法通過完整性校驗。

在測試腳本引擎漏洞方面,Sung 等人[21]提出了一種使用內(nèi)置API 和內(nèi)置對象來測試JavaScript 腳本引擎的方法,以減少運行時錯誤的產(chǎn)生。該方法雖然利用腳本引擎API 和內(nèi)置對象信息減少了運行時錯誤的數(shù)量,但測試樣本的生成不夠自動化,且需要一些先驗知識做引導(dǎo)。除此之外,Nicolas 等人[22]提出利用程序運行時的內(nèi)存訪問信息來改進測試樣本,以測試更復(fù)雜的程序模塊,但對于大型腳本引擎效果不佳。

別名分析的主要目的在于確定程序中指針的具體指向,進而判定指針操作是否存在潛在風險[23]?,F(xiàn)有別名分析技術(shù)主要分為靜態(tài)別名分析以及動態(tài)別名分析。靜態(tài)別名分析通過分析程序源代碼來識別別名關(guān)系,常結(jié)合控制流圖或調(diào)用圖進行分析[24],即過程間分析[25-26]。通常靜態(tài)別名分析所需時間與控制流圖的復(fù)雜程度成正比,為了精簡控制流圖,Tobias 等人[27]提出將分支條件與變量狀態(tài)關(guān)聯(lián),并依據(jù)變量狀態(tài)來刪除冗余分支。動態(tài)別名分析則通過監(jiān)控內(nèi)存中變量的變化來識別別名關(guān)系,常在中間代碼層面進行程序插裝以判斷不同變量之間是否共享同一內(nèi)存區(qū)域[28-29]。Manu 等人[30]提出實施簡單的可達性分析,縮小監(jiān)控范圍提高分析性能。然而,由于腳本引擎結(jié)構(gòu)復(fù)雜,使用現(xiàn)有技術(shù)獲得精確分析結(jié)果存在性能開銷大的問題,且內(nèi)置對象結(jié)構(gòu)特殊,無法使用通用的處理策略。

3 腳本引擎與釋放后使用漏洞

腳本引擎作為腳本語言運行的載體,負責解析執(zhí)行腳本語言,以及提供相應(yīng)的運行環(huán)境。腳本引擎提供了一系列定制的應(yīng)用程序接口(Application programming interface,API)來幫助用戶高效使用內(nèi)部功能,我們稱之為特殊API,如Adobe Reader 內(nèi)嵌腳本引擎中的Doc.createIcon 負責在文檔中創(chuàng)建按鈕元件。與特殊API 類似,腳本引擎中的內(nèi)置對象也具有特殊的結(jié)構(gòu)和語義,如Adobe Reader 內(nèi)嵌引擎中的Doc 對象代表當前文檔。特殊API 以及內(nèi)置對象是腳本引擎的重要組成部分,不同腳本引擎中特殊API 以及內(nèi)置對象的數(shù)量和功能均有較大差異,合理使用特殊API 與內(nèi)置對象能觸及更多、更深層次的引擎代碼。

在使用腳本引擎API 時,需要遵照特定的使用規(guī)范,即提供正確數(shù)量和類型的參數(shù)。當腳本引擎API 參數(shù)的數(shù)量或類型錯誤時,腳本引擎會拋出運行時異常,并終止腳本代碼的執(zhí)行。腳本引擎中API參數(shù)的類型包括布爾類型,整數(shù)類型,浮點類型,字符串類型以及對象類型五類,不同類型之間可以動態(tài)轉(zhuǎn)化。

進一步,經(jīng)過實驗發(fā)現(xiàn),腳本引擎API 在運行時需要特定結(jié)構(gòu)的內(nèi)置對象,因此需要分析內(nèi)置對象的細粒度類型信息。通常,對于腳本引擎API,軟件開發(fā)者會提供一份官方的使用規(guī)范,說明不同腳本引擎API 需要的參數(shù)個數(shù)以及類型。然而,以Adobe Reader 內(nèi)嵌腳本引擎為例,官方提供的使用規(guī)范并不詳細,未說明腳本引擎API 對象參數(shù)的細粒度類型信息。我們統(tǒng)計了Adobe Reader 內(nèi)嵌腳本引擎中的386 個API,其中使用對象類型參數(shù)的API 有255個,占比約66%。為了減少運行時錯誤,我們需要提取API 參數(shù)的細粒度類型,進而正確且針對性地使用腳本引擎API 和內(nèi)置對象。

內(nèi)置對象的生命周期由腳本引擎負責維護,在生命周期結(jié)束時被腳本引擎回收。用戶可通過調(diào)用腳本引擎API 來創(chuàng)建,更改和刪除內(nèi)置對象,即能夠通過調(diào)用腳本引擎API 影響內(nèi)置對象生命周期。按照創(chuàng)建方式的不同,具體可以分為靜態(tài)內(nèi)置對象以及動態(tài)內(nèi)置對象。靜態(tài)內(nèi)置對象由腳本引擎預(yù)創(chuàng)建,可直接使用對象名稱訪問,而動態(tài)內(nèi)置對象需要調(diào)用腳本引擎API 創(chuàng)建后才能訪問。

對于靜態(tài)內(nèi)置對象以及動態(tài)內(nèi)置對象,腳本引擎采用一個活躍對象集合維護其生命周期?;钴S對象集合存儲腳本引擎中存活的內(nèi)置對象,其狀態(tài)隨著內(nèi)置對象的創(chuàng)建和刪除實時變化?;钴S對象集合由腳本引擎負責維護,用戶無法直接更改其狀態(tài),但可以通過影響內(nèi)置對象生命周期,間接影響活躍對象集合的狀態(tài)?;钴S對象集合常用于查詢內(nèi)置對象生命周期信息,在腳本引擎中具有重要意義。

腳本引擎在通過豐富的API 以及內(nèi)置對象提供便利的同時,也引入了更多安全漏洞。攻擊者可利用腳本引擎API 以及內(nèi)置對象構(gòu)造特殊的攻擊代碼,觸發(fā)多種類型的安全漏洞,對用戶造成危害。其中,釋放后使用漏洞與非法內(nèi)存操作緊密相關(guān),可利用性強,常成為攻擊者的重點關(guān)注目標。釋放后使用漏洞的本質(zhì)為通過懸掛指針訪問了被釋放的內(nèi)存區(qū)域,而懸掛指針是指向被釋放內(nèi)存區(qū)域的指針,是由于對象釋放后缺乏合理的訪問檢查導(dǎo)致。我們以圖1 為例具體說明釋放后使用漏洞的觸發(fā)模式和修補方式。

圖1 展示了1 個典型釋放后使用漏洞的觸發(fā)模式。首先執(zhí)行創(chuàng)建操作(malloc)創(chuàng)建對象p(對應(yīng)標號為①的語句)。接著調(diào)用合適的API 序列執(zhí)行釋放操作(free),釋放創(chuàng)建出的對象p并產(chǎn)生了懸掛指針(對應(yīng)標號為②的語句)。最后,使用(use)被釋放的對象p并訪問其內(nèi)部的懸掛指針,觸發(fā)了釋放后使用類型的安全漏洞(對應(yīng)標號為③的語句)?,F(xiàn)有工作針對釋放后使用漏洞的修補方式對應(yīng)語句⑤⑦⑧,即增加對象釋放標志并根據(jù)對象狀態(tài)實時維護釋放標志(對應(yīng)標號為⑦的語句),同時在每次訪問對象前檢查釋放標志的狀態(tài)(對應(yīng)標號為⑧的語句),當對象釋放標志被設(shè)置為True 時拒絕訪問對象。

修復(fù)釋放后使用漏洞的核心在于消除懸掛指針,現(xiàn)有設(shè)置對象釋放標志的方式簡單有效,且適用于所有類型的對象,通用性強。然而,由于腳本引擎內(nèi)部存在復(fù)雜對象關(guān)系,比如內(nèi)置對象別名關(guān)系,導(dǎo)致不同內(nèi)置對象內(nèi)部存在共享內(nèi)存區(qū)域,進而在釋放時出現(xiàn)多個懸掛指針。相比于原有的釋放后使用漏洞,使用對象別名關(guān)系可以在不同對象中引入懸掛指針,增加了漏洞的不確定性,利用釋放標志進行修補也更加困難。我們將由對象別名關(guān)系導(dǎo)致的釋放后使用漏洞觸發(fā)模式總結(jié)為圖2 所示。

如圖2 所示,首先執(zhí)行創(chuàng)建操作產(chǎn)生了對象p和對象q(對應(yīng)標號為①②的語句)。接著,通過執(zhí)行特定腳本引擎API 序列,建立了對象p和對象q之間的別名關(guān)系(對應(yīng)標號為③的語句),具體對應(yīng)對象p,q內(nèi)部的共享內(nèi)存區(qū)域。之后,執(zhí)行釋放操作釋放對象p(對應(yīng)標號為④的語句),產(chǎn)生了懸掛指針。不同于傳統(tǒng)釋放后使用漏洞觸發(fā)模式,在最后訪問懸掛指針時,并未使用上一步中釋放的對象p,而是使用了與對象p存在別名關(guān)系的對象q(對應(yīng)標號為⑤的語句)。

相較于傳統(tǒng)的釋放后使用漏洞,通過建立內(nèi)置對象別名關(guān)系,引入了更多的懸掛指針,增加了釋放后使用漏洞的可利用性。傳統(tǒng)的釋放后使用漏洞觸發(fā)模式與對象別名關(guān)系導(dǎo)致的釋放后使用漏洞觸發(fā)模式有如下異同點:二者均通過訪問懸掛指針觸發(fā)安全漏洞,但在傳統(tǒng)釋放后使用漏洞觸發(fā)模式中,釋放以及使用的為同一個對象,僅產(chǎn)生了一個懸掛指針。在別名關(guān)系導(dǎo)致的釋放后使用漏洞中,釋放以及使用操作涉及不同對象,產(chǎn)生了多個懸掛指針。相較于傳統(tǒng)的釋放后使用漏洞,別名關(guān)系觸發(fā)的釋放后使用漏洞更為復(fù)雜,涉及不同內(nèi)置對象的交互,并且現(xiàn)有的防御措施無法有效抵御別名關(guān)系導(dǎo)致的釋放后使用漏洞。

4 別名關(guān)系識別

本研究提出了一種基于數(shù)據(jù)結(jié)構(gòu)特征腳本引擎內(nèi)置對象別名關(guān)系識別方法,能高效準確地建立并識別腳本引擎內(nèi)置對象別名關(guān)系,如圖3 所示,整個內(nèi)置對象別名關(guān)系識別方法共分為三個階段。

第一階段對應(yīng)文章3.2 節(jié),負責提取腳本引擎內(nèi)置對象的數(shù)據(jù)結(jié)構(gòu)特征。該階段主要采用動態(tài)插裝的方式,使用腳本引擎API 創(chuàng)建并訪問內(nèi)置對象并監(jiān)控程序執(zhí)行,獲取程序運行時內(nèi)置對象的內(nèi)存地址。進一步,通過識別與對象起始地址相關(guān)的內(nèi)存單元類型,提取腳本引擎內(nèi)置對象的數(shù)據(jù)結(jié)構(gòu)特征。

第二階段對應(yīng)文章3.3 節(jié),負責生成參數(shù)類型正確的腳本引擎API 調(diào)用實例。此階段我們采用靜態(tài)分析技術(shù)來提取腳本引擎API 對象參數(shù)的內(nèi)存訪問模式,并與第一階段中提取的對象數(shù)據(jù)結(jié)構(gòu)特征匹配,獲得API 對象參數(shù)候選集。進一步,使用獲得的API 對象參數(shù)列表,生成參數(shù)類型正確的腳本引擎API 調(diào)用語句,并組合不同API 調(diào)用語句產(chǎn)生測試樣本。

第三階段對應(yīng)文章3.4 節(jié),負責監(jiān)測共享內(nèi)存區(qū)域來識別內(nèi)置對象別名關(guān)系。該階段主要使用動態(tài)測試的技術(shù),獲取程序運行時對象的內(nèi)存地址,提取對應(yīng)的內(nèi)置對象數(shù)據(jù)結(jié)構(gòu)特征,并轉(zhuǎn)化為數(shù)據(jù)結(jié)構(gòu)特征圖存儲。通過在API 執(zhí)行前后插入檢查點的方式,分析數(shù)據(jù)結(jié)構(gòu)特征圖的變化,并根據(jù)數(shù)據(jù)結(jié)構(gòu)特征圖的變化識別內(nèi)置對象別名關(guān)系。

在接下來的部分,我們首先介紹內(nèi)置對象數(shù)據(jù)結(jié)構(gòu)特征的定義,之后依次介紹流程每個階段的具體實現(xiàn)。

4.1 內(nèi)置對象數(shù)據(jù)結(jié)構(gòu)特征

內(nèi)置對象數(shù)據(jù)結(jié)構(gòu)特征T定義為由不同類型內(nèi)存單元組成的,具有明顯分層與指向關(guān)系的一種內(nèi)存特征。我們以字長為單位劃分內(nèi)置對象所在的內(nèi)存區(qū)域,并將劃分后的字長大小的內(nèi)存區(qū)域稱為內(nèi)存單元。字長的大小與操作系統(tǒng)相關(guān),在64 位操作系統(tǒng)中字長大小為8 字節(jié),32 位操作系統(tǒng)中字長大小為4 字節(jié)。對于內(nèi)置對象數(shù)據(jù)結(jié)構(gòu)特征T,我們采用有向圖的方式表示如下:

其中,V代表有向圖中頂點的集合,E代表有向圖中邊的集合,具體如下:

頂點集合V中每個元素vi均對應(yīng)一個內(nèi)存單元,vi的具體定義如下:

vi定義為由Value,Type和Ancient 組成的三元組,其中Value 代表運行時內(nèi)存單元中存儲的數(shù)值,隨著腳本引擎API 的執(zhí)行發(fā)生變化。Type 代表內(nèi)存單元數(shù)值的類型,具體包括可變數(shù)值類型(Number),固定數(shù)值類型(Padding)以及指針類型(Pointer)3 種,在具體實現(xiàn)時用不同數(shù)值代表3 種類型(如指針類型對應(yīng)數(shù)值0x1)。固定數(shù)值類型代表內(nèi)存單元中數(shù)據(jù)的數(shù)值固定,即相同類型的內(nèi)置對象擁有相同的數(shù)值。同時,固定數(shù)值類型對應(yīng)的數(shù)值與運行狀態(tài)無關(guān),即多次創(chuàng)建出相同類型的內(nèi)置對象,其固定數(shù)值類型對應(yīng)的數(shù)值均相同??勺償?shù)值類型的定義與固定數(shù)值類型相反。對于相同類型對象,可變數(shù)值類型內(nèi)存單元中數(shù)據(jù)的數(shù)值不固定,且與運行狀態(tài)緊密相關(guān),在多次運行中會得到不同的數(shù)值。指針類型的內(nèi)存單元代表該內(nèi)存單元中的數(shù)據(jù)指向其他合法內(nèi)存單元,能夠通過指針解引用操作進行層級展開。Ancient為內(nèi)存單元所屬標識,代表該內(nèi)存單元從屬于特定內(nèi)置對象。我們?yōu)橥粌?nèi)置對象下屬所有內(nèi)存單元設(shè)置統(tǒng)一的Ancient,并使用唯一Ancient值以區(qū)分不同內(nèi)置對象。通常,Ancient 值被設(shè)置為內(nèi)置對象運行時內(nèi)存起始地址,方便查詢內(nèi)置對象信息。

邊集E中每個元素ei代表有向圖中頂點的指向關(guān)系,其具體定義如下:

其中,vs和ve分別代表該有向邊的起始頂點和終止頂點,對應(yīng)頂點集合V中的元素。Offset 為頂點vs代表內(nèi)存單元到頂點ve代表內(nèi)存單元的內(nèi)存偏移。在ei中,頂點vs對應(yīng)指針類型的內(nèi)存單元,我們根據(jù)其中包含的指向信息劃分對象數(shù)據(jù)結(jié)構(gòu)特征內(nèi)部的層級。以Stream 對象為例,Stream 對象起始地址對應(yīng)的內(nèi)存區(qū)域稱為第一層級,第一層級中指針類型指向的內(nèi)存區(qū)域為第二層級,以此類推。

我們將存儲對象數(shù)據(jù)結(jié)構(gòu)特征的有向圖稱為數(shù)據(jù)結(jié)構(gòu)特征圖。數(shù)據(jù)結(jié)構(gòu)特征圖中的根節(jié)點代表具體內(nèi)置對象,每個子節(jié)點對應(yīng)數(shù)據(jù)結(jié)構(gòu)特征中的每個內(nèi)存單元。以Stream 類型對象為例,最終轉(zhuǎn)化成的數(shù)據(jù)結(jié)構(gòu)特征圖如圖4 所示。以圖4 右下角陰影標注的頂點為例,三元組中Value 的值為0x1340,代表運行時內(nèi)存單元中的數(shù)值為0x1340。Type 值為0x1代表該頂點的類型為指針類型。Ancient 值為0x1220對應(yīng)對象根節(jié)點的Value 值(即圖4 中Stream 標注的頂點),標明該頂點屬于Stream 對象。

內(nèi)置對象數(shù)據(jù)結(jié)構(gòu)特征中的層級個數(shù)以及內(nèi)存單元個數(shù)可自主配置。通常而言,使用的層級個數(shù)和內(nèi)存單元個數(shù)越多,特征的分類效果越好,而性能開銷則越大。為了確定合適的配置,我們進行了相關(guān)探索,結(jié)果如表1 所示。當層級個數(shù)為1,每個層級中內(nèi)存單元的個數(shù)為4 時,所有內(nèi)置對象被歸為一個類,沒有任何區(qū)分度。隨著層級個數(shù)與內(nèi)存單元個數(shù)的增加,內(nèi)置對象種類的數(shù)量也隨之上升,上升幅度呈遞減趨勢。最終,出于平衡性能開銷與分類效果的考慮,確定層級個數(shù)為3,層級中內(nèi)存單元個數(shù)為8。第6 章的實驗評估結(jié)果也表明,這樣的配置可以保證較高的測試用例生成效率。

表1 內(nèi)置對象種類統(tǒng)計表Table 1 Statistics of built-in object type

4.2 提取內(nèi)置對象數(shù)據(jù)結(jié)構(gòu)特征

在該節(jié)中我們具體介紹如何提取內(nèi)置對象數(shù)據(jù)結(jié)構(gòu)特征。該階段主要采用動態(tài)插裝的方式,獲取程序運行時內(nèi)置對象的內(nèi)存地址,具體又分為獲取對象內(nèi)存地址以及內(nèi)存單元類型識別兩部分。進一步,通過識別內(nèi)置對象內(nèi)存地址相關(guān)的內(nèi)存單元類型,提取出內(nèi)置對象的數(shù)據(jù)結(jié)構(gòu)特征。

4.2.1 提取內(nèi)置對象列表

首先需要提取腳本引擎中所有靜態(tài)、動態(tài)內(nèi)置對象。為了提取腳本引擎所有的靜態(tài)內(nèi)置對象,我們采用深度優(yōu)先遍歷的方式,從文檔根節(jié)點出發(fā)依次訪問文檔中所有節(jié)點。具體實現(xiàn)時我們使用for-in的方式進行遍歷,對于訪問到的每一個節(jié)點,調(diào)用內(nèi)置操作符typeof 判斷其節(jié)點類型,并記錄對象類型節(jié)點的信息,即靜態(tài)內(nèi)置對象信息。同時,為了避免遍歷過程陷入死循環(huán),我們標記了所有已訪問的節(jié)點,并以集合的方式存儲。當某次訪問的節(jié)點位于標記集合中時,不再從該節(jié)點出發(fā)進行更深層次的遍歷。

腳本引擎中的動態(tài)內(nèi)置對象需要調(diào)用腳本引擎API 創(chuàng)建。由于官方文檔的說明信息不全,如針對Collab 這一類型的內(nèi)置對象,官方文檔中僅包含三個子方法說明,而實際存在23 個子方法。因此,需要提取不在官方文檔中的腳本引擎API,以獲得完整的腳本引擎API 列表。我們同樣采用從文檔根節(jié)點出發(fā)深度優(yōu)先遍歷的方式,記錄類型為函數(shù)的節(jié)點信息,將其整理為最終的腳本引擎API 列表,使用提取出的API 列表可以獲得所有動態(tài)內(nèi)置對象。接下來,針對提取出的所有內(nèi)置對象,提取對應(yīng)的數(shù)據(jù)結(jié)構(gòu)特征。

4.2.2 獲取內(nèi)置對象內(nèi)存地址

為了獲得內(nèi)置對象內(nèi)存地址,需要使用腳本引擎API 創(chuàng)建和訪問內(nèi)置對象。通過在腳本引擎API執(zhí)行前后插入檢查點,結(jié)合動態(tài)插裝技術(shù)來獲得內(nèi)置對象內(nèi)存地址。我們以圖5 中偽代碼為例,說明如何獲得內(nèi)置對象內(nèi)存地址。

為了獲取對象內(nèi)存地址,我們生成腳本引擎API 調(diào)用語句創(chuàng)建和訪問內(nèi)置對象,對應(yīng)圖5 中②④,同時在每條腳本引擎API 調(diào)用語句前后設(shè)置檢查點,對應(yīng)圖5 中①③⑤行。使用微軟提供的動態(tài)調(diào)試工具Windbg 可以在檢查點判斷上一條API 調(diào)用語句返回值的類型,根據(jù)返回值的類型可以獲取部分由API 創(chuàng)建的動態(tài)內(nèi)置對象內(nèi)存地址。除此之外,還可以依據(jù)腳本引擎活躍對象集合狀態(tài)的變化獲取內(nèi)置對象內(nèi)存地址。當創(chuàng)建新內(nèi)置對象時,活躍對象集合將相應(yīng)地增加新元素,通過分析活躍對象集合的變化可以獲取內(nèi)置對象內(nèi)存地址。

4.2.3 識別內(nèi)存單元類型

在獲取了內(nèi)置對象內(nèi)存地址后,需要識別內(nèi)存單元類型,具體為從對象內(nèi)存地址出發(fā),識別其對應(yīng)內(nèi)存區(qū)域中內(nèi)存單元數(shù)據(jù)的類型。接下來,我們將結(jié)合圖6 闡述識別內(nèi)存單元數(shù)據(jù)類型的具體流程。

為了提取對象數(shù)據(jù)結(jié)構(gòu)特征,需要將內(nèi)存單元識別為指針類型,可變數(shù)值類型或固定數(shù)值類型。進一步,依據(jù)指針類型內(nèi)存單元的指向關(guān)系,識別出層級結(jié)構(gòu)特征,并組合各個層級形成對象數(shù)據(jù)結(jié)構(gòu)特征。我們使用進程的內(nèi)存布局信息作為先驗知識來初步區(qū)分指針類型或數(shù)值類型。在Windows 系統(tǒng)中程序的內(nèi)存布局可大致分為代碼段,數(shù)據(jù)段,棧以及堆。對于指針類型的內(nèi)存單元,其存儲數(shù)值對應(yīng)的內(nèi)存地址位于堆中。通過獲取進程運行時內(nèi)存布局信息,得到堆起始和終止內(nèi)存地址,并依據(jù)內(nèi)存單元中數(shù)據(jù)數(shù)值是否位于堆范圍來初步區(qū)分指針類型與數(shù)值類型(超出堆范圍的數(shù)據(jù)可判定為數(shù)值類型)。使用進程的內(nèi)存布局信息能識別大約90%的內(nèi)存單元類型,但仍有10%的數(shù)據(jù)因數(shù)值特殊而無法區(qū)分,如數(shù)值類型中存在位于堆的范圍的特殊值。此類數(shù)據(jù)對應(yīng)的內(nèi)存單元(后續(xù)稱為待定數(shù)值單元)將進一步進行內(nèi)存單元的類型識別。

我們發(fā)現(xiàn)開啟地址隨機化之后,在多次測試中,待定數(shù)值單元中的數(shù)值每次均位于堆范圍內(nèi)的概率較小。因此,可以進行多次獨立測試,并統(tǒng)計待定數(shù)值單元中數(shù)值位于堆范圍的次數(shù),來判斷該待定數(shù)值單元的類型。同時,由于固定數(shù)值類型在多次測試中將具有相同的數(shù)值,也可用該方法來區(qū)分可變數(shù)值類型與固定數(shù)值類型。從內(nèi)置對象內(nèi)存起始地址出發(fā)進行層級展開時,我們通過配置參數(shù)的方式設(shè)置層級的個數(shù)以及層級的大小,并通過不斷實驗調(diào)整配置參數(shù),以達到最好的區(qū)分效果。最終,結(jié)合各層級中的內(nèi)存單元類型可以提取出準確的內(nèi)置對象數(shù)據(jù)結(jié)構(gòu)特征。

在提取出內(nèi)置對象數(shù)據(jù)結(jié)構(gòu)特征后,需要將數(shù)據(jù)結(jié)構(gòu)特征轉(zhuǎn)換為數(shù)據(jù)結(jié)構(gòu)特征圖進行存儲。在轉(zhuǎn)換的過程中,我們重點關(guān)注指針類型對應(yīng)的內(nèi)存單元,并將其指向關(guān)系對應(yīng)的內(nèi)存偏移映射為數(shù)據(jù)結(jié)構(gòu)特征圖中的邊。

4.3 生成參數(shù)類型正確的API 序列

官方文檔中對于腳本引擎API 的描述通常較為粗略,如針對腳本引擎API 參數(shù)的描述中僅說明了參數(shù)的基礎(chǔ)類型(整數(shù)類型,布爾類型,對象類型等),而沒有說明參數(shù)的細粒度類型。根據(jù)統(tǒng)計,在Adobe Reader 內(nèi)置腳本引擎中總共包含163 種不同類型的內(nèi)置對象。當一個腳本引擎API 的參數(shù)為對象類型時,能接收腳本引擎中所有類型的內(nèi)置對象,且不會提示類型錯誤。然而,當輸入?yún)?shù)的細粒度類型與腳本引擎API 參數(shù)需求的細粒度類型不符時,API 無法正確處理所提供的參數(shù),從而無法產(chǎn)生對象別名關(guān)系。我們發(fā)現(xiàn)當為腳本引擎API 提供正確類型的對象參數(shù)時,涉及的參數(shù)內(nèi)存訪問模式能夠匹配對應(yīng)的對象數(shù)據(jù)結(jié)構(gòu)特征。

我們按照腳本引擎內(nèi)置對象具體數(shù)據(jù)結(jié)構(gòu)的不同,劃分內(nèi)置對象的細粒度類型。相較于粗粒度類型,即基礎(chǔ)類型(如對象類型,字符串類型,整型等),細粒度類型能夠更全面地展現(xiàn)不同腳本引擎內(nèi)置對象的差異。使用內(nèi)置對象細粒度類型,我們可以在調(diào)用腳本引擎API 時針對性地搭配內(nèi)置對象,在最大程度上保證API 調(diào)用具有合法語義,同時也減少了運行時錯誤的產(chǎn)生,間接增加了代碼覆蓋率。

在獲得腳本引擎API 對象參數(shù)信息之后,將生成腳本引擎API 調(diào)用語句。對于API 需求的對象類型參數(shù),我們根據(jù)參數(shù)候選集,選取類型符合的內(nèi)置對象作為輸入。當參數(shù)包含多個候選對象時,需分別使用不同候選對象生成對應(yīng)的API 調(diào)用語句。同時,在單個測試樣本的生成過程中,我們以列表的形式記錄已生成的內(nèi)置對象,方便后續(xù)使用。當引用新的靜態(tài)內(nèi)置對象或由于API 調(diào)用產(chǎn)生了新的動態(tài)內(nèi)置對象時,需要相應(yīng)地更改對象列表的內(nèi)容。對于其他基礎(chǔ)類型的API 參數(shù)(整數(shù)類型,字符串類型等),我們通過構(gòu)建常量表的方式,每次采用隨機策略從常量表中選擇類型合適的數(shù)據(jù)填充。

4.3.1 API 參數(shù)內(nèi)存訪問模式

對于API 參數(shù)內(nèi)存訪問模式M,我們采用如下定義:

M=<函數(shù)名,參數(shù)位次,內(nèi)存解引用序列>

其中函數(shù)名為具體腳本引擎API 的名稱,參數(shù)位次代表參數(shù)在API 調(diào)用時對應(yīng)的序號,而內(nèi)存解引用序列為該參數(shù)被使用時,按照層級的訪問順序,依次對應(yīng)層級跳轉(zhuǎn)時的內(nèi)存偏移。

以Collab 內(nèi)置對象下屬getInitiatorSource方法為例,該方法在使用時需要兩個 Stream 類型或類Stream 類型的參數(shù),而官方文檔中僅說明了兩個參數(shù)為基礎(chǔ)對象類型。對于Collab.getInitiatorSource 方法中參數(shù)的內(nèi)存訪問模式和對應(yīng)的對象數(shù)據(jù)結(jié)構(gòu)特征,我們總結(jié)如圖7 所示。以Collab.getInitiatorSource的第二個參數(shù)為例,提取出的內(nèi)存訪問模式可表示為。

在圖7左側(cè)標號①②的語句為具體腳本引擎API,在語句①中通過API Collab.newWrStream ToCosObj創(chuàng)建了FileStream 類型的內(nèi)置對象ObjA,并作為語句②中API 的第二個參數(shù)使用。圖7左側(cè)下半部分為API Collab.getInitiatorSource 對應(yīng)函數(shù)體的偽代碼,函數(shù)體中的*號代表指針解引用操作,即讀取指針指向內(nèi)存單元中的數(shù)據(jù)。在腳本引擎 API Collab.getInitiatorSource 對應(yīng)的函數(shù)體中,首先對第二個參數(shù)進行了指針解引用操作,并將讀取的值存儲于變量v1中,其內(nèi)存訪問模式偏移為0x0(下劃線部分)。之后,通過將變量v1傳給函數(shù)G,并將函數(shù)的返回值賦值給變量v2。函數(shù)G 針對傳入的參數(shù)再次進行了指針解引用操作,此時的內(nèi)存訪問模式偏移為0x4(下劃線部分)。據(jù)此,可以發(fā)現(xiàn)Collab.getInitiatorSource 對應(yīng)的函數(shù)F 中對第二個參數(shù)進行了兩次指針解引用操作,對應(yīng)的內(nèi)存訪問模式偏移分別為0x0以及0x4。

在圖7 右側(cè)為FileStream類型內(nèi)置對象的部分數(shù)據(jù)結(jié)構(gòu)特征,其中存在0x0 和0x4 的特征信息(虛線框部分,分別為第二層級到第三層級,以及第三層級到第四層級對應(yīng)的內(nèi)存偏移),與Collab.getInitiatorSource 函數(shù)第二個參數(shù)的內(nèi)存訪問模式相匹配。同時,不同內(nèi)置對象的數(shù)據(jù)結(jié)構(gòu)特征擁有不同的內(nèi)存偏移信息,而不同腳本引擎API 中對象參數(shù)的內(nèi)存訪問模式也不同。因此,可以通過匹配腳本引擎API 對象參數(shù)的內(nèi)存訪問模式與內(nèi)置對象內(nèi)存結(jié)構(gòu)特征,推斷腳本引擎API 對象參數(shù)的候選列表。

對于兩個不同類型的內(nèi)置對象,其數(shù)據(jù)結(jié)構(gòu)特征中包含的偏移信息分別為(0x1,0x2,0x4)以及(0x1,0x2,0x6,0x8),而腳本引擎API對象參數(shù)內(nèi)存訪問模式對應(yīng)的偏移為(0x1,0x2),則該腳本引擎API 參數(shù)候選集將同時包含上述兩種內(nèi)置對象。精確的靜態(tài)分析技術(shù)需要考慮多種因素,如控制流信息,數(shù)據(jù)依賴等,針對大型腳本引擎存在性能開銷過大的問題。我們的目標在于盡可能地縮小測試樣本生成時API 的搜索空間,因此我們提出了一種上下文不敏感,路徑不敏感,流不敏感的過程間數(shù)據(jù)流分析方法,用于提取API 對象參數(shù)的內(nèi)存訪問模式?;趦?nèi)存訪問模式,可以獲得參數(shù)類型的候選列表,即正確參數(shù)類型的超集。該方法在縮小腳本引擎API 參數(shù)搜索空間的同時,保證覆蓋正確的API 對象參數(shù)類型,減少參數(shù)類型錯誤導(dǎo)致的運行時異常。接下來,我們將介紹如何提取腳本引擎API 對象參數(shù)的內(nèi)存訪問模式。

4.3.2 提取API 參數(shù)內(nèi)存訪問模式

不同于已有的基于控制流圖或程序調(diào)用圖的靜態(tài)分析技術(shù),我們將重心放在提取腳本引擎API 對象參數(shù)的內(nèi)存訪問模式,主要關(guān)注API 實現(xiàn)體中與對象類型參數(shù)相關(guān)的處理邏輯。

我們的分析方法為上下文不敏感以及路徑不敏感,因此我們不關(guān)注API 實現(xiàn)體中的分支或循環(huán)等語句的控制條件,粗粒度地認為API 實現(xiàn)體中每一條語句均有被執(zhí)行的可能?;跀?shù)據(jù)流分析的思路,我們認為API 實現(xiàn)體中涉及對象參數(shù)的語句均為分析目標。我們提出的分析方法為過程間的數(shù)據(jù)流分析,因此遇到函數(shù)調(diào)用時,需要展開函數(shù)調(diào)用,嵌套分析函數(shù)調(diào)用內(nèi)部的處理邏輯。在匯編語言中,函數(shù)調(diào)用又可細分為直接函數(shù)調(diào)用以及間接函數(shù)調(diào)用。直接函數(shù)調(diào)用以函數(shù)名為句柄調(diào)用函數(shù),能在靜態(tài)分析時直接展開;間接函數(shù)調(diào)用通過函數(shù)表的方式進行函數(shù)調(diào)用,僅能在動態(tài)執(zhí)行時展開。接下來,以圖8 的一段函數(shù)實現(xiàn)體為例,具體介紹所使用的靜態(tài)分析方法。

在圖8 中,腳本引擎API 對應(yīng)的實現(xiàn)體F,其使用兩個參數(shù)分別用arg1和arg2表示,其中arg1和arg2對應(yīng)的參數(shù)類型為對象。為了獲取參數(shù)的內(nèi)存訪問模式,需要跟蹤函數(shù)實現(xiàn)體中arg1參數(shù)的使用。在函數(shù)實現(xiàn)體F 中,arg1被作為另一函數(shù)G 的參數(shù)使用,因而需要進一步展開函數(shù)G 以分析函數(shù)G 中參數(shù)arg1的使用情況。此處,函數(shù)G 表現(xiàn)為直接函數(shù)調(diào)用,可以直接展開獲得具體函數(shù)實現(xiàn)。對于間接調(diào)用的情況,由于無法獲取具體函數(shù)實現(xiàn),我們將在稍后進行說明。

在函數(shù)G 中針對arg1進行了兩次指針解引用操作,并最終返回指針解引用的結(jié)果。兩次指針解引用分別對應(yīng)的固定偏移值為0x4 和0x8。因此,我們將偏移值0x4 和0x8 作為函數(shù)G 中arg1參數(shù)的內(nèi)存訪問模式,記為。同時,該固定偏移與內(nèi)置對象數(shù)據(jù)結(jié)構(gòu)特征中的偏移對應(yīng),代表著對象參數(shù)的類型信息。同理,可以提取函數(shù)H 中參數(shù)的內(nèi)存訪問模式,記為。對于函數(shù)F,通過分析可以得知,參數(shù)arg1被函數(shù)G 使用,而參數(shù)arg2被函數(shù)H 所使用。結(jié)合函數(shù)G 和函數(shù)H 提取出的參數(shù)內(nèi)存訪問模式,函數(shù)F 的參數(shù)內(nèi)存訪問模式可以歸納為。進一步,我們稱函數(shù)F 為父函數(shù),函數(shù)H 和函數(shù)G 為子函數(shù),父函數(shù)參數(shù)的內(nèi)存訪問模式為所有子函數(shù)參數(shù)內(nèi)存訪問模式的并集。對于間接調(diào)用的情況,以圖9 中代碼為例進行說明。

在函數(shù)P 的實現(xiàn)體中,Indirect-Call 代表間接函數(shù)調(diào)用,其將arg1作為參數(shù)使用,并將函數(shù)調(diào)用的返回值賦值給了變量b,后續(xù)變量b作為直接函數(shù)調(diào)用Q 的參數(shù)使用。對于圖9 中的間接函數(shù)調(diào)用,由于無法獲得具體函數(shù)實現(xiàn)體進行參數(shù)分析,我們粗粒度地認為變量b等同于參數(shù)arg1,即變量b提取出的內(nèi)存訪問模式也作為參數(shù)arg1的內(nèi)存訪問模式。進一步,通過分析直接函數(shù)調(diào)用Q的實現(xiàn)體,可以提取出函數(shù)Q 中參數(shù)arg1對應(yīng)的內(nèi)存訪問模式,記為。最終,可以得到變量b對應(yīng)的內(nèi)存訪問模式為,從而可以獲得函數(shù)P中參數(shù)的內(nèi)存訪問模式為

使用靜態(tài)分析的方式結(jié)合所有腳本引擎API 的實現(xiàn)體,可以提取出對象類型參數(shù)的內(nèi)存訪問模式。進一步,將內(nèi)存訪問模式與對象數(shù)據(jù)結(jié)構(gòu)信息匹配,獲得腳本引擎API 參數(shù)的細粒度信息。由于使用粗粒度的提取方式,最終提取結(jié)果為正確參數(shù)類型的超集,如針對Collab.getInitiatorSource 這一API,其正確參數(shù)類型為FileStream 類型,而提取出的參數(shù)候選集中包含 FileStream 類型,Stream 類型以及ReadStream 類型。除此之外,可以用同樣的方法提取腳本引擎API 返回值的內(nèi)存訪問模式,并采用同樣的方式進行存儲(對于API 返回值不像參數(shù)一樣區(qū)分序號,將統(tǒng)一用R 代替,如)。

4.4 識別對象別名關(guān)系

我們將3.3 中生成的測試樣本輸入腳本引擎,動態(tài)識別測試樣本運行過程中的內(nèi)置對象別名關(guān)系。由于內(nèi)置對象別名關(guān)系的本質(zhì)在于不同對象內(nèi)部的共享內(nèi)存單元,相較于已有別名關(guān)系識別方法,我們將內(nèi)置對象數(shù)據(jù)結(jié)構(gòu)特征轉(zhuǎn)化為對應(yīng)的數(shù)據(jù)結(jié)構(gòu)特征圖,根據(jù)數(shù)據(jù)結(jié)構(gòu)特征圖狀態(tài)的變化來高效準確地識別內(nèi)置對象別名關(guān)系。

在該節(jié)中,我們著重表述如何依據(jù)數(shù)據(jù)結(jié)構(gòu)特征圖識別內(nèi)置對象別名關(guān)系。首先,對于腳本引擎中的每個內(nèi)置對象,按照3.2 節(jié)中描述的方法,獲取對應(yīng)的數(shù)據(jù)結(jié)構(gòu)特征圖。當出現(xiàn)對象別名關(guān)系時,不同對象對應(yīng)的數(shù)據(jù)結(jié)構(gòu)特征圖之間將出現(xiàn)新的連通節(jié)點,對應(yīng)別名關(guān)系中的共享內(nèi)存單元。接下來,依據(jù)建立對象別名關(guān)系前后,對象數(shù)據(jù)結(jié)構(gòu)特征圖的變化來具體說明如何識別對象別名關(guān)系。以Stream 類型和ReadStream 類型的內(nèi)置對象為例,在建立別名關(guān)系之前,其數(shù)據(jù)結(jié)構(gòu)特征圖如圖10 所示。

在圖10 中左半部分Obj1_Id 標識的數(shù)據(jù)結(jié)構(gòu)特征圖代表Stream 類型的內(nèi)置對象,右半部分Obj2_Id標識的數(shù)據(jù)結(jié)構(gòu)特征圖代表ReadStream 類型的內(nèi)置對象,Obj1_Id 以及Obj2_Id 分別為內(nèi)置對象運行時的內(nèi)存地址。由于執(zhí)行腳本引擎API 引起了內(nèi)置對象的變化,需要重新計算內(nèi)置對象對應(yīng)的數(shù)據(jù)結(jié)構(gòu)特征圖。當執(zhí)行完某條腳本引擎API 之后,Stream 對象和ReadStream 對應(yīng)數(shù)據(jù)結(jié)構(gòu)特征圖的變化如圖11所示。

在圖11 中用我們虛線框代表別名關(guān)系對應(yīng)的節(jié)點,即別名關(guān)系對應(yīng)的共享內(nèi)存單元。我們采用圖12 中描述的算法,通過對比腳本引擎API 執(zhí)行前后對象數(shù)據(jù)結(jié)構(gòu)特征圖的狀態(tài)變化,識別內(nèi)置對象別名關(guān)系。

我們設(shè)置監(jiān)控粒度為單條腳本語句,如圖12 所示,首先我們初始化最終輸出的別名關(guān)系內(nèi)置對象集合R為空集。在執(zhí)行腳本引擎API 前,之后,依據(jù)內(nèi)置對象內(nèi)存地址集合S,計算對應(yīng)的對象數(shù)據(jù)結(jié)構(gòu)特征圖集GA(對應(yīng)算法中步驟2,3,4)。當執(zhí)行完某條腳本語句后,再次計算內(nèi)置對象數(shù)據(jù)結(jié)構(gòu)特征圖集,記為GB。最后,以對象內(nèi)存地址為索引,對比先后兩次生成的對象數(shù)據(jù)結(jié)構(gòu)特征圖,根據(jù)數(shù)據(jù)結(jié)構(gòu)特征圖的變化來識別內(nèi)置對象別名關(guān)系。

當建立內(nèi)置對象別名關(guān)系時,共享內(nèi)存單元對應(yīng)的數(shù)據(jù)結(jié)構(gòu)特征圖節(jié)點(圖11 中虛線框節(jié)點)的祖先屬性將發(fā)生變化。因為共享節(jié)點可以歸屬于不同內(nèi)置對象,所以該節(jié)點的祖先屬性將增多(對應(yīng)圖12中步驟11)。我們依據(jù)腳本引擎API 執(zhí)行前后是否存在數(shù)據(jù)結(jié)構(gòu)特征圖節(jié)點的祖先屬性符合上述變化,來判斷是否建立了內(nèi)置對象別名關(guān)系。

通常,在單個測試樣本中,涉及的腳本引擎內(nèi)置對象數(shù)量較少,因此每次重新計算數(shù)據(jù)結(jié)構(gòu)特征圖的性能開銷較小。相比于已有的別名關(guān)系識別方法,我們提出的基于數(shù)據(jù)結(jié)構(gòu)特征的方法具有更高的識別效率。

5 利用別名關(guān)系檢測釋放后使用漏洞

內(nèi)置對象別名關(guān)系有助于檢測與對象內(nèi)存操作相關(guān)的程序漏洞。通過建立內(nèi)置對象別名關(guān)系,并搭配合適的腳本引擎API 釋放內(nèi)置對象,可以在多個對象內(nèi)部引入懸掛指針,進而觸發(fā)釋放后使用漏洞。該部分具體分為三個部分進行介紹,分別為釋放后使用漏洞的檢測流程,提取特殊API 序列以及生成測試樣本檢測釋放后使用漏洞。

5.1 釋放后使用漏洞的檢測流程

在釋放后使用漏洞中,根本原因是訪問了被釋放內(nèi)存區(qū)域,從而觸發(fā)程序錯誤。對于釋放后使用漏洞,必不可少的環(huán)節(jié)為釋放對象產(chǎn)生懸掛指針以及訪問懸掛指針。同時,必須遵循釋放操作在前,使用操作在后的原則,否則無法觸發(fā)釋放后使用漏洞。因此,需要針對內(nèi)置對象提取釋放API 序列以及使用API 序列,并基于提取的API 序列生成測試樣本以檢測釋放后使用漏洞,整個系統(tǒng)的具體檢測流程如圖13 所示。

整個檢測流程按從左到右的順序執(zhí)行,核心在于利用內(nèi)置對象別名關(guān)系。為了提取釋放對象API序列以及使用對象API 序列,需要使用不同的判斷邏輯。進一步,利用提取出的釋放序列和使用序列生成測試樣本以檢測腳本引擎中的程序漏洞。

對于建立的別名關(guān)系的兩個內(nèi)置對象(記為內(nèi)置對象A以及內(nèi)置對象B),分別獲取API 測試樣本運行時的對象內(nèi)存地址。進一步,依據(jù)對象內(nèi)存地址以及對象數(shù)據(jù)結(jié)構(gòu)特征,獲取別名內(nèi)存單元對應(yīng)的內(nèi)存地址,又稱為別名地址。我依據(jù)API 測試樣本運行時是否滿足設(shè)定的內(nèi)置對象釋放條件來提取釋放對象API 序列(具體條件在4.2 節(jié)中進行描述)。在提取使用對象API 序列時,我們針對別名地址設(shè)置內(nèi)存訪問斷點。通過監(jiān)控測試樣本運行過程中內(nèi)存斷點的觸發(fā)情況,獲取使用對象API 序列。

最后,需要按照一定模式組合釋放對象API 序列和使用對象API序列,并在釋放對象API序列執(zhí)行前建立內(nèi)置對象別名關(guān)系,以檢測腳本引擎中的釋放后使用漏洞。

5.2 提取特殊API 序列

在本節(jié)中,我們主要敘述如何提取釋放對象API 序列以及使用對象API 序列,提取過程對應(yīng)的具體流程如圖14 所示。我們首先使用動態(tài)插裝技術(shù)獲得內(nèi)置對象內(nèi)存地址,之后結(jié)合建立的對象別名關(guān)系獲得別名單元的內(nèi)存地址。根據(jù)測試樣本運行過程中,別名單元內(nèi)存地址是否滿足特定釋放/使用模式,提取對應(yīng)的釋放/使用API 序列。

5.2.1 提取釋放API 序列

首先介紹釋放對象API 序列的提取,我們著重關(guān)注對象內(nèi)部共享內(nèi)存區(qū)域的釋放情況,通過判定對象內(nèi)部共享內(nèi)存區(qū)域是否被釋放,來提取釋放對象API 序列。我們具體使用3 種檢查策略來判定共享內(nèi)存區(qū)域是否被釋放,分別為判定別名區(qū)域中數(shù)據(jù)的狀態(tài),判定內(nèi)置對象存活性以及判定系統(tǒng)釋放函數(shù)是否觸發(fā)。

我們將根據(jù)3.2 節(jié)中提取的API 參數(shù)信息,隨機生成API 調(diào)用序列。通過歸納已知釋放對象API 序列的特征,在填充API 參數(shù)時,盡量選擇空值作為參數(shù),如長度為0 的字符串,數(shù)字0 等。除此之外,還可以利用腳本引擎垃圾回收機制釋放內(nèi)置對象。通過消除內(nèi)置對象的引用或引入臨時變量的方式可以影響對象生命周期,如將指向內(nèi)置對象的變量指向空對象,或不使用變量存儲API 返回值,進而觸發(fā)腳本引擎垃圾回收機制釋放內(nèi)置對象。

在第一種檢查策略中,我們根據(jù)別名區(qū)域中數(shù)據(jù)的狀態(tài)來判定對象是否被釋放。當內(nèi)置對象被系統(tǒng)回收時,對象內(nèi)存區(qū)域中的數(shù)據(jù)被填充為特殊值,該特殊值可通過簡單人工分析獲得。根據(jù)別名地址中的數(shù)據(jù)是否等于該特殊值,可以判斷別名區(qū)域是否被釋放,進而判定API 序列是否觸發(fā)了釋放行為。

第二種檢查策略直接判斷內(nèi)置對象存活性。在第2章中我們介紹了活躍對象集合的概念,通過檢查內(nèi)置對象是否從活躍對象集合消失來判定API 序列是否觸發(fā)了釋放行為。

第三種檢查策略為監(jiān)控系統(tǒng)釋放函數(shù)。腳本引擎本質(zhì)為操作系統(tǒng)中的進程,一切內(nèi)存操作均需調(diào)用操作系統(tǒng)的底層函數(shù),即使用API 創(chuàng)建和釋放內(nèi)置對象的同時,也調(diào)用了操作系統(tǒng)的創(chuàng)建及釋放函數(shù)。對于操作系統(tǒng)底層的釋放函數(shù),其參數(shù)為需要釋放的內(nèi)存地址。可以通過監(jiān)控底層釋放函數(shù)的調(diào)用,判斷參數(shù)值是否等于對象別名單元內(nèi)存地址,決定當前API 序列是否觸發(fā)了釋放行為。

在提取釋放對象API 序列的過程中,我們依舊選擇單個腳本引擎API 作為插裝粒度,并在單條API調(diào)用語句前后加入檢查點。每當遭遇檢查點時,需要實施上述三種檢查策略,判斷當前情景下是否發(fā)生了內(nèi)置對象釋放行為。同時,可以在單個測試樣本中同時監(jiān)控多個存在別名關(guān)系的內(nèi)置對象,提高測試效率。

5.2.2 提取使用API 序列

在提取使用對象API 序列時,無需關(guān)注對象的狀態(tài),即對象存活與否與是否能訪問別名區(qū)域中的數(shù)據(jù)無直接關(guān)系。雖然,正常的使用規(guī)范要求對象被釋放后無法訪問對象內(nèi)部數(shù)據(jù)。但是,當內(nèi)置對象被釋放后仍能成功訪問其內(nèi)部數(shù)據(jù),說明該內(nèi)置對象未設(shè)置相應(yīng)的釋放標志位,因此更容易產(chǎn)生懸掛指針。綜上,在提取使用對象API 序列時,僅關(guān)心對象內(nèi)部的別名區(qū)域是否能被成功訪問。

接下來介紹如何提取使用API 序列。為了提取使用API 序列,在生成API 測試樣本時,將待測內(nèi)置對象作為API 參數(shù)使用以滿足內(nèi)置對象訪問條件。我們利用Windbg 調(diào)試工具提供的內(nèi)存讀寫監(jiān)控功能來判斷當前API 序列是否能夠訪問特定內(nèi)置對象。通過將內(nèi)置對象內(nèi)部別名單元內(nèi)存地址設(shè)置為讀斷點,依據(jù)API 測試樣本運行過程中是否觸發(fā)了讀斷點,提取對應(yīng)的使用API 序列。我們針對每一對存在別名關(guān)系的內(nèi)置對象,分別提取對應(yīng)的使用API 序列。

5.3 生成測試樣本檢測釋放后使用漏洞

為了檢測腳本引擎中的釋放后使用漏洞,需要構(gòu)造特殊的腳本引擎API 調(diào)用序列以滿足釋放后使用漏洞的模式,對應(yīng)到腳本引擎中為先調(diào)用釋放對象API 序列,后調(diào)用使用對象API 序列。由于單懸掛指針導(dǎo)致的釋放后使用漏洞容易被修補,需要引入內(nèi)置對象別名關(guān)系以創(chuàng)建多懸掛指針,具體的測試樣本生成策略如圖15 所示。

對于存在別名關(guān)系的內(nèi)置對象(記為對象A以及對象B),我們分別搭配對應(yīng)的釋放API 序列集合以及使用API序列集合,如圖15中對象A搭配釋放API序列集合,對象B搭配使用API 序列集合。按照第2章中介紹的別名關(guān)系釋放后使用漏洞觸發(fā)模式,我們選取釋放API 序列集合和使用API 序列集合中的元素進行組合,并遵循釋放在前使用在后的順序,生成不同測試樣本。同時,在執(zhí)行釋放API 序列之前需要先建立內(nèi)置對象別名關(guān)系。

在實驗中我們發(fā)現(xiàn),為了提高內(nèi)置對象間別名關(guān)系的出現(xiàn)概率,需要腳本引擎API 間需要具有較強的關(guān)聯(lián)性。通過提取API 參數(shù)和返回值的內(nèi)存訪問模式,可分別獲得參數(shù)以及返回值的候選對象列表,記為Param_Set 以及Return_Set,而不同腳本引擎API 之間Param_Set 和Return_Set 存在交集。在生成具體測試樣本時,我們考慮將腳本引擎API 的返回值作為另一API 的參數(shù),以增加API 之間的關(guān)聯(lián)性。同時,為了保證合理語義,如API-1 的Param_Set與API-2的Return_Set存在交集,在生成測試樣本時,將優(yōu)先調(diào)用API-2 創(chuàng)建內(nèi)置對象,再將API-2 的返回值作為API-1 的參數(shù)。最后,將產(chǎn)生的測試樣本輸入腳本引擎,以檢測腳本引擎中的釋放后使用漏洞。

6 實驗結(jié)果評估

本章中,我們針對論文中提出的內(nèi)置對象別名關(guān)系識別方法設(shè)計多項評估實驗,分別從別名關(guān)系準確率,測試樣本生成效率以及別名關(guān)系識別開銷三個方面進行評估。同時,對于提出的釋放后使用漏洞檢測方法,我們在真實軟件Adobe Reader 內(nèi)嵌JavaScript 引擎上實施了漏洞檢測實驗,評估該方法檢測釋放后使用漏洞的性能,驗證該方法的有效性和可用性。

6.1 別名關(guān)系準確率

論文的一個核心點在于利用對象數(shù)據(jù)結(jié)構(gòu)特征建立并識別內(nèi)置對象別名關(guān)系,因此需要評估內(nèi)置對象別名關(guān)系的準確性。

為了評估測試樣本中內(nèi)置對象別名關(guān)系是否準確,我們生成了長度相同的測試樣本,分批識別其中的內(nèi)置對象別名關(guān)系。對于每個待測樣本,依據(jù)提取出的腳本引擎API 參數(shù)類型信息,生成100 條腳本引擎API 調(diào)用語句,并動態(tài)監(jiān)測樣本運行過程中是否建立了內(nèi)置對象間別名關(guān)系。最終,我們統(tǒng)計了100 個測試樣本的運行結(jié)果,總計包含18354 條別名關(guān)系記錄,平均每個測試樣本包含183 條別名關(guān)系,平均每條腳本引擎API 生成了1.8 個內(nèi)置對象別名關(guān)系。通過對18354 條別名關(guān)系記錄進行去重,我們最終提取出284 條不同的內(nèi)置對象別名關(guān)系,共涉及27 個不同類型的內(nèi)置對象,占動態(tài)內(nèi)置對象比例的51%。

我們采用圖16 中的模式來評估內(nèi)置對象別名關(guān)系識別的準確性。針對一組待驗證別名關(guān)系的內(nèi)置對象,變更其中一個內(nèi)置對象的狀態(tài),如改變對象屬性值等,并監(jiān)控另一內(nèi)置對象的變化。依據(jù)待驗證別名關(guān)系的內(nèi)置對象狀態(tài)是否同步變化,來判定內(nèi)置對象別名關(guān)系是否準確。

存在別名關(guān)系的兩個內(nèi)置對象分別記為ObjA和ObjB,并將ObjA對象與ObjB對象最終存在別名關(guān)系的內(nèi)存單元抽象為ObjA.X.Y以及ObjB.E.F(對應(yīng)圖16 中共享內(nèi)存單元M)。初始時,共享內(nèi)存單元M中存儲的數(shù)值為1。我們使用腳本引擎API 對ObjA.X.Y進行賦值操作,更改M中的數(shù)值為2。之后,通過ObjB.E.F讀取M中存儲的數(shù)值,并判斷讀取的數(shù)值是否等于更改后的數(shù)值。當讀取的數(shù)值為1 時,我們認為識別ObjA對象和ObjB對象間別名關(guān)系時產(chǎn)生了誤報;當讀取的數(shù)值為2 時,我們認為對象別名關(guān)系識別正確。我們采用如上方式對提取出的284 條內(nèi)置對象別名關(guān)系進行了評估,并人工審查了評估結(jié)果。最終,我們證明了提取出的284 條內(nèi)置對象別名關(guān)系真實存在,即提出的內(nèi)置對象別名關(guān)系識別方法沒有產(chǎn)生誤報。

同樣,我們采用圖16中的模式來評估文中提出的別名關(guān)系識別方法是否存在漏報現(xiàn)象。我們將測試范圍設(shè)置為全體內(nèi)置對象,即腳本引擎中存活的所有靜態(tài)內(nèi)置對象以及動態(tài)內(nèi)置對象。通過運行測試腳本代碼,并統(tǒng)計不同狀態(tài)下腳本引擎中存在的所有內(nèi)置對象別名關(guān)系,以此作為對比的標準。在實驗過程中,針對全體內(nèi)置對象別名關(guān)系進行多次統(tǒng)計,排除內(nèi)置對象隨機初始化數(shù)值引發(fā)的干擾。在統(tǒng)計多次實驗結(jié)果后,平均每個測試樣本中包含總計196條內(nèi)置對象別名關(guān)系,使用論文中的別名關(guān)系識別方法能識別出183條內(nèi)置對象別名關(guān)系,最終漏報率約6.6%。

進一步,我們將本文中提出的別名關(guān)系識別方法與現(xiàn)有研究中的別名關(guān)系識別方法進行對比,比較不同方法在識別腳本引擎內(nèi)置對象別名關(guān)系時的性能差異。我們選用Pintool 工具[31]實現(xiàn)現(xiàn)有研究中的別名關(guān)系識別方法[32],核心思想為監(jiān)測程序運行過程中的內(nèi)存讀寫操作。當監(jiān)測到內(nèi)存寫指令涉及的不同操作數(shù)分別屬于不同內(nèi)置對象時,認為出現(xiàn)了內(nèi)置對象別名關(guān)系。在保持測試樣本一致的前提下,我們分別統(tǒng)計了相同時間內(nèi),不同別名關(guān)系識別方法的相關(guān)指標,包括別名關(guān)系數(shù)量、誤報率及漏報率,同時將測試樣本中別名關(guān)系總數(shù)作為對照標準,最終統(tǒng)計結(jié)果如表2 所示。

表2 別名關(guān)系識別方法對比Table 2 Comparison of different alias relationship recognition method

從結(jié)果中可見,在測試時長為10 min 時,使用傳統(tǒng)別名關(guān)系識別方法,總共識別出17 組內(nèi)置對象別名關(guān)系,漏報率為88%,而使用論文中提出的基于數(shù)據(jù)結(jié)構(gòu)特征的別名關(guān)系識別方法能夠識別出137組別名關(guān)系,漏報率為6%,明顯優(yōu)于傳統(tǒng)方法。同樣,我們分別測試了30 min 以及60 min 條件下,不同別名關(guān)系識別方法所能識別出的別名關(guān)系數(shù)量,最終證明了論文中的別名關(guān)系識別方法相較于傳統(tǒng)方法有較大優(yōu)勢。

6.2 測試樣本生成效率

本節(jié)中,我們主要評估使用對象數(shù)據(jù)結(jié)構(gòu)特征對于生成測試樣本的影響。當腳本引擎API 參數(shù)為對象類型,生成測試樣本時搜索空間的大小與API參數(shù)候選集的大小成正比。在不提取細粒度參數(shù)類型的情況下,API 參數(shù)候選集的大小等于腳本引擎中所有內(nèi)置對象的個數(shù)。依據(jù)統(tǒng)計結(jié)果,平均一個腳本引擎API 需要3 個對象參數(shù),而腳本引擎中共包含237 個API 以及163 種內(nèi)置對象,則生成測試樣本時最終搜索空間大小為237*163*163*163。我們通過實驗發(fā)現(xiàn),不同腳本引擎API 需要不同細粒度類型的內(nèi)置對象作為參數(shù),而傳遞其余類型的內(nèi)置對象將引發(fā)運行時錯誤。通過提取腳本引擎API 參數(shù)的內(nèi)存訪問模式,結(jié)合內(nèi)置對象數(shù)據(jù)結(jié)構(gòu)特征,縮小了腳本引擎API 對象參數(shù)的候選集大小。

我們分析并提取了每個腳本引擎API 的細粒度參數(shù)信息,最終確定了每個腳本引擎API 中對象參數(shù)的候選集大小,最終的統(tǒng)計結(jié)果如圖17 所示。根據(jù)圖17 中的統(tǒng)計結(jié)果可以發(fā)現(xiàn),約80%腳本引擎API 的參數(shù)候選集的大小不超過6。通過提取腳本引擎API 細粒度參數(shù)信息,能明顯優(yōu)化生成測試樣本時的搜索空間,大幅減少待測樣本個數(shù)(從237*163*163*163 縮小為237*6*6*6),提高了測試效率。

除此之外,提取腳本引擎API 細粒度參數(shù)信息對于減少運行時錯誤有明顯助益。我們分別在使用API 細粒度參數(shù)信息與不使用的情況下,生成相同數(shù)量的測試樣本,并統(tǒng)計測試樣本運行過程中產(chǎn)生的運行時錯誤。通過對比運行時錯誤數(shù)量,評估腳本引擎API 細粒度參數(shù)信息對于減少運行時錯誤的效果,具體統(tǒng)計結(jié)果如表3 中所示。

表3 運行時錯誤數(shù)量Table 3 Number of runtime errors

在表3 中,我們分別生成了100 個,200 個,300個以及400 個測試樣本,并統(tǒng)計產(chǎn)生運行時錯誤的樣本數(shù)量。在使用API 參數(shù)細粒度信息時,存在運行時錯誤的樣本數(shù)量分別為17 個,32 個,46 個以及57個,而在不使用API 細粒度參數(shù)信息時,運行時錯誤的數(shù)量分別為73 個,153 個,231 個以及316 個??梢园l(fā)現(xiàn),使用API 細粒度類型信息生成的測試樣本中,包含的運行時錯誤數(shù)量明顯少于不使用細粒度參數(shù)信息。因此,可以證明提取腳本引擎API 細粒度信息對于減少運行時錯誤,提高測試樣本質(zhì)量有明顯幫助。

6.3 別名關(guān)系識別開銷

為了評估論文中別名關(guān)系識別方法的高效性,即相較于已有別名關(guān)系識別方法,我們提出的別名關(guān)系識別方法具有較短的檢測時長。我們生成了包含隨機數(shù)量腳本語言代碼的測試樣本,統(tǒng)計了測試樣本在不識別別名關(guān)系,使用傳統(tǒng)別名識別方法以及基于對象數(shù)據(jù)結(jié)構(gòu)特征識別別名關(guān)系三種場景下的運行時長,取單條腳本語言代碼平均運行時長作為性能評估的依據(jù)。

傳統(tǒng)別名關(guān)系識別通過監(jiān)控內(nèi)存數(shù)據(jù)的方式進行實現(xiàn),具體為監(jiān)控程序運行過程中的內(nèi)存讀寫指令,根據(jù)讀寫指令涉及的操作數(shù)來識別對象別名關(guān)系。內(nèi)存讀指令用于標明操作數(shù)與對象的從屬關(guān)系,內(nèi)存寫指令用于判斷對象別名關(guān)系。當內(nèi)存寫指令涉及的兩個操作數(shù)分別來自不同對象時,認為檢測到對象別名關(guān)系。我們總計測試了100 組數(shù)據(jù),結(jié)果如圖18 所示。

在圖18 中,運行耗時以秒為單位。在不識別內(nèi)置對象別名關(guān)系時,單條腳本語言代碼的平均運行時長約為1 s。使用傳統(tǒng)別名識別方法時,單條腳本語言代碼的平均運行時長為269 s,相比于不識別別名關(guān)系,其運行耗時明顯增加。采用我們提出的基于對象數(shù)據(jù)結(jié)構(gòu)特征的別名關(guān)系識別方法,單條腳本語言代碼的平均運行時長為13 s。雖然相比于不進行別名分析運行時長有所上升,但額外提取了內(nèi)置對象別名關(guān)系,且性能開銷仍在可接受范圍。同時,相比于傳統(tǒng)別名關(guān)系識別方法能顯著縮短運行時長。因此,我們提出的基于對象數(shù)據(jù)結(jié)構(gòu)特征的別名關(guān)系識別方法,能快速檢測腳本引擎內(nèi)置對象別名關(guān)系,提升了別名關(guān)系識別效率。

6.4 別名關(guān)系識別方案的通用性

在軟件內(nèi)嵌腳本引擎中,為了方便用戶,定義了不同結(jié)構(gòu)的內(nèi)置對象與不同功能的腳本引擎API,搭配使用腳本引擎API 與內(nèi)置對象可實現(xiàn)多種復(fù)雜功能。根據(jù)我們的調(diào)查結(jié)果,腳本引擎中的安全漏洞常與內(nèi)置對象緊密相關(guān),究其原因在于內(nèi)置對象的內(nèi)部結(jié)構(gòu)復(fù)雜,且可通過腳本引擎API 建立深層次聯(lián)系,使得管理和維護內(nèi)置對象具有較大難度。在本文中,為了檢測腳本引擎中的深層次安全漏洞,我們將重心放在識別內(nèi)置對象別名關(guān)系。進一步,為了證明論文中別名關(guān)系識別方法的通用性,我們設(shè)計了如下實驗。

在軟件內(nèi)嵌腳本引擎中,除了具有特殊結(jié)構(gòu)的內(nèi)置對象,還存在用戶自定義對象,即由用戶自定義對象內(nèi)部結(jié)構(gòu)及屬性。我們采用包含自定義對象的數(shù)據(jù)集,并使用論文中方法識別內(nèi)置對象別名關(guān)系。進一步,使用6.1 節(jié)中描述的方法來驗證別名關(guān)系的準確性,最終實驗結(jié)果如表4 所示。

表4 自定義對象別名關(guān)系識別統(tǒng)計表Table 4 Statistics of custom object alias relationship

如表4 所示,在所使用的數(shù)據(jù)集中,總共包含對象別名關(guān)系153,165,138 以及144 組,其中自定義對象別名關(guān)系總數(shù)為36,43,25 以及31 組,而使用論文中的方法共能識別出自定義對象別名關(guān)系33,39,23以及29。進一步我們計算了自定義對象別名關(guān)系識別的漏報率以及誤報率,其中誤報率采用6.1 節(jié)中方法計算,最終證明無誤報現(xiàn)象,而漏報率分別為8.3%,9.3%,8%以及6.4%,證明了論文中方法能以較高效率識別自定義對象別名關(guān)系,從而證明方法的通用性。

6.5 釋放后使用漏洞檢測結(jié)果

在本節(jié)中,我們以發(fā)現(xiàn)的未知釋放后使用漏洞為例,介紹具體的檢測思路。首先是漏洞編號“CVE-2020-3745”的釋放后使用漏洞,具體漏洞代碼如圖19 所示。

通過調(diào)用腳本引擎API util.streamFromString 創(chuàng)建了Stream 類型的內(nèi)置對象,記為ObjA對象,對應(yīng)圖19 中第①行。在圖19 第②行中,調(diào)用了腳本引擎API Collab.drivers.getInitiatorSource 并將ObjA對象作為參數(shù),創(chuàng)建了ReadStream 類型的內(nèi)置對象,記為ObjB。同時,該API 也建立了ObjA對象和ObjB對象間的別名關(guān)系。之后,通過調(diào)用API this.reset-Form 釋放了ObjB對象。由于ObjA對象和ObjB對象存在別名關(guān)系,導(dǎo)致ObjA對象內(nèi)部出現(xiàn)了懸掛指針。在圖19 第④行中,通過API util.stringFrom-Stream 訪問了ObjA對象內(nèi)部的懸掛指針,觸發(fā)了釋放后使用漏洞。我們將上述漏洞的觸發(fā)模式總結(jié)為圖20。

通過使用論文中提出的方法,總計檢測出4 個未知的釋放后使用漏洞,現(xiàn)將每個未知釋放后使用漏洞對應(yīng)的漏洞編號,及其涉及的具體腳本引擎內(nèi)置對象信息總結(jié)如表5 所示。

表5 檢測出的釋放后使用漏洞列表Table 5 List of detected UAF vulnerabilities

7 總結(jié)與展望

在腳本引擎漏洞挖掘領(lǐng)域,利用模糊測試來檢測漏洞的方法已經(jīng)被證明有效,但現(xiàn)有的模糊測試方法主要關(guān)注如何生成符合腳本語言語法規(guī)范的測試樣本,未能很好地利用腳本引擎API 以及內(nèi)置對象,在挖掘腳本引擎深層次漏洞方面未能取得較好的效果。

針對上述局限性,本研究提出了一種基于數(shù)據(jù)結(jié)構(gòu)特征的腳本引擎內(nèi)置對象別名關(guān)系識別技術(shù),通過快速識別內(nèi)置對象別名關(guān)系來輔助檢測釋放后使用漏洞。與傳統(tǒng)腳本引擎漏洞檢測方案不同,我們重點關(guān)注腳本引擎中的特殊API 及內(nèi)置對象,采取自動化的方式提取內(nèi)置對象數(shù)據(jù)結(jié)構(gòu)特征,并基于提取出的對象特征識別腳本引擎API 細粒度參數(shù)信息。進一步,搭配合理API 參數(shù),我們可以生成高質(zhì)量的測試樣本,提高內(nèi)置對象別名關(guān)系的出現(xiàn)概率。同時,我們利用對象數(shù)據(jù)結(jié)構(gòu)特征快速準確地識別內(nèi)置對象別名關(guān)系,大幅縮短了現(xiàn)有別名分析技術(shù)所需時間。最后,利用識別的內(nèi)置對象別名關(guān)系,搭配特定API 序列有針對性地釋放和使用內(nèi)置對象,構(gòu)造性地檢測腳本引擎中的釋放后使用漏洞。最終,我們提取出284 組內(nèi)置對象別名關(guān)系,并據(jù)此檢測出4 個未知的釋放后使用漏洞。

誠然,本方法也存在諸多不足和需要繼續(xù)改進的地方。首先,在提取腳本引擎API 細粒度參數(shù)信息時,現(xiàn)有的靜態(tài)分析方法在處理間接函數(shù)調(diào)用時粒度較粗,提取的參數(shù)信息不夠精確,后續(xù)可針對識別精度進行改進,進一步縮小腳本引擎API 參數(shù)候選集的大小,實現(xiàn)精確的API 參數(shù)細粒度類型識別。

其次,生成測試樣本時,對于如何填充腳本引擎API 的參數(shù)考慮不足,如針對基礎(chǔ)類型只是簡單地選取隨機常量值,對象類型僅考慮了參數(shù)候選集中的元素。在后續(xù)工作中,可以更深入地探究腳本引擎API的執(zhí)行邏輯,探究參數(shù)對于API執(zhí)行軌跡的影響,從而更針對性地提供API 所需參數(shù)。除此之外,使用內(nèi)置對象別名關(guān)系檢測釋放后使用漏洞時,提取的內(nèi)置對象別名關(guān)系數(shù)量及釋放API 序列數(shù)量較少。對于這一問題,可以通過加強腳本引擎API 的關(guān)聯(lián)性,以及改進監(jiān)控策略,來發(fā)現(xiàn)更多的釋放API 序列。

最后,本工作未在JavaScript 以外的腳本引擎中進行實驗,后續(xù)考慮將實驗移植到JavaScript 語言以外的腳本引擎中,進一步證明方法的通用性。在其他腳本引擎中,可通過分析對象內(nèi)部結(jié)構(gòu),來識別對象內(nèi)部別名關(guān)系,進而檢測安全漏洞。

猜你喜歡
腳本數(shù)據(jù)結(jié)構(gòu)內(nèi)置
數(shù)據(jù)結(jié)構(gòu)線上線下混合教學模式探討
內(nèi)置加勁環(huán)T型管節(jié)點抗沖擊承載力計算
重典型應(yīng)用,明結(jié)構(gòu)關(guān)系
自動推送與網(wǎng)站匹配的腳本
內(nèi)置電子游戲的運動鞋
友誼的小船說翻就翻
舉一反三新編
外設(shè)天地行情
愚公移山
數(shù)據(jù)結(jié)構(gòu)與算法課程設(shè)計教學模式的探討
靖边县| 青州市| 高雄县| 永安市| 南雄市| 厦门市| 卢湾区| 焉耆| 天津市| 社旗县| 淮安市| 石阡县| 四平市| 靖西县| 普兰店市| 乡城县| 岳西县| 监利县| 六枝特区| 长泰县| 古丈县| 水富县| 昭觉县| 精河县| 阿坝县| 丘北县| 富宁县| 顺昌县| 莱州市| 清河县| 永年县| 青神县| 武宁县| 合水县| 县级市| 大冶市| 高碑店市| 都匀市| 武强县| 西峡县| 遂平县|