肖順陶,周安民,劉 亮,賈 鵬,劉露平
(四川大學(xué)電子信息學(xué)院,成都610065)
(*通信作者電子郵箱906120662@qq.com)
底層虛擬機(jī)混淆器(Obfuscator Low Level Virtual Machine,OLLVM)[1]是瑞士西部應(yīng)用科技大學(xué)信息安全小組于2010年6月發(fā)起的一個(gè)項(xiàng)目,該項(xiàng)目的目的是提供一個(gè)基于LLVM編譯套件的開源工具,通過代碼混淆和防篡改來提高軟件安全性。憑借其出色的混淆效果、自定義的混淆方案、不依賴編程語言和平臺(tái)架構(gòu)等特性,OLLVM在軟件安全領(lǐng)域得到了廣泛運(yùn)用。惡意軟件開發(fā)者也逐步在自己的軟件中使用該混淆技術(shù),以增加安全人員的分析難度,防止自己的惡意軟件被破解。由于目前關(guān)于OLLVM的資料比較少,加上反混淆受OLLVM保護(hù)的程序難度較大,極少數(shù)大型殺毒公司雖有相應(yīng)的OLLVM反混淆方案,但出于商業(yè)目的,并未開源自己的反混淆方案,因此實(shí)現(xiàn)OLLVM反混淆對(duì)于保護(hù)軟件用戶的合法權(quán)益和維護(hù)健康的軟件安全生態(tài)環(huán)境都具有重要的現(xiàn)實(shí)意義,也是一個(gè)亟需解決的問題。
在OLLVM反混淆方面,目前多采用基于符號(hào)執(zhí)行的方法來消除控制流平展化[2]。該方面研究比較出色的是國外的Quarkslab團(tuán)隊(duì),該團(tuán)隊(duì)提出了一種基于Python的逆向工程框架 Miasm[3],其支持可執(zhí)行的可移植(Portable Executable,PE)文件、可執(zhí)行與可鏈接格式 (Executable and Linkable Format,ELF)等多種文件格式解析,并且支持x86、高級(jí)精簡(jiǎn)指令系統(tǒng)處理器(Advanced RISC Machines,ARM)、無互鎖管道微處理器(Microprocessor without Interlocked Piped Stages,MIPS)等多種架構(gòu)平臺(tái),可以通過中間表示(Intermediate Representation,IR)[4-5]表征匯編指令語義,并使用 JIT(Just In Time)[4]技術(shù)進(jìn)行代碼的模擬執(zhí)行。
Miasm雖然為目前最出色的針對(duì)OLLVM的反混淆工具,但其依然存在許多問題,如反混淆后的圖形顏色粗陋、基本塊中的IR中間表示晦澀難懂、反混淆后的圖形無法反編譯恢復(fù)程序源碼等。針對(duì)這些缺點(diǎn),本文在研究符號(hào)執(zhí)行工具angr[6]和二進(jìn)制分析逆向工程框架(Binary Analysis and Reverse engineering Framework,BARF)[7]逆向框架的基礎(chǔ)上,在x86架構(gòu)的Linux平臺(tái)下,提出了一種基于符號(hào)執(zhí)行的OLLVM自動(dòng)化反混淆框架。該框架以C/C++文件經(jīng)OLLVM混淆后得到的ELF文件作為輸入,利用BARF框架進(jìn)行反匯編和基本塊的分析,并結(jié)合自定義的基本塊識(shí)別算法確定序言、主分發(fā)器、相關(guān)塊、預(yù)分發(fā)器、返回塊等基本塊;接著,使用符號(hào)執(zhí)行工具angr從各基本塊起始地址開始符號(hào)執(zhí)行,確定真實(shí)的可達(dá)路徑以及各基本塊之間的前后關(guān)系;最后,修復(fù)二進(jìn)制程序,包括使用無操作(No OPeration,NOP)指令填充無用基本塊、有用塊(包含序言、相關(guān)塊、返回塊)指令替換、有用塊跳轉(zhuǎn)偏移量修正這三個(gè)方面。由于所有的修改都是直接針對(duì)混淆后程序的匯編代碼進(jìn)行的,因此經(jīng)過反混淆后,最終得到一個(gè)可執(zhí)行文件,并能實(shí)現(xiàn)反編譯恢復(fù)出未混淆程序的原始代碼。
控制流平展化(Control Flow Flattening)[8]思想最先由美國維吉尼亞大學(xué)的Wang等[9]提出,早在2008年便有學(xué)者將控制流平展化技術(shù)運(yùn)用于C/C++代碼的保護(hù)[2]??刂屏髌秸够窃诓桓淖?cè)闯绦蚬δ艿那疤嵯?,將C/C++代碼中的if/while/for/do等控制語句轉(zhuǎn)換為等價(jià)的switch分支結(jié)構(gòu),以消除case代碼塊之間原有的邏輯調(diào)用關(guān)系,使得所有代碼塊看起來都為平行關(guān)系,從而達(dá)到對(duì)程序控制流混淆的目的。
控制流平展化的思想是將源程序函數(shù)分成一個(gè)入口塊和多個(gè)相鄰的基本塊(case代碼塊),每個(gè)基本塊都有相應(yīng)編號(hào),并且這些基本塊都有相同的前驅(qū)和后驅(qū)模塊。前驅(qū)模塊也叫主分發(fā)器,通過表征程序狀態(tài)的控制變量(switch變量)來完成基本塊的分發(fā),這使得基本塊的分支目標(biāo)不能輕易地通過靜態(tài)分析方式來確定,從而阻礙逆向分析者對(duì)程序的理解,增加逆向分析者分析時(shí)間,其模型如圖1所示。
圖1 控制流平展化模型Fig.1 Control flow flattening model
目前OLLVM提供了三種混淆編譯選項(xiàng),本文主要使用控制流平展化選項(xiàng),其可以使用-mllvm、-perFLA兩個(gè)參數(shù)來自定義控制流平展化程度,如設(shè)置參數(shù)-perFLA的值為100對(duì)源程序所有函數(shù)開啟控制流平展化保護(hù)。
符號(hào)執(zhí)行(symbolic execution)[10-11]就是把程序輸入和程序變量都視為符號(hào),并在符號(hào)執(zhí)行時(shí),更新每個(gè)變量的符號(hào)表達(dá)式。本文提出的反混淆方案使用angr符號(hào)執(zhí)行框架,angr是一款基于python的二進(jìn)制文件分析框架,兼具靜態(tài)分析和動(dòng)態(tài)符號(hào)執(zhí)行功能,其由 angr、simuvex、claripy、cle、pyvex、archinfo[6]六大模塊組成,本文主要使用前五個(gè)模塊。
angr模塊是angr框架的分析和協(xié)調(diào)中心模塊,用于完成二進(jìn)制文件的分析和路徑探索,本文提出的反混淆框架使用了Project/Factory/Path這3個(gè)容器。其中:Project容器用于給待分析的二進(jìn)制文件創(chuàng)建一個(gè)angr的Project對(duì)象;Factory容器有多種類型,如:factory.block、factory.blank_state 等,本文則使用相應(yīng)的factory類型來獲取程序執(zhí)行過程中生成的SimState對(duì)象。SimState對(duì)象跟蹤并記錄著程序動(dòng)態(tài)執(zhí)行過程中的符號(hào)信息、符號(hào)對(duì)應(yīng)的寄存器信息和符號(hào)對(duì)應(yīng)的內(nèi)存信息等;Path容器用于存放程序動(dòng)態(tài)執(zhí)行過程中的path對(duì)象,本文使用factory.path(state)來獲取以指定程序狀態(tài)state為起點(diǎn)的path對(duì)象,該path對(duì)象代表從state狀態(tài)起點(diǎn)到程序執(zhí)行終點(diǎn)的完整路徑信息。
simuvex模塊是angr框架的VEX IR模擬執(zhí)行引擎,負(fù)責(zé)記錄程序運(yùn)行狀態(tài)并進(jìn)行代碼模擬執(zhí)行。在給定VEX IRSB(IR基本塊)和內(nèi)存、寄存器的初始狀態(tài)后,它可以進(jìn)行靜態(tài)分析和動(dòng)態(tài)符號(hào)執(zhí)行。
claripy模塊是angr框架的求解引擎,它將程序中的一些狀態(tài)用符號(hào)表示,并將程序的每一個(gè)路徑都翻譯為一個(gè)邏輯表達(dá)式,形成表達(dá)式樹(expression tree)[12],最終得到一個(gè)非常大的路徑公式,在完成公式求解后,就能得到覆蓋所有路徑的輸入變量。本文提出的反混淆框架主要使用了claripy的位向量值(Bit Vector Value,BVV)構(gòu)造函數(shù)claripy.BVV(),用于指定表達(dá)式中臨時(shí)變量的值,從而使程序選擇不同的分支進(jìn)行符號(hào)執(zhí)行。
cle模塊是angr框架的文件解析加載器,angr使用一個(gè)精簡(jiǎn)的加載器“CLE Loads Everything”[6],該加載器并非完全精確,但是能加載ELF/ARM等可執(zhí)行文件。
pyvex模塊則為angr框架提供了將二進(jìn)制代碼轉(zhuǎn)換為VEX IR中間表示的接口。
BARF是一個(gè)基于Python的,支持多平臺(tái)和二進(jìn)制指令轉(zhuǎn)換為中間表示的逆向工具,其主要由 Core、Arch、Analysis[7]三個(gè)模塊組成。本文主要使用BARF對(duì)混淆后的二進(jìn)制文件進(jìn)行反匯編、生成包含逆向工程中間語言(Reverse Engineering Intermediate Language,REIL)[5]表示的控制流圖(Control Flow Graph,CFG)[13],并提供相應(yīng)的 IR 基本塊分析功能。
Core模塊是BARF框架的核心模塊,它定義了REIL中間表示、可滿足性理論(Satisfiability Modulo Theories,SMT)求解器和二進(jìn)制接口(Binary Interface,BI)。
Arch模塊指出了該框架所支持的平臺(tái)架構(gòu),目前支持x86和arm兩種平臺(tái)。
Analysis模塊是本文提出的反混淆框架的核心功能模塊,主要由 Basic Block和 Code Analyzer兩部分組成,Basic Block主要用于對(duì)生成的CFG進(jìn)行分析,如獲取基本塊地址等,Code Analyzer主要用于獲取相應(yīng)的 SMT[14-15]表達(dá)式。
要實(shí)現(xiàn)OLLVM的反混淆,主要面臨以下三個(gè)問題:
1)識(shí)別出有用塊和無用塊。經(jīng)OLLVM控制流平展化混淆的程序中會(huì)增加很多無用的基本塊以混淆程序邏輯,這就需要設(shè)計(jì)有效的基本塊識(shí)別算法,找出有用的基本塊和無用的基本塊。
2)確定有用塊之間的前后關(guān)系,得到真實(shí)有效的程序執(zhí)行路徑,因?yàn)榛煜绦蛑械暮芏嗷緣K跳轉(zhuǎn)邏輯并不是程序的實(shí)際執(zhí)行流程。
3)修復(fù)二進(jìn)制程序。在使用NOP指令填充無用基本塊后,為使程序正常運(yùn)行,我們需要對(duì)跳轉(zhuǎn)指令的跳轉(zhuǎn)偏移量進(jìn)行修正;同時(shí),還需要將cmov條件傳送指令改寫成相應(yīng)的條件跳轉(zhuǎn)指令,并在其后添加一條jmp指令,使其跳向另一分支。
圍繞以上三個(gè)問題,本文提出了一種基于符號(hào)執(zhí)行的OLLVM自動(dòng)化反混淆框架,其架構(gòu)如圖2所示。
圖2 OLLVM自動(dòng)化反混淆框架架構(gòu)圖Fig.2 Architecture diagram of OLLVM automatic deobfuscation framework
為了得到有效的反混淆結(jié)果,需要找到混淆程序中的有用塊和無用塊,這里的有用塊是指對(duì)恢復(fù)原始控制流圖有用的基本塊,包含序言、相關(guān)塊、返回塊,而無用塊是指OLLVM混淆后添加的無用基本塊,詳細(xì)定義如下:
1)序言塊:函數(shù)的起始?jí)K;
2)主分發(fā)器:序言塊的直接后繼塊;
3)預(yù)分發(fā)器:后繼為主分發(fā)器的塊(非序言塊);
4)相關(guān)塊:后繼為預(yù)分發(fā)器的塊;
5)無后繼的塊為返回塊;
6)剩下的為無用塊。
其次,BARF框架Analysis模塊中的Basic Block子模塊提供了CFG控制流圖恢復(fù)功能,由該模塊即可獲得基本塊的首地址、基本塊指令、基本塊分支等信息,因此可結(jié)合BARF框架的基本塊分析功能設(shè)計(jì)相應(yīng)的基本塊識(shí)別算法。在對(duì)OLLVM控制流平展化混淆策略進(jìn)行深入研究后,根據(jù)上述基本塊定義,設(shè)計(jì)了如下的基本塊識(shí)別算法:算法中 cfg為BARF框架解析輸入文件后得到的程序控制流圖;blocks代表程序控制流圖中的所有基本代碼塊;predispatcher_Retn(cfg)用來獲取返回塊、預(yù)分發(fā)器的首地址;relevant_NOP_Blks(cfg)用來獲取相關(guān)塊、無用塊的首地址,算法具體描述如下。
輸入 OLLVM混淆后的ELF文件;
輸出 各基本塊首地址。
Begin
序言←sys.argv[2] /* 序言塊首地址*/
主分發(fā)器←序言直接后繼
function predispatcher_Retn(cfg)
for each block∈blocks do
if block沒有分支then
返回塊←block
elif block直接后繼為主分發(fā)器then
預(yù)分發(fā)器←block
end if
end for
return預(yù)分發(fā)器,返回塊
end function
function relevant_NOP_Blks(cfg)
相關(guān)塊←[]
無用塊←[]
for each block∈blocks do
if block直接后繼為預(yù)分發(fā)器and
block指令條數(shù)>2 then
添加block到相關(guān)塊
elif block非序言非返回塊then
添加block到無用塊
end if
end for
return相關(guān)塊,無用塊
end function
End
相比Miasm,本文提出的基本塊識(shí)別算法,采用了更嚴(yán)格的相關(guān)塊定義(指令數(shù)大于2條的后繼為預(yù)分發(fā)器的塊才是相關(guān)塊,指令數(shù)小于等于2條的后繼為預(yù)分發(fā)器的塊只是完成到預(yù)分發(fā)器的跳轉(zhuǎn),對(duì)恢復(fù)程序控制流圖沒有意義,Miasm在后續(xù)處理中也刪除了這些無用的相關(guān)塊),因而能在保證基本塊完備性的前提下,得到真實(shí)有用的相關(guān)塊。
由于在運(yùn)行框架之初就會(huì)給定混淆文件特定函數(shù)入口地址(即序言塊首地址),而且經(jīng)OLLVM混淆后,序言塊只有1個(gè)后繼塊,因此通過BARF框架獲得的序言塊直接后繼塊地址、以及沒有后繼分支的基本塊地址(分別對(duì)應(yīng)主分發(fā)器首地址、返回塊首地址)都是正確的;依此類推,后續(xù)得到的基本塊地址也都是正確的,這就保證了所得基本塊首地址的正確性。
綜上,經(jīng)過上述算法的處理,即可得到完備的、正確的有用塊和無用塊的首地址列表,這為下一步的符號(hào)執(zhí)行做好了準(zhǔn)備。
OLLVM將源程序的控制流打亂了,因此需要找到真實(shí)的控制流,這里采用符號(hào)執(zhí)行的方式進(jìn)行路徑探索,確定各個(gè)有用塊之間的前后順序以及分支關(guān)系。
雖然Miasm和angr兩種符號(hào)執(zhí)行引擎都能得到混淆程序的控制流字典變量{父節(jié)點(diǎn):(子節(jié)點(diǎn)集合)},但Miasm所獲得的控制流字典變量中,當(dāng)父節(jié)點(diǎn)有2個(gè)子節(jié)點(diǎn)(即有分支)時(shí),并不能看出子節(jié)點(diǎn)是滿足條件的分支,還是不滿足條件的分支,必須結(jié)合其符號(hào)執(zhí)行的約束條件才能判斷;而本文基于angr的符號(hào)執(zhí)行模塊,在符號(hào)執(zhí)行過程中,當(dāng)遇到有分支的相關(guān)塊時(shí),首先將存在分支的指令保留下來,然后利用angr的求解引擎Claripy設(shè)置兩個(gè)1 b的位向量值〈BV1 1〉、〈BV1 0〉,分別進(jìn)行符號(hào)執(zhí)行,從而得到父節(jié)點(diǎn)的滿足條件的左子節(jié)點(diǎn)和不滿足條件的右子節(jié)點(diǎn),即意義明確的控制流字典變量{父節(jié)點(diǎn):(子節(jié)點(diǎn)集合)}。由于基本塊識(shí)別模塊已經(jīng)得到完備的正確的有用塊地址列表,因而能保證符號(hào)執(zhí)行后得到正確的有用塊之間的拓?fù)潢P(guān)系,詳細(xì)實(shí)現(xiàn)過程如下:
首先,利用符號(hào)執(zhí)行工具angr,從基本塊識(shí)別模塊得到的有用塊地址列表中的每一個(gè)地址(除去返回塊首地址,因?yàn)榉祷貕K沒有后續(xù)分支)處開始符號(hào)執(zhí)行,一旦遇到cmov類型的條件傳送指令,就將該基本塊指令保存下來,并設(shè)置基本塊分支標(biāo)志位has_branches為True,并且通過angr的求解引擎Claripy設(shè)置兩個(gè)1 b的位向量值〈BV1 1〉、〈BV1 0〉,分別代表?xiàng)l件滿足和條件不滿足時(shí)的約束條件,用于在符號(hào)執(zhí)行過程中改變臨時(shí)變量的值,從而強(qiáng)行使程序走右側(cè)條件滿足時(shí)的分支或者走左側(cè)條件不滿足時(shí)的分支,以達(dá)到完整路徑遍歷的目的;接著,在符號(hào)執(zhí)行函數(shù) symbexec()中,調(diào)用BP_inspect()函數(shù)監(jiān)控angr IR基本塊中是否出現(xiàn)ITE(If-Then-Else)[16]條件表達(dá)式,一旦出現(xiàn)ITE條件表達(dá)式便在此處設(shè)置斷點(diǎn),并依次將ITE條件表達(dá)式中臨時(shí)變量的值修改為〈BV1 1〉、〈BV1 0〉,繼續(xù)符號(hào)執(zhí)行,分別得到左右兩側(cè)不同分支對(duì)應(yīng)的基本塊首地址。當(dāng)依次完成有用塊地址列表中地址的遍歷后,就能得到有用塊之間的前后順序及其分支關(guān)系了。
另外,如果遇到call指令,則將該call指令地址保存下來作為hook函數(shù)的hook地址,并使用hook的方式直接返回,而不去執(zhí)行call指令調(diào)用的子程序,因?yàn)槲覀兏P(guān)注的是該基本塊的整體輪廓。經(jīng)過上述步驟后,即可得到有用塊之間正確的拓?fù)潢P(guān)系,但無用的基本塊仍然存在,為了還原程序真實(shí)的控制流圖,還需使用NOP指令填充無用的基本塊,并完成有用塊的指令修復(fù),這就是指令修復(fù)模塊的主要工作。
混淆后的程序從一個(gè)有用塊跳轉(zhuǎn)至下一個(gè)有用塊時(shí),它們之間隔著許多無用的基本塊,因此在使用NOP指令填充無用塊后,還需修正跳轉(zhuǎn)指令的跳轉(zhuǎn)偏移量,使其能正常跳轉(zhuǎn)到下一個(gè)有用塊,所以指令修復(fù)模塊主要完成無用塊指令填充、有用塊指令修復(fù)兩方面的工作。
在指令修復(fù)模塊中創(chuàng)新性地使用指令修復(fù)技術(shù),通過對(duì)無用塊進(jìn)行NOP指令填充;對(duì)有用塊(含直接跳轉(zhuǎn)塊(只有一個(gè)子節(jié)點(diǎn))、有分支塊(有兩個(gè)子節(jié)點(diǎn)))進(jìn)行指令替換、指令填充、偏移修正,最終得到一個(gè)可執(zhí)行的反混淆文件,從而克服了Miasm由于反混淆后的結(jié)果是一種圖片、無法進(jìn)行反編譯等缺點(diǎn)。由于符號(hào)執(zhí)行模塊已經(jīng)得到了有用塊之間正確的拓?fù)潢P(guān)系并且保存下了有分支的指令,因此可依據(jù)符號(hào)執(zhí)行結(jié)果進(jìn)行指令修復(fù)工作,并能確保指令替換、偏移修正工作的有效性,從而保證反混淆結(jié)果能正常運(yùn)行,并完成對(duì)程序語義的完整還原,得到正確的反混淆結(jié)果。
為此,本文提出的反混淆框架編寫了2個(gè)具有通用性的指令修復(fù)函數(shù)以自動(dòng)完成指令修復(fù)工作。nop_padding()用于向指定地址填充0x90;jmp_padding()用于填充新的跳轉(zhuǎn)偏移量。對(duì)于無用塊,只需調(diào)用nop_padding()函數(shù)使用NOP指令填充該基本塊;對(duì)于有用塊則須進(jìn)行直接跳轉(zhuǎn)塊指令修復(fù)、有分支塊指令修復(fù)。
2.3.1 直接跳轉(zhuǎn)塊
對(duì)于直接跳轉(zhuǎn)塊,只需將該基本塊的最后一條指令改寫為jmp指令,并完成跳轉(zhuǎn)偏移量的修正。具體如下:首先,向該基本塊最后一條指令首地址填充jmp的opcode值;接著調(diào)用nop_padding()函數(shù)向其后4個(gè)地址填充0x90;然后按照下一跳有用塊首地址,計(jì)算出修正的跳轉(zhuǎn)偏移量,并調(diào)jmp_padding()函數(shù)填充新的跳轉(zhuǎn)偏移量即可。
2.3.2 有分支塊
對(duì)于含有分支的基本塊,須將cmov指令改寫成相應(yīng)的條件跳轉(zhuǎn)指令跳至符合條件的分支(如:cmovx指令可改寫成jx指令),并在其后添加一條jmp指令。jx指令的下一跳是滿足X條件的右分支基本塊,jmp指令的下一跳是不滿足X條件的左分支基本塊,然后分別根據(jù)兩個(gè)分支基本塊首地址進(jìn)行跳轉(zhuǎn)偏移量修正即可,分支基本塊的首地址可從符號(hào)執(zhí)行模塊中獲取。
測(cè)試環(huán)境為裝有最新版LLVM-4.0混淆框架、本文提出的OLLVM反混淆框架、Miasm反混淆框架的 Ubuntu16.04 amd64系統(tǒng)計(jì)算機(jī),詳細(xì)配置為:CPU型號(hào)Intel Core i7-4790@3.60 GHz,內(nèi)存 12 GB。
經(jīng)OLLVM混淆后的程序(為ELF文件)含有非常多的分支,在對(duì)其進(jìn)行反混淆,特別是符號(hào)執(zhí)行時(shí)勢(shì)必會(huì)造成時(shí)間開銷,因此本節(jié)就從反混淆用時(shí)指標(biāo)上對(duì)本文提出的反混淆框架性能進(jìn)行測(cè)試,并與Miasm反混淆框架性能進(jìn)行對(duì)比。
測(cè)試方法一 將從 SPECint-2000[17](https://www.spec.org/cpu2000/CINT2000/)下載的200個(gè)C/C++程序使用相同的OLLVM混淆策略進(jìn)行混淆后,便得到了200個(gè)ELF格式的樣本程序。首先使用本文提出的反混淆框架對(duì)每個(gè)測(cè)試樣本進(jìn)行反混淆,為減小誤差,取20次反混淆時(shí)間的平均值作為該樣本最終的反混淆用時(shí),直至完成所有樣本的測(cè)試;然后,重啟電腦,改用Miasm反混淆框架,仍采用上述的測(cè)試方案,完成所有樣本的反混淆用時(shí)測(cè)試。為方便展示,此處從200個(gè)樣本程序中選取10個(gè)具有較大范圍覆蓋率并具有代表性的樣本程序進(jìn)行結(jié)果展示,實(shí)驗(yàn)結(jié)果如圖3所示。
圖3 兩種框架反混淆用時(shí)對(duì)比Fig.3 Time consumption comparison of deobfuscation for two frameworks
表1詳細(xì)列出了測(cè)試時(shí)使用的10個(gè)樣本程序大小,及各程序所包含的分支循環(huán)數(shù)。由圖3可知,ELF文件大小為2.12 MB時(shí),兩個(gè)框架的反混淆用時(shí)增幅均較為明顯,對(duì)比表1發(fā)現(xiàn),該ELF文件對(duì)應(yīng)的分支循環(huán)個(gè)數(shù)為2427個(gè),遠(yuǎn)遠(yuǎn)多于大小為1.08 MB和3.75 MB的ELF文件的分支循環(huán)個(gè)數(shù)。綜合各實(shí)驗(yàn)數(shù)據(jù)可知,反混淆用時(shí)和ELF文件大小無關(guān),而與分支循環(huán)個(gè)數(shù)呈正相關(guān),分支循環(huán)個(gè)數(shù)越多,符號(hào)執(zhí)行時(shí)耗費(fèi)的時(shí)間也越多。
表1 OLLVM混淆后的ELF文件Tab.1 OLLVM obfuscated ELF files
其次,本實(shí)驗(yàn)中Miasm框架反混淆用時(shí)為7 s~21 min不等,而本文提出的反混淆框架為5 s~16 min不等,在混淆程序分支個(gè)數(shù)不多的情況下,二者反混淆用時(shí)較為接近,但隨著分支循環(huán)個(gè)數(shù)的不斷增加,本文提出的反混淆框架反混淆用時(shí)優(yōu)勢(shì)越發(fā)明顯。
綜上所述,本文提出的反混淆框架相比Miasm框架具有更優(yōu)秀的反混淆用時(shí)表現(xiàn)。
反混淆中最關(guān)心的是反混淆結(jié)果的正確性,因此為驗(yàn)證本文提出的反混淆框架的正確性,將從反混淆結(jié)果程序語義正確性(以代碼相似性來衡量)、反混淆結(jié)果運(yùn)行正確性兩個(gè)方面進(jìn)行測(cè)試。
由于Miasm框架反混淆后得到的只是保留程序語義的Miasm IR Graph,無法比較代碼相似性,因此只對(duì)本文提出的反混淆框架做該項(xiàng)測(cè)試。
測(cè)試方法二 使用GNU編譯套件(GNU Compiler Collection,GCC)依次將3.1節(jié)中的200個(gè)C/C++文件編譯為可執(zhí)行文件,并使用二進(jìn)制文件對(duì)比工具 BinDiff[18-19](http://www.zynamics.com/bindiff.html)逐個(gè)比較反混淆后程序與未混淆源程序的代碼相似性。為進(jìn)一步凸顯本文提出的反混淆框架性能以及方便展示,這里僅展示3.1節(jié)中分支最多的10個(gè)程序的代碼相似性對(duì)比結(jié)果,如圖4所示。
圖4 代碼相似性Fig.4 Code similarity
從圖4數(shù)據(jù)可知,10個(gè)測(cè)試樣本的平均代碼相似度為96.7%,表明反混淆后的程序與未混淆源程序具有極高的代碼相似度。
由于反混淆后的程序均為可執(zhí)行文件,因此可以通過對(duì)比未混淆程序和反混淆后程序的運(yùn)行結(jié)果,來判斷反混淆結(jié)果的正確性。
測(cè)試方法三 分別運(yùn)行3.2節(jié)中的200個(gè)未混淆源程序及與之對(duì)應(yīng)的反混淆后的程序,對(duì)比二者的運(yùn)行結(jié)果一致度。為方便展示,此處僅展示分支循環(huán)個(gè)數(shù)最多的10個(gè)程序的未混淆程序與反混淆后程序運(yùn)行對(duì)比結(jié)果,如表2所示。
由表2可知,未混淆源程序與反混淆后程序的執(zhí)行結(jié)果完全一致,這說明反混淆結(jié)果是正確的,加之反混淆后的程序與未混淆源程序具有極高的代碼相似度(見圖4),因此可以利用IDA等工具對(duì)反混淆后的程序進(jìn)行反編譯以恢復(fù)源程序代碼。
本節(jié)將從多文件格式支持、多平臺(tái)架構(gòu)支持、反混淆效果三方面對(duì)Miasm框架和本文提出的反混淆框架進(jìn)行對(duì)比,詳細(xì)的對(duì)比結(jié)果如表3所示。
由表3可知,本文提出的OLLVM反混淆框架的反混淆效果明顯優(yōu)于Miasm反混淆框架,特別是在反混淆結(jié)果的后續(xù)處理上,Miasm反混淆后得到的只是保留程序語義的Miasm IR Graph,不僅圖形粗陋,而且圖形中使用晦澀難懂的Miasm IR語言;更重要的是,其反混淆結(jié)果無法反編譯以恢復(fù)未混淆程序源代碼,而本文提出的通用型自動(dòng)化反混淆框架直接針對(duì)OLLVM混淆后的程序的匯編代碼進(jìn)行操作,在保證程序語義完整性前提下,最終得到一個(gè)結(jié)果正確的可執(zhí)行文件,因而可通過使用IDA進(jìn)行加載,獲得顏色分明、層次清晰的程序控制流圖,并且控制流圖中的代碼塊采用易懂的匯編語言;另外,還能通過IDA對(duì)反混淆后的程序進(jìn)行反編譯以恢復(fù)源程序的C/C++代碼。這些都說明了本文提出的反混淆框架相比Miasm框架具有更出色的反混淆性能。
表2 未混淆程序與反混淆后程序運(yùn)行結(jié)果一致度Tab.2 Consistency of non-obfuscated and deobfuscated program in operation result
表3 不同框架反混淆性能對(duì)比Tab.3 Deobfuscation performance comparison of different frameworks
OLLVM混淆技術(shù)的流行為惡意軟件的肆虐提供了土壤,針對(duì)這一問題以及Miasm框架在OLLVM反混淆方面的缺陷,本文提出了一種基于符號(hào)執(zhí)行的OLLVM通用型自動(dòng)化反混淆框架,該框架結(jié)合符號(hào)執(zhí)行、指令修復(fù)技術(shù)很好地克服了Miasm框架的缺點(diǎn),并能恢復(fù)出未混淆程序源代碼,實(shí)驗(yàn)結(jié)果表明該框架相比Miasm框架具有更優(yōu)秀的反混淆性能。
目前本文提出的反混淆框架只能很好地實(shí)現(xiàn)x86架構(gòu)下C/C++文件的OLLVM反混淆,而Android SO文件因采用arm指令,其在基本塊識(shí)別上與x86指令具有不同的規(guī)則,但二者在反混淆的思路、核心技術(shù)上具有共通性,下一步將以此為基礎(chǔ)研究Android SO文件的OLLVM反混淆。