何本偉,郭云飛,梁浩,王慶豐
(信息工程大學信息技術研究所,河南 鄭州 450001)
計算機軟件時刻影響著人們的生活,廣泛應用于通訊、銀行、醫(yī)療、交通控制等重要領域。然而,由于當前軟件開發(fā)與分發(fā)機制中存在“單一文化主義”[1-2]現(xiàn)象,導致相同的程序和代碼運行在數(shù)百萬甚至數(shù)億臺計算機上,這種軟件同構性為攻擊者實施大規(guī)模攻擊提供了極大便利,給系統(tǒng)安全、網(wǎng)絡安全、隱私保護等帶來了極大的隱患,已成為當前軟件發(fā)展中亟待解決的問題。
早在1993 年,COHEN[3]就已發(fā)現(xiàn)軟件的分發(fā)機制存在重大安全隱患,并提出軟件多樣化思想,即隨機化程序的內(nèi)存布局,生成功能語義相同但內(nèi)在結構不同的軟件,以抵御大規(guī)模攻擊。近年來,隨著代碼重用攻擊(如Ret2libc、ROP、JOP 等)[4]的興起,軟件多樣化技術使得攻擊者獲得的有關程序先驗知識失效,攻擊者無法獲得足夠多且有效的以跳轉(zhuǎn)結尾的指令片段(gadgets)以構造攻擊負載,從而有效防御了代碼重用攻擊,因此,軟件多樣化技術受到研究人員的廣泛關注。軟件多樣化的典型應用是地址空間布局隨機化(ASLR)[5-6]防御機制,在程序加載時隨機化程序棧、堆、共享庫等的基地址,對攻擊者隱藏程序的內(nèi)存布局信息,使攻擊者已有的先驗知識失效。目前,ASLR 已被廣泛部署到Windows、Linux、iOS 等現(xiàn)代主流的操作系統(tǒng)中,以防御攻擊者獲得程序信息進行大規(guī)模軟件攻擊。然而,ASLR 隨機化粒度僅到頁一級,可以通過暴力破解[7]或信息泄漏[8]來突破ASLR 防御。
為提高隨機化破解難度,研究人員針對源代碼和二進制代碼提出了多種更細粒度的軟件多樣化技術。文獻[9]通過比較指令替換、偽控制流等6 種多樣化技術對gadgets 存活率的影響,評估不同多樣化技術對ROP 攻擊的防御效果。文獻[10]提出一種全面的基于編譯器的軟件多樣化方法,允許實時重新隨機化內(nèi)存布局以防御JIT-ROP 攻擊,然而每間隔2 s 重新隨機化會增加大約20%的開銷。文獻[11-13]基于編譯器進行類似的NOP 指令插入,該方法在每條指令前以一定概率插入NOP 指令,打亂gadgets 的位置,但是其基本塊和函數(shù)順序保持不變。文獻[14]通過側(cè)信道攻擊推測NOP 指令插入概率,能夠?qū)Χ鄻踊蟮淖凅w進行去隨機化處理。相關的開源多樣化編譯工具也相當成熟,如Obfuscator-LLVM、Tigress[15-16]等。文獻[17]提出一種針對二進制代碼的重隨機化方法,在運行時每隔一段時間觸發(fā)隨機化,由于函數(shù)指針識別困難,因此其只對函數(shù)內(nèi)部基本塊進行重排序。文獻[18]針對PE 文件提出就地隨機化技術,通過對代碼段指令進行小規(guī)模轉(zhuǎn)換以降低開銷,轉(zhuǎn)換后gadgets 的存活率高達30%左右,攻擊者依然有很大可能利用存活的gadgets 構建攻擊負載。文獻[19]借助Intel VT 硬件虛擬化技術實現(xiàn)了運行時代碼隨機化方法,在程序進行讀寫類操作時,會觸發(fā)對二進制代碼的再隨機化處理,能夠有效防御代碼重用攻擊,但是目前該方法僅支持C 語言多樣化。文獻[20]提出一種指令級細粒度的隨機化方法,在靜態(tài)分析階段對程序進行分析和重新組裝,利用二進制重寫技術隨機化虛擬地址空間中幾乎所有指令的位置,然而該方法不僅會導致13%~16%的高運行開銷,文件大小也會大幅增加。
綜上,現(xiàn)有軟件多樣化方法大多依賴源代碼,直接對二進制代碼進行轉(zhuǎn)換則存在開銷大或安全性低的問題,且二進制反編譯準確性有待提高。針對上述問題,本文提出一種面向二進制代碼的細粒度軟件多樣化方法。該方法通過重寫器反編譯二進制代碼,對其數(shù)據(jù)和指針進行正確識別區(qū)分,打亂代碼段中函數(shù)塊和基本塊內(nèi)的指令順序,以阻止攻擊者獲取程序內(nèi)存信息而實施代碼重用攻擊。
本文提出一種基于二進制重寫的細粒度軟件多樣化方法,其框架如圖1 所示。首先,通過開源二進制重寫器Egalito[21]對目標程序進行反編譯,并將程序提升到其中間語言表示(EIR),獲取函數(shù)、基本塊和指令的相關信息;然后,根據(jù)獲取的信息對代碼段內(nèi)各函數(shù)塊所處的內(nèi)存位置進行重排序;接著,為了進一步提高程序的隨機化熵,提升程序去隨機化難度,同時考慮到細粒度的隨機化會帶來較高的性能開銷,對指令進行依賴性分析,且只對基本塊內(nèi)的指令進行就地隨機化,重新排列指令位置;最后,使用重寫器Egalito 重新編譯修改后的EIR,生成新的變體二進制。
圖1 本文軟件多樣化方法總體架構Fig.1 The overall architecture of the software diversification method in this paper
相比于ASLR 技術,函數(shù)重排序?qū)⒑瘮?shù)塊放在代碼段中的任意位置,為程序提供更細粒度的保護,使攻擊者難以通過暴力破解獲取程序信息。如圖2所示,攻擊者將函數(shù)fun1~fun4 中gadgets 的地址和所需參數(shù)進行拼接,形成攻擊負載,然后通過棧溢出漏洞,將該攻擊負載放入棧中,同時覆蓋棧中某個函數(shù)的返回地址。當程序執(zhí)行時,控制流會被劫持而依次執(zhí)行惡意的gadgets,最終獲取shell 權限。在程序進行函數(shù)重排序多樣化轉(zhuǎn)換后,攻擊者在棧上放置的地址指向其他函數(shù)中的指令片段,如圖2 右側(cè)箭頭所指的fun1'~fun4',攻擊者所預期的gadgets 發(fā)生改變,導致攻擊失敗。
圖2 函數(shù)重排序?qū)adgets 的影響示例Fig.2 Example of the impact of function reorder on gadgets
為了降低函數(shù)重排序中地址重定位的復雜度,本文采用地址無關可代碼(PIC)技術構建目標程序。PIC 使用基寄存器+偏移量的相對地址尋址方式對常量和函數(shù)地址進行操作,函數(shù)塊可以部署到地址空間中的任意位置,從而避免轉(zhuǎn)換后變體地址重定位的問題。通過重寫器Egalito 將二進制代碼轉(zhuǎn)換成其中間語言表示EIR,由于EIR 是低級的IR,轉(zhuǎn)換結果可預測性更強,因此能夠避免在反編譯和重編譯過程中產(chǎn)生錯誤。同時,Egalito 提供大量的EIR 編程接口,對EIR 的轉(zhuǎn)換最終都會在重編譯時體現(xiàn)到生成的變體二進制中,從而完成對.text 段的函數(shù)重排序,生成大量的多樣化變體。
本文首先迭代訪問程序代碼段中的函數(shù),獲得函數(shù)的起始地址、函數(shù)塊的大小等,使得多樣化后函數(shù)在代碼段中的位置不會重疊;然后將原始函數(shù)符號與其順序索引一一對應,通過時間戳設置隨機數(shù)種子,使用shuffle()函數(shù)隨機化初始順序,以確保每次轉(zhuǎn)換后生成的變體代碼段函數(shù)順序不同。函數(shù)重排序?qū)崿F(xiàn)流程如算法1 所示。
算法1二進制代碼函數(shù)重排序
指令重排序是常用的多樣化技術之一,不僅可以改變gadgets 的位置,還能減少非預期gadgets 的數(shù)量。由于X86 架構指令長度是可變的,因此會產(chǎn)生非預期的gadgets,如圖3 所示,指令{lea-0×1(%rax),%edx} 的最后1 個字節(jié)編碼FF 和指令{mov %rax,%rbx }的前2 個字節(jié)編碼48、89 會組成新的匯編指令{FF 48 89},由此產(chǎn)生非預期的gadgets {dec %rax,%rbx,ret },攻擊者可以通過這種拼接方式獲得所需要的gadgets。指令重排序可以通過顛倒指令順序來消除非預期的gadgets,且不影響原始語義。
圖3 指令重排序消除非預期gadgets 的示例Fig.3 Example of instruction reorder eliminating unintended gadgets
編譯器代碼生成階段的實際指令順序主要取決于指令的周期成本和應用的代碼優(yōu)化技術??蓤?zhí)行文件中基本塊內(nèi)的指令順序是多個正確順序之一。為此,本文在基本塊內(nèi)部進行指令重排序,避免整個代碼段指令重排序因指令的不連接性導致運行時高額的性能開銷。相比于函數(shù)重排序,指令重排序能夠生成數(shù)量更多的變體,設目標程序含有n個函數(shù)、m個基本塊,第i個基本塊含有ki條指令,函數(shù)重排序最多可生成的排列方式數(shù)量為:
而指令重排序最多可生成的排列方式數(shù)量為:
一般而言,基本塊內(nèi)含有若干指令,函數(shù)塊內(nèi)含有若干基本塊,可以看出式(2)結果比式(1)大,攻擊者更難以通過暴力破解來對基本塊內(nèi)指令重排序生成的變體進行去隨機化處理,從而避免其重新獲得程序的內(nèi)存布局進行攻擊。
指令重排的原則是不能改變原指令間的依賴關系,否則會影響程序的執(zhí)行結果。因此,本文通過分析指令之間的依賴關系來確保重新排列的正確性。圖4(a)中第4 條指令{lea -0×1(%rax),%r12}中寄存器 rax 的值取決于第 1 條指令{lea -0×8(%rbp),%rax },這種依賴關系被稱為寫后讀入(RAW),指令不能顛倒順序。其他類型的指令關系如圖4(b)所示,包括讀后讀入(RAR)、寫后寫入(WAW)和讀后寫入(WAR),可以看出,只有RAR 不存在依賴關系,指令排序才可以調(diào)換。除此之外,某些指令間還存在隱式依賴,運算類指令如add、sub 等影響標志位變化,運算類指令調(diào)換順序可能會導致運行結果不同。為了保證重排序的正確性,本文采用保守的轉(zhuǎn)換原則,不改變運算指令的順序,依舊默認其有依賴關系。
圖4 指令重排序流程Fig.4 The process of instruction reorder
圖4(a)中指令間的依賴關系可以用圖4(c)所示的有向無環(huán)圖D=G(V,E)表示,其中,節(jié)點V表示指令,有向邊E表示節(jié)點間的依賴關系。對有向無環(huán)圖進行拓撲排序有2 種實現(xiàn)算法,即入度表算法和深度優(yōu)先算法DFS,兩者時間復雜度均為O(V+E)。然而,DFS 生成的序列不具有隨機性,因此,本文利用入度表算法進行基本塊內(nèi)指令的隨機化,以確保能夠生成任意多的變體。入度表算法步驟如下:
步驟1隨機選擇有向無環(huán)圖中一個入度為0的節(jié)點并輸出。
步驟2刪除該節(jié)點及其所有的有向出邊。
步驟3重復步驟1、步驟2,直至有向無環(huán)圖中所有節(jié)點都被輸出。
基本塊內(nèi)指令重排序的實現(xiàn)流程如算法2 所示。
算法2二進制代碼基本塊內(nèi)指令重排序
將上述算法中的指令排序應用于圖4(a),可以得到與圖4(d)語義相同的新指令順序。
為了驗證二進制代碼軟件多樣化后程序的整體性能和多樣化效果,本文對轉(zhuǎn)換后程序的性能和異構度進行實例測試。實驗使用SPEC CPU2006 基準測試集中12 個C/C++程序進行多樣化轉(zhuǎn)換,源程序生成用的是O2 級別優(yōu)化。實驗環(huán)境如表1 所示。
表1 實驗環(huán)境Table 1 Experimental environment
圖5 展示了源程序、函數(shù)重排序(Fun_reorder)、基本塊內(nèi)指令重排序(Ins_reorder)和兩者一起轉(zhuǎn)換(all)后程序的實際運行時間。從圖5 可以看出,僅對源程序進行函數(shù)重排序轉(zhuǎn)換時,平均性能開銷約增加1.1%,最大降低6.0%,這是因為Egalito 重寫器會對二進制進行優(yōu)化,已被證明平均能使SPEC CPU2006 的基準測試速度提高1.7%。由于本文方法僅對基本塊內(nèi)的指令進行重排序,運行開銷約為1.8%,遠小于文獻[18]方案在整個代碼段排序所導致的運行開銷(13%~16%)。同時使用函數(shù)重排序和指令重排序這2 種多樣化方法轉(zhuǎn)換程序,理論上指令地址可以加載到地址空間中的任意位置,隨機化粒度幾乎和文獻[18]方案對所有指令在地址空間重排序相當,而在該安全性下,前者平均性能開銷僅為3.1%,具有較高的實用性。
圖5 多樣化方法對軟件性能的影響Fig.5 The impact of diversification methods on software performance
安全評估主要考慮軟件轉(zhuǎn)換前后的異構度,軟件的異構度越大,攻擊者越難以通過共模漏洞發(fā)起大規(guī)模攻擊。本文利用gadgets 的存活率、模糊哈希Tlsh[22]和開源工具Bindiff[23]這3 個指標,分別從程序代碼片段、哈希值和控制流圖要素的異構度角度來評估軟件多樣化效果。
3.2.1 gadgets 的存活率
gadgets 的存活率指多樣化轉(zhuǎn)換前后二進制代碼中相同的gadgets 數(shù)所占比例,存活率越低表明相同的gadgets 越少,攻擊者想用相同gadgets 攻擊鏈在其他相同軟件復現(xiàn)攻擊的難度越大。本文先用ROPGadgets 工具[24]掃描二進制代碼中的gadgets,再通過對比找到相同的gadgets,進而計算存活率。由于重寫器會把代碼段的地址移位到0x40000000 處,為了方便比較,本文用重寫器重寫生成對照組,對照組僅將原程序代碼段和轉(zhuǎn)換后的程序代碼段移到相同位置,不做其他任何轉(zhuǎn)換。
實驗結果如表2 所示,第1 列表示基準測試集中軟件的名稱,第2 列表示對照組程序中gadgets 的數(shù)量,第3~第5列為不同轉(zhuǎn)換下gadgets的存活率,第6列表示2 種方法同時多樣化后相同的gadgets 數(shù)量。分析表2 數(shù)據(jù)可知:僅在基本塊內(nèi)部進行指令重排序時gadgets 的平均存活率可以達到14.88%,相同的gadgets 數(shù)量很多,原因是gadgets 一般是以ret 和間接jmp/call 結尾的指令片段,但是基本塊內(nèi)的指令重排序很難影響該類指令位置;函數(shù)重排序的效果遠好于基本塊內(nèi)指令重排序,函數(shù)重排序可以將指令放到地址空間中的任意位置,gadgets 只有很小的概率會留在原地,存活的gadgets 主要存在于二進制代碼中不進行隨機化的部分,如.plt 段、.plt.got 段等;當使用2 種技術同時轉(zhuǎn)換時,gadgets 存活率與僅進行函數(shù)重排序相當,很顯然,在計算存活率時,基本塊內(nèi)指令重排序的影響很小,此時gadgets 的平均存活率為5.71%,平均存活數(shù)量不足300,其中缺少具備內(nèi)存讀寫功能的gadgets,且?guī)缀鯖]有以ret 指令結尾的gadgets,攻擊者難以使用同一攻擊負載進行大規(guī)模的軟件攻擊,增加了攻擊成本,從而達到了軟件多樣化的防御效果。
表2 多樣化轉(zhuǎn)換后gadgets 的存活率Table 2 Survival rate of gadgets after diversified transform
3.2.2 Tlsh 算法
模糊哈希算法Tlsh 將50 Byte 以上的數(shù)據(jù)計算生成一個哈希值,通過計算哈希值之間的相似度,從而得到原始文件之間的同源性關聯(lián)。Tlsh 算法廣泛應用于文件的相似性比較,相比于gadgets 存活率僅對代碼段進行比較,Tlsh 算法不僅可以比較二進制代碼段的相似性,還能比較可執(zhí)行文件中數(shù)據(jù)的相似性。本文采用文獻[25]中對二進制文件的相似性判定閾值52,即當Tlsh 算法比較結果大于52 時,認定源程序和多樣化轉(zhuǎn)換后的程序之間沒有相關性。
表3 所示為SPEC CPU2006 基準測試集中程序轉(zhuǎn)換前后Tlsh 算法的比較結果,從中可以看出,僅對程序444.namd 進行單個軟件多樣化方法轉(zhuǎn)換時沒有達到閾值,其余轉(zhuǎn)換結果均超過閾值。
表3 多樣化轉(zhuǎn)換后Tlsh 算法的比較結果Table 3 Comparison results of Tlsh algorithm after diversified transform
3.2.3 Bindiff 測試
Bindiff 是基于指紋和圖像開發(fā)的IDAPro 插件,通過靜態(tài)分析方法將二進制代碼轉(zhuǎn)化為圖結構,常用于惡意程序的同源性檢測任務。本次實驗通過Bindiff 算法計算2 種多樣化技術轉(zhuǎn)換前后對函數(shù)、基本塊和指令3 個層級的異構度影響,圖6 所示為實驗數(shù)據(jù)的歸一化處理結果,函數(shù)隨機化和基本塊內(nèi)指令隨機化對函數(shù)、基本塊、指令的異構度影響分別用FI、FB、FF 和II、IB、IF 表示。從圖6 可以看出,2 種技術對函數(shù)的異構度影響都不大,平均分別為5.2%和3.4%,這是因為函數(shù)隨機化雖然打亂了程序的內(nèi)存布局,但是函數(shù)實際間的調(diào)用關系并未發(fā)生變化,也導致基本塊和指令的匹配度較高。指令隨機化對函數(shù)的異構度影響更小,但是在一定程度上影響了程序基本塊間的匹配,而基本塊的不匹配會導致Bindiff 算法認為基本塊內(nèi)的指令也不匹配,使得指令重排序的總異構度大于函數(shù)重排序,平均分別為45.2%和30.3%。因此,基本塊內(nèi)指令重排序的多樣化效果優(yōu)于函數(shù)重排序。
圖6 多樣化轉(zhuǎn)換后函數(shù)、基本塊和指令的異構度Fig.6 Heterogeneity of functions,basic blocks and instructions after diversification transform
現(xiàn)有軟件多樣化方法大多依賴源代碼,而對二進制代碼直接進行轉(zhuǎn)換,容易導致較高的性能開銷,而且由于缺乏相應的符號和調(diào)試信息,導致難以準確地進行反編譯。針對上述問題,本文提出一種面向二進制代碼的細粒度軟件多樣化方法。該方法通過重寫器反編譯二進制代碼,打亂代碼段中函數(shù)塊和基本塊內(nèi)的指令順序,以阻止攻擊者獲取程序內(nèi)存信息而進行代碼重用攻擊。實驗結果表明,該方法能夠有效躲避同源性檢測,且多樣化后程序的gadgets 平均存活率為5.71%,在該安全性下性能開銷僅為3.1%,具有良好的實用性。下一步將在二進制代碼的基本塊層進行多樣化方法研究,使軟件能夠在多個粒度進行多樣化,以更好地防御大規(guī)模代碼重用攻擊。