王雅儀,劉 琛,黃天波,文偉平
(北京大學 軟件與微電子學院,北京 102600)
從早期的個人計算機發展至今,軟件的攻擊與保護之爭從未停止過,在軟件種類和平臺呈現百花齊放的當下,如何有效地保護軟件,始終是一個值得探究的問題。
作為軟件保護技術之一的代碼混淆因使用上的靈活性和較低成本帶來了可觀的安全性,在工業界和學術界得到不斷關注和發展。軟件攻擊的目的在于理解或部分理解原生代碼,從而添加或者移除代碼邏輯,以滿足攻擊者自身的利益訴求。代碼混淆預期的目標是增大原生程序的理解難度,包括阻止自動化工具的反編譯,增加原生程序本身的復雜度等。Collberg 等[1]在1997 年首次對代碼混淆技術進行了明確的分類,包括布局混淆、數據混淆、控制流混淆和預防性混淆。此后,又出現了各種形式的代碼混淆研究,應用于軟件的不同階段,如在實現[2]或編譯[3]時,涵蓋了代碼的不同部分,如在源代碼[4]、二進制代碼[5-6]或中間代碼[7]級別。
不同于文獻[1]的分類方式,本文按照混淆的粒度,將代碼混淆方法分為函數混淆、基本塊混淆和指令混淆。指令作為高級程序的底層組織,面向指令層級的混淆可以做一些更細致精準的操作。如果攻擊者能夠破解程序,首先面對的便是破解后的程序指令,因此指令混淆對此階段的代碼保護具有重要作用。這也正是本文的工作聚焦于指令混淆的原因。
底層虛擬機混淆器(Obfuscator Low Level Virtual Machine,OLLVM)[7]是一款經典的開源混淆項目,它支持指令替換、虛假控制流、控制流扁平化等功能。然而,在指令混淆層面,它只提供指令替換一種功能,并不支持指令加花,并且指令替換只支持5 種運算符,共計13 種替換方案。
針對OLLVM 的不足,本文設計了一種改進的指令混淆框架,命名為InsObf,旨在對其中的指令混淆進行補充。已實現的優化工作包括:拓展指令替換功能至13 種運算符,共計52 種指令替換方案;增加指令加花的功能,包含疊加跳轉和虛假循環兩種指令加花方案。實驗結果證明,本文提出的指令混淆框架的效果相較于OLLVM,在時間開銷增長約10個百分點,空間開銷增長約20 個百分點的情況下,圈復雜度和抗逆向能力均提升了近4 倍;同基于OLLVM 改進的Armariris 和Hikari 相比,本文提出的框架在同一量級時空開銷下,圈復雜度增長最高,達到了混淆前的6 倍,并將指令相似度降低到23.5%,可以提供更高的代碼復雜度。
指令混淆是針對程序中的指令進行添加、修改等操作的一種混淆方法,包括指令加花[8-10]、指令替換[7,11]、指令加密[12-13]和指令重組[14]。向指令流[15]中添加死代碼[16]、冗余操作數[17]、新段[18]等,都屬于這類轉換。相較于指令加密和指令重組帶來的時空開銷高、程序健壯性差等問題,指令加花和指令替換相對更為成熟,且使用場景更為廣泛,特別地,在OLLVM 僅實現了指令替換,存在指令加花的技術空白,因此本文研究一方面聚焦于填補OLLVM 指令加花的空白,另一方面著眼于進一步加強目前已有的指令替換方案。下文將介紹指令加花和指令替換的相關研究工作。
1.1.1 指令加花
指令加花的目的是向程序中添加一些冗余信息,這些信息不會改變程序預期的行為,但可增大指令的理解難度,擾亂指令的原生特征。
常見的方式是插入一些不會被執行或執行后不影響程序預期執行結果的指令。Zhang 等[19]通過引入動態不透明謂詞在兩個基本塊之間插入一個冗余的基本塊,從而擾亂原始基本塊間的依賴關系;Cho 等[17]提出插入無關的循環變量和冗余的操作數,復雜化原有的計算表達式;文獻[20]中通過插入虛假的調用,在后續指令中使用調用結果來干擾程序的分析;Peng 等[21]提出在循環條件前利用不透明謂詞插入指令,復雜化程序結構。也有一些研究[22-24]采用插入NOP(No Operation)指令的方式,盡管NOP 指令不執行任何操作,但其隨機性的存在可以使代碼片段在內存中位置的預測變得更加困難。
另一類是在指令的特定位置插入垃圾指令來阻止反匯編工具的處理。Linn 等[25]通過在指令流的特定位置插入不完整的字節來引入反匯編錯誤;文獻[26]中針對ARM 指令進行混淆處理,通過添加無效指令和構造隨機數據,實現對線性掃描反匯編同步的延遲,以阻止自動化反編譯器對指令的約簡。
1.1.2 指令替換
指令替換是指用功能相同但更復雜的指令序列替換源程序中的指令運算符或操作數,使得程序中的指令更難理解。例如,指令中的操作數可以用一段代碼替換[20],拆分為多個變量[17,27],反之,也可以把多個變量合并在一起。Rajba等[28]使用按位與、左移和異或運算符對加法運算符進行混淆。對布爾變量進行分割[29]、將常量替換為一串運算表達式[30]或者利用代碼動態生成[29]也是常見的替換方案。Kanzaki 等[31-32]提出首先通過代碼自修改機制將原始指令替換成另一套偽指令集,然后在代碼運行時自動恢復成原始代碼片段,使得逆向人員只能拿到替換后的偽指令集。Darwish 等[33]針對匯編語言,指出系統中任何操作都可以通過不同方式實現,因此可以從原指令的可能替換組中隨機生成等價的代碼替換。
1.2.1 OLLVM
底層虛擬機(Low Level Virtual Machine,LLVM)[34]為代碼混淆技術跨平臺使用提供了可能。OLLVM 是第一個基于LLVM 的開源混淆項目,借助于LLVM 的跨平臺性,OLLVM適用于任何LLVM 支持的編程語言,例如C/C++、Objective-C、Fortran 等。
在指令替換部分,OLLVM 支持整數ADD、SUB 和布爾運算符AND、OR 和XOR。表1 是具體支持的替換方案。

表1 OLLVM指令替換方案Tab.1 Instruction substitution schemes in OLLVM
1.2.2 Armariris
Armariris[35]是由上海交通大學密碼與計算機安全實驗室維護的LLVM 混淆框架,目前提供了字符串加密、控制流扁平化和指令替換三個功能。項目本身相較于OLLVM 的改進在于字符串加密方案的補充,雖然僅針對源代碼中的常量字符串提供異或加密的方式,但是為基于LLVM 的字符串加密提供了新的可能性。
1.2.3 Hikari
Hikari[36]是一個基于OLLVM 進行優化的二進制加固工具,主要改進了控制流混淆和字符串混淆等功能,未涉及指令層級的混淆優化。其中,控制流混淆的改進,如直接跳轉間接化,通過將程序中跳轉指令的目標地址由明文改為數組匹配的方式,實現間接跳轉。在字符串層面,不同于Armariris 的字符串處理方式,加密是在函數內部完成而非全局處理,且只有運行過的函數才會將解密過的字符串存放在內存中。
InsObf 指令混淆框架的工作原理如圖1 所示,可以被分為三層:源代碼層、LLVM 中間表示(LLVM-Intermediate Representation,LLVM-IR)層和目標平臺層。源代碼層包含待混淆的源程序。在LLVM-IR 層中,源程序通過LLVM 前端編譯器(例如Clang[37])轉換為LLVM-IR 文件,然后由指令混淆生成混淆后的LLVM-IR 文件。根據預期的混淆強度,可以選擇繼續混淆或使用LLVM 后端生成基于特定目標平臺的可執行程序。

圖1 InsObf整體架構Fig.1 Overall architecture of InsObf
InsObf 指令混淆框架包含兩種混淆方法:指令加花和指令替換。在混淆的過程中,InsObf 首先通過指令加花方法來混淆LLVM-IR 文件,插入一些冗余指令組成的基本塊。然后用指令替換方法處理操作符和操作數,進一步加強安全性。
指令加花通過結合指令的數據依賴分析,隨機選擇花指令插入的位置,保證混淆結果的多樣性和隨機性。插入的疊加跳轉指令和原始基本塊內的指令高度相似但不同,虛假循環指令使用原始基本塊中的變量作為循環控制條件,兩種形式的花指令在充分利用源程序中指令信息的同時,能夠有效干擾逆向人員的分析,加大分析的難度。指令替換可以從生成的多個等價表達式中隨機選擇替換方案,來實現對原有語義的隱藏,給程序帶來多樣化。當指令替換同指令加花進行效果疊加后,這種隱藏能力被最大限度地發揮:既作用于源程序的真實指令語義,破壞反混淆器既定的模式識別;又作用于添加的花指令,進一步隱藏真實指令和源程序指令之間的相似度,使得通過利用程序地址空間的攻擊更難實現。
本文提出了兩種指令加花方法:一種是疊加跳轉,另一種是虛假循環。文獻[19,38]將垃圾指令添加到基本塊之間,然而基本塊內部指令在語義上更有連貫性,相應地,直接破壞基本塊內部指令結構將給程序的理解上帶來更大的難度。因此,本文在原有基本塊內部添加花指令,同時考慮到程序的三種基本結構中,相較于順序結構,分支和循環結構包含更多程序的控制流信息,往往是攻擊者關注的重點,大量分支和循環結構的存在會加大分析的難度。因此,在花指令的設計形式上,構造疊加跳轉和虛假循環的形式。為了構造形式的完整性,本文將構造的花指令以新的基本塊的形式進行添加,首先對原有的基本塊進行拆分,然后在拆分后的基本塊間插入構造的花指令基本塊,并通過不透明謂詞進行有效的整合。
2.1.1 疊加跳轉
定義1疊加跳轉。記程序的基本塊集合為originBBs,對?b∈originBBs,將其分割為b1和b2,并根據分割后的基本塊生成新的虛假塊集合bogusBBs,在保證程序語義的前提下,構造bogusBBs內虛假塊與b1、b2的跳轉關系,形成具有層級的基本塊關系。
添加疊加跳轉指令的過程中有如下兩點需要討論說明:
1)基本塊內的指令之間往往存在數據依賴關系,如何在不破壞數據依賴關系的情況下對基本塊進行分割?
2)插入基本塊給程序的運行帶來了新的開銷,如何盡可能地降低增加的時空開銷?
為了解決問題1),本文設計了一種數據依賴分析算法,詳細的步驟如算法1 所示。在對基本塊進行分割之前,需要先分析基本塊指令間的數據依賴關系,進而將原本單一基本塊內的所有指令劃分為幾個獨立的指令集合。每個集合內的指令是高度內聚的,即具有強相關性;不同集合中的指令無數據依賴關系。極端情況下,整個基本塊內的指令均存在強相關性,此時,一個基本塊即是一個集合,將不對基本塊做任何處理。

算法1 的輸入為基本塊內所有的LLVM-IR 指令,輸出為存儲指令依賴分析結果的嵌套堆棧集合,集合內的每一個子集也是一個棧,用于存儲一組相互依賴的指令。算法實現的中間過程需要借助棧和隊列來完成,前者用于存儲相關依賴指令,最后作為子集存儲到依賴分析結果集中;后者用于存儲當前指令搜索過程中涉及的直接依賴指令和間接依賴指令,并不斷向外擴展,直到沒有新的依賴指令。針對每一條新加入的指令,均需要備份并對其依賴關系進行重新檢查,通過隊列實現每一條加入的新指令,其依賴的指令也會同步加入指令堆棧中。在指令依賴關系分析過程中,若出現所依賴的指令已經添加到其他的指令集合中,則生成原生指令的備份存儲到新的集合中,以避免數據流的斷裂問題。在基本塊分割時,則從指令依賴分析結果集之間隨機選擇一個位置,如原始的基本塊根據算法1 的分析結果被分割成startBB和mainBB,效果如圖2 所示。

圖2 拆分基本塊示意圖Fig.2 Schematic diagram of splitting basic blocks
問題2),因受限于算法本身花指令的設計,在空間開銷上未作出較大優化。對于時間開銷,本文使用Palsberg[39]引入的動態不透明謂詞,將花指令添加到運行時不可達的位置來盡可能降低時間上的開銷。如在圖2 的基礎上將mainBB用作原型副本,復制得到新的基本塊,并隨機替換其中一些指令(不需要等價),從而獲得bogusBB1、bogusBB2等。這些新被創建的基本塊和mainBB 高度相似但不同,在充分利用程序信息的同時起到混淆視聽的效果。然后,這些創建的新基本塊將通過不透明謂詞控制,插入到startBB 和mainBB 之間。圖3 為插入疊加跳轉指令后的控制流示意圖。另外,創建的新基本塊的數量可以由用戶指定,針對不同規模的程序提供定制化的功能,從而提供更高的靈活性,默認數值為2。

圖3 插入疊加跳轉指令后的控制流Fig.3 Control flow after inserting multiple jump instruction
2.1.2 虛假循環
定義2虛假循環。構造循環對應的基本塊集合loopBBs,記程序的基本塊集合為 originBBs,對?b∈originBBs,將其分割為基本塊b1和b2,在保證程序語義的前提下,通過構造跳轉關系,在b1和b2中嵌入loopBBs 等虛假塊。
為了增加混淆效果的多樣性,可以由用戶指定混淆概率,對程序中的所有基本塊以設定的概率依次決定是否要添加虛假循環,默認混淆概率為80%。
以一段用C 語言編寫的循環程序為例,為了敘述上的簡便性,這里的loopBBs 為程序對應的基本塊集合,loopBBs={entryBB,loopCondition,loopBody}。entryBB 對應于第1)~2)行循環語句的前序聲明對應的基本塊,loopCondition 為第3)行循環條件判斷對應的基本塊,loopBody 為第4)行內部循環體對應的基本塊。

圖4 為插入虛假循環指令后的控制流示意圖。在startBB 和bogusBB 之間插入loopBBs,如 圖4 中深色區域所示。值得注意的是,循環指令的設計具有高度靈活性,在實際應用時可以使用與源程序有著數據相關性、更加復雜的形式來代替。例如,在循環體內添加源程序中相關變量的運算操作,在循環條件中引入源程序中的變量等,從而進一步和源程序產生數據關聯。

圖4 插入虛假循環指令后的控制流Fig.4 Control flow after inserting bogus loop instruction
指令替換混淆是指令的等效替換,即用語義相同但形式不同的一條或多條指令來替換。在OLLVM 中,指令替換僅支持5 種運算符,共計13 種替換方案。本文基于OLLVM,在支持的指令操作符數量和替換方案上做了補充,共計13 種運算符,52 種替換方案,具體的運算符和替換方案如表2 所示。在實際應用時,指令替換可以隨機選擇替換方案,給程序帶來多樣性。

表2 OLLVM和InsObf支持的運算符和替換方案數Tab.2 The number of operators and substitution schemes supported in OLLVM and InsObf
在OLLVM 的指令替換方案中,主要關注的是指令中操作符的替換,在操作數方面僅提供了增加冗余操作數的替換方案,考慮到混淆優化處于LLVM-IR 層,在后端編譯時,如果使用高級別的優化選項,例如-O2 和-O3,OLLVM 中提出的類似于a=b+c替換為a=b-(-c)的方案可以被編譯器優化恢復。因此,除OLLVM 中提供的替換思路,本文還額外增加了兩種更高強度的方法:數據拆分替換和循環替換,來對指令中的操作數進行破壞,消除后端編譯器優化的影響,目前這兩種方法只支持整數類型的變換。
定義3數據拆分替換。程序的運算操作集記為O,O所涉及的常量和變量集合記為D,Δx記為運算中產生的進位或借位信息。對?N∈D,將N拆分為高位數據Nhigh和低位數據Nlow,?o∈O轉變為針對Nhigh和Nlow的操作,并同步更新Δx的數值,默認為0。
以32 位整數的ADD 指令為例,數據拆分替換會將該整數拆分為高16 位和低16 位,具體的方案如圖5 所示。

圖5 ADD指令數據拆分替換示例Fig.5 Example of data splitting substitution for ADD instruction
在數據拆分替換中,因為在LLVM-IR 層8 位整數不能被拆分為更低位的數字,因此只關注16 位、32 位、64 位和128位的整數。
考慮到數據拆分替換后的混淆特征較為明顯,進一步提出循環替換算法,對操作符進行降級處理,例如將左移指令轉變為乘法指令、乘法指令轉變為加法指令等。
定義4循環替換。記操作的閾值為N,操作Op1 為將程序中的左移指令轉變為循環形式的乘法指令,操作Op2 為將乘法指令轉變為循環形式的加法指令。使用Op1 或Op2對?i∈{inst|inst∈{MUL,SHL}}進行處理,若循環的處理次數超過閾值N,閾值內的循環邏輯保留,超出的部分仍使用之前的指令形式。
以操作Op2(乘法指令轉變為循環形式的加法指令)為例,循環替換的過程如圖6 所示。需要注意的是,為了保證程序運行的效率,閾值N可由用戶設定。如用戶未設定,在程序首次使用時,統計程序中所有乘法指令轉變為加法指令的頻次分布,取中位數作為默認值。

圖6 乘法指令循環替換示例Fig.6 Example of loop substitution for MUL instruction
本文選擇了20 個典型的C++程序[40]為實驗數據集,包括數組、樹、圖等數據結構和搜索、排序、加密等算法。實驗環境為8 GB 內存的Ubuntu 18.04 64 位機,實驗基于LLVM 10 release 版進行。為了驗證本文提出的指令混淆框架InsObf 的混淆效果,分別從指令混淆的優化效果和不同混淆方法的對比上進行實驗分析。InsObf 指令混淆框架的指令加花子模塊實現,記為InsObf-junk;InsObf 指令混淆框架的指令替換子模塊實現,記為InsObf-sub;InsObf 指令混淆框架整體實現,記為InsObf-mix;OLLVM 的指令替換模塊,記為OLLVM-sub。
指令混淆優化效果從混淆效果分析和性能分析兩方面進行評估。對于混淆效果分析,采用Collberg[1]提出的定量的混淆指標:混淆強度(potency)[14]和抗逆向(resilience)[14];對于性能分析,采用時空開銷作為測試指標。
3.1.1 混淆強度
圈復雜度(Cyclomatic Complexity,CC)由McCabe[41]1976年提出,用來描述程序的復雜性。混淆強度用來衡量自然人理解程序的難度[1],常用CC 進行評估。在數學上,程序控制流圖G的圈復雜度V(G)可以定義為:
其中:E是G中邊的數量,N是節點的數量。
目前公開的圈復雜度工具需依據源碼進行數值處理,因此本文使用LLVM-CBE[42]從混淆前后的可執行文件中獲取到對應的C 源代碼,然后使用Lizard[43]得到混淆前后的圈復雜度。結果如表3 所示。

表3 InsObf和OLLVM圈復雜度分析 單位:%Tab.3 Cyclomatic complexity analysis of InsObf and OLLVM unit:%
從表3 的結果可以看出,同ollvm-sub 相比,InsObf-sub 的圈復雜度提升近1 倍,印證了新增的替換模式能夠有效增大程序的復雜性。InsObf-junk 通過構造基本塊和跳轉關系,對源程序的控制流進行了破壞,從而實現圈復雜度相較于ollvm-sub 提升近2 倍。而指令混淆框架的整體實現InsObfmix 的圈復雜度相較于ollvm-sub 更是提升了近4 倍,有效加大了攻擊者分析和理解程序的難度。
3.1.2 抗逆向
混淆后的抗逆向能力,表示混淆后的程序抵御自動去混淆攻擊的能力[1],為了有效量化該指標,本文進一步做了如下定義:
定義5抗逆向。針對混淆添加的程序代碼,使用去混淆工具無法移除的代碼比重。比重越大,即不可移除的混淆代碼越多,則混淆方法的抗逆向能力越強;反之,抗逆向能力越弱。抗逆向能力計算方式為:
其中:Lorigin是混淆前文件的代碼行數,Lobfus是混淆后文件的代碼行數,Ldeobf是去混淆處理后文件的代碼行數。
本文使用IDA Pro[44]獲取混淆前后文件的代碼行數,然后使用插件D-810[45]對混淆后的文件進行去混淆處理,得到對應的去混淆文件代碼。分別統計針對ollvm-sub、InsObfsub、InsObf-junk 和InsObf-mix 抗逆向的效果,并以ollvm-sub作為對比基準,結果如表4 所示。

表4 InsObf和OLLVM抗逆向分析 單位:%Tab.4 Resilience analysis of InsObf and OLLVM unit:%
從表4 可以看出,InsObf-sub 的抗逆向能力在ollvm-sub基礎上提升近2 倍,InsObf-junk 提升近3 倍,可印證兩種方法在防反混淆工具處理層面是有明顯的增強效果的;同時考慮到指令加花添加了更多的指令,因此數值上會高于指令替換。指令混淆框架的整體實現InsObf-mix 更是提升近4 倍,可有效抵抗反混淆工具的攻擊。
3.1.3 時空開銷
為了獲取混淆前后增長的時空開銷,首先針對測試數據集中的每一個文件進行混淆處理,然后使用腳本獲取具體的混淆前后的時空開銷:時間統計上取多次運行的平均值,空間上取可執行文件的大小。針對數據集,分別使用ollvmsub、InsObf-sub、InsObf-junk 與InsObf-mix 進行如上處理,數值記錄如表5 所示。

表5 InsObf和OLLVM時空開銷分析 單位:%Tab.5 Time and space cost analysis of InsObf and OLLVM unit:%
從表5 可以看出,InsObf-sub 混淆前后時空開銷增長和ollvm-sub 差距不大,均在5 個百分點以內。同時InsObf-junk相較于ollvm-sub 在時間開銷上降低約6 個百分點,空間開銷增加約16 個百分點。在時間開銷上,雖然InsObf-junk 使用到的不透明謂詞有所增加,但相較于ollvm-sub 還是有明顯的優勢。另一方面,算法本身添加的大量的花指令使其空間開銷高于ollvm-sub。整體的指令混淆框架,因為指令加花和指令替換的疊加效果,相較于ollvm-sub 時間上增加約10 個百分點,空間上約20 個百分點。
綜合表3~5,可以得到如下結論:
1)InsObf-sub 在OLLVM 的基礎上,以增加5 個百分點以內的時空開銷為代價,可有效增加混淆后的程序圈復雜度和抗逆向的能力。
2)InsObf-junk,相較于InsObf-sub,在增加10 個百分點的空間開銷的情況下,圈復雜度可以提升近一半,抗逆向能力可以提升近一倍。
3)相較于OLLVM,InsObf 在時間開銷增加約10 個百分點,空間開銷增加約20 個百分點的情況下,可提高4 倍的圈復雜度和抗逆向能力。
不同的混淆方法在實現時側重于程序不同的安全屬性,如指令混淆側重于指令的替換和復雜化,控制流混淆側重于隱藏或擾亂程序的原生路徑,字符串混淆側重于隱藏或保護程序中的字符串等常量信息,所以目前沒有統一的指標可以在混淆所帶來的安全性角度,對不同混淆方法給出一個定性或定量、足夠完備或公平的對比說明,但從廣義上的程序復雜性角度,可以使用時空開銷和圈復雜度進行效果的說明。因此,本文將從時空開銷和圈復雜度的角度比較InsObf 和目前業界針對OLLVM 的兩大改進版:Armariris 和Hikari 的效果。分別統計混淆前后可執行文件在時空和圈復雜度上的增長,表6 是不同混淆方法對比的效果,Armariris-string 為Armariris 改進的字符串加密(StringObfuscation)的實現;Hikari-string 為Hikari 改進的字符串加密(StringEncryption)的實現;Hikari-cf 為Hikari 改進的控制流混淆方法的疊加使用,包 括 FunctionCallObfuscate、FunctionWrapper 和IndirectBranching 三種。

表6 不同混淆方法的混淆效果 單位:%Tab.6 Obfuscation effects of different methods unit:%
時間開銷上,Armariris 的字符串加密混淆使用了異或的加解密方式,所以時間增長上最少,Hikari 的控制流混淆涉及控制流的多級處理,在時間開銷上尤甚。空間開銷上,本文提出的指令混淆框架增加最多,Armariris 的字符串加密混淆增長最少,幾乎為0,考慮到本文方法添加了較多的花指令,而Armariris 使用異或進行加解密,未在空間上引入額外開銷,結果在預料內。圈復雜度數值處理上,需要使用llvmcbe 獲取混淆前后的源文件,但Hikari 混淆后的文件無法使用LLVM-CBE 有效生成源文件,因此表6 中Hikari-cf 一欄置空。但從已有的數據中,InsObf-mix 混淆前后的圈復雜度增長最多,達到了6 倍。
為了同Hikari 改進的控制流混淆方法進行有效對比,使用BinDiff[46]比 較Hikari 和InsObf 混淆前后程序的指令相似度,結果如表7 所示。

表7 InsObf和Hikari的相似度分析 單位:%Tab.7 Similarity of InsObf and Hikari unit:%
表7 的結果顯示,InsObf 混淆前后在指令相似度的降低上優于Hikari,這是因為一方面InsObf-junk 通過不透明謂詞引入了大量的跳轉指令,這也就導致JUMP 指令的相似度也隨之降低;另一方面由于InsObf-sub 對指令中的運算符和操作數進行了大量的替換工作,最終使得指令相似度大幅降低。而Hikari 因為FunctionWrapper 針對函數調用做了較多混淆處理,所以其CALL 指令的相似度低于InsObf。
綜合表6 和表7,可以得到如下結論:本文提出的指令混淆框架InsObf 在空間開銷上和Hikari 的控制流混淆處于同一量級;在時間開銷上,高于Armariris 的字符串混淆,低于Hikari 的字符串混淆和控制流混淆,處于中等水平;在圈復雜度和指令相似度方面,有明顯的增加。在同基于OLLVM優化的多種混淆方法的對比中,可以證明本框架可對程序指令進行完備的混淆處理,提供指令層級的有效保護。
本文基于OLLVM 的指令混淆模塊進行了改進,拓展了指令替換功能支持的運算符數和替換方案數,并額外增加了指令加花的功能。其中,指令替換支持13 種運算符共計52種方案,可以從生成的多個等價表達式中隨機選擇替換方案,使得通過利用程序地址空間的攻擊更難實現。指令加花通過插入疊加跳轉指令和虛假循環指令,在充分利用源程序中指令信息的同時,能夠有效破壞程序的結構,加大攻擊者分析、理解程序的難度。實驗結果表明,與OLLVM 的指令替換功能相比,本文提出的框架在時間開銷增加約10 個百分點,空間開銷增加約20 個百分點的情況下,圈復雜度和抗逆向能力均可提升4 倍;在同Armariris 和Hikari 的對比中,通過時空和圈復雜度、指令相似度等指標,論證本文提出的指令層級的混淆方案,在同一量級的時空開銷下可以提供更高的代碼復雜度。
未來工作中可以針對時空開銷的降低和增加浮點運算符的指令替換進行處理,且在指令替換中結合加密算法,如密碼學中的同態加密,進一步提高代碼的安全性。