?

二進制代碼塊: 面向二進制程序的細粒度控制流完整性校驗方法

2016-02-20 03:23王明華AbhishekVasishtBhaskar蘇璞睿馮登國
信息安全學報 2016年2期
關鍵詞:控制流二進制結點

王明華, 尹 恒, Abhishek Vasisht Bhaskar, 蘇璞睿, 馮登國

?

二進制代碼塊: 面向二進制程序的細粒度控制流完整性校驗方法

王明華1,2, 尹 恒2, Abhishek Vasisht Bhaskar2, 蘇璞睿3,4, 馮登國4

1百度X-Lab2雪城大學3中國科學院軟件研究所計算機科學國家重點實驗室4中國科學院軟件研究所可信計算與信息保障實驗室

控制流完整性(CFI)是一種在程序中通過保護間接轉移有效減少代碼注入和代碼重用攻擊等威脅的技術。由于二進制程序缺少源代碼級別的語義, CFI策略的設定需要很謹慎?,F有的面向二進制的 CFI解決方案, 如BinCFI和CCFIR, 雖然能夠提供對二進制程序的防護能力, 但它們應用的策略過于寬松, 依然會受到復雜的代碼重用攻擊。本文提出一種新的面向二進制的CFI保護方案, 稱為BinCC。它可以通過靜態二進制重寫為x86下的二進制程序提供細粒度保護。通過代碼復制和靜態分析, 我們把二進制代碼分成幾個互斥代碼塊。再進一步將代碼中的每個間接轉移塊歸類為塊間轉移或塊內轉移, 并分別應用嚴格CFI策略來限制這些轉移。為了評估BinCC, 我們引入新的指標來評估每種間接轉移中合法目標的平均數量, 以及利用call-preceded gadgets產生ROP漏洞利用的難度。實驗結果表明與BinCFI比較, BinCC顯著地將合法轉移目標降低了81.34%, 并顯著增加了攻擊者繞過CFI限制實施復雜的ROP攻擊的難度。另外, 與BinCC可以降低大約14%的空間開銷, 而只提升了4%的運行開銷。

控制流完整性

1 前言

ASLR[22]和DEP[2]能夠緩解傳統漏洞威脅。然而, 即使ASLR和DEP開啟, 攻擊者仍然能夠通過代碼重用實施攻擊[4,21], 例如ROP[20]就是這樣一種代碼重用技術。近年來, 一些研究工作[3][6-8][13][19][24]已經提出針對代碼重用攻擊的解決方案, 并且取得了一定的成果。

控制流完整性[1]在緩解控制流劫持攻擊中扮演著重要的角色, 其主要思想是依據控制流圖(CFG)來限制程序中的控制流轉移。對于具有源代碼的程序, CFG圖的構造趨于完整, 能夠依次提出嚴格的CFI約束策略。但是, 對于二進制程序而言, 由于缺乏源代碼或調試信息, 無法構造完整的CFG信息, 所以提出的CFI策略僅能是粗粒度的。盡管諸如CCFIR和BinCFI等CFI解決方案可以阻止絕大多數控制流劫持威脅, 但是它們仍然可能受到利用call-preceded實現的復雜ROP的攻擊[5][10]。

本文提出一個新的面向二進制程序的CFI保護方法——BinCC, 旨在為二進制程序提供更細粒度的保護。具體的方法是: 通過復制少量的代碼, 并執行靜態分析, 把二進制代碼分成幾個互斥代碼塊, 并將每個間接轉移分為塊間轉移或塊內轉移。接下來提出嚴格的CFI策略來約束這兩種控制流轉移, 其中, 塊內轉移只允許到達當前塊內的目標, 塊間轉移只允許到達代碼塊中特定類型的目標, 從而顯著地縮減了控制流轉移合法目標的數量。

為了評估BinCC, 我們引入新的指標來評估每種間接轉移中合法目標的平均數量, 以及利用call- preceded gadgets實現ROP利用攻擊的難度。與BinCFI相比較, 實驗結果表明BinCC在這兩方面均有顯著的提高。其中, BinCC將合法轉移目標降低了81.34%。尤其, BinCC對returns指令(ret)提供細粒度的保護, 將合法目標數量平均降低了87%, 顯著增加了通過call-preceded gadget實現的復雜ROP攻擊的難度。除此之外, BinCC還具有合理的性能。相比BinCFI, BinCC運行開銷僅增加4%, 而空間開銷降低14%。

總言之, BinCC做出了如下貢獻:

? BinCC通過代碼復制和代碼塊構造, 將程序中的間接控制流轉移分類為塊內轉移和塊間轉移, 并分別實施了細粒度的CFI策略。

? 與BinCFI和CCFIR相比, BinCC顯著縮減了間接控制流轉移(尤其是returns指令)的合法目標數量。

? BinCC不僅可以緩解常見的控制流劫持威脅, 而且能夠顯著增加了利用call-preceded gadget實施復雜ROP利用攻擊的難度。

? BinCC加固的程序具有合理的性能。比起BinCFI, BinCC能夠將加固的程序大小減少約14%, 運行時間增加約4%。

內容組織如下。在第二章中討論背景和相關技術, 接著在第三章中介紹代碼塊的概念和CFI策略。在第四章中介紹代碼塊結構, 在第五章中介紹CFI的實施方法。第六章為實驗評估。第七章探討本文方法的局限性。第八章對本文進行總結。

2 背景和相關技術

CFI解決方案包括基于源代碼和基于二進制兩類。由于在實踐中我們獲得的二進制程序大部分都是沒有源碼的, 所以我們將側重點放在基于二進制的解決方案上。為了說明問題, 我們對CCFIR和BinCFI的實現方式, 以及針對它們的攻擊方式進行討論。

2.1 基于源代碼的CFI

許多研究工作[3][11][12][16][23]依賴于源代碼來提出CFI的防護策略。研究工作[11][23]主要側重于保護虛函數的調用。主要思路是利用類繼承結構分析來識別合法目標, 并插入檢查代碼來實現類方法和vtable檢查。這兩種解決方案都可以為虛函數調用操作提供細粒度保護, 但無法對返回操作進行保護。CFL[3]為在每個間接轉移前加一個鎖操作, 并僅在合法地址處進行相應的解鎖操作。這個方法與我們的方法在相關調用函數返回上類似, 但是它依賴于源碼, 而源碼在實際應用中并不容易得到, 而且這個方法缺乏對模塊化支持。MCFI[15]是一個支持模塊化的CFI解決方案。它使用幾個地址表來存儲間接轉移的合法目標, 并在模塊動態加載時使用輔助類型信息來更新目標。在運行時, MCFI通過執行特定的校驗例程, 對間接控制流轉移的合法性進行校驗。

RockJIT[16]是MCFI[15]工作的擴展, 它可以緩解與JIT代碼相關的控制流攻擊, 可以阻止由JITed代碼引起的控制流攻擊。通過使用JIT編譯器的源代碼來計算程序的CFG, 并通過運行時產生的動態代碼來更新CFI策略。CPI[12]提出了指針代碼完整性和指針代碼分離的思想。通過有選擇地保護易受控制流劫持攻擊的指針訪問, 該方法可以保障程序控制流安全。

2.2 基于二進制的CFI

研究工作[14][24][25]是基于面向二進制的解決方案。O-CFI利用一個查詢表來存放合法控制流轉移目標地址, 輔助O-CFI在運行時對轉移目標的校驗。O-CFI額外使用了隨機化技術對程序中所有代碼塊進行了隨機化處理, 減低了程序遭受信息泄露的風險。SFI[24]是一種沙箱技術, 用來實施控制流完整性保護。其基本思想是使不可信的模塊在相同的程序地址空間內執行, 不允許其訪問其他程序的數據和代碼。PittSFIled[13]和NaCl[25]是基于SFI實現的, 用于保護局部代碼安全, 而且其限制間接轉移的目標也需要滿足內存對齊的要求。

Lockdown[17]使用影子棧對ret指令進行約束。然而, 影子??赡芤敫叩倪\行開銷, 同時為了維護call/ret對, 需要更多的內存讀和寫操作。此外, 影子棧需要存儲在安全內存域中, 這需要由硬件或類似SFI的分離技術來提供支持。安全內存域也可能存在信息泄露的風險, 能夠被攻擊者利用, 如文獻[9]中一個真實的攻擊實例。

CCFIR[26]是一個面向Windows x86 PE二進制程序的CFI解決方案。它將間接轉移的目標安排在一個稱為“springboard”的新節中。不同類型的跳轉目標在springboard按照不同的粒度對齊。在間接控制流轉移時, 首先跳轉至springboard中, 檢測轉移目標地址是否按照某種粒度對齊。如果對齊, 控制流會由springboard中對應stub函數接管, 繼續執行; 否則, 轉移目標被認為不合法。BinCFI[27]是另一種面向Linux x86 ELF二進制程序的CFI方案。BinCFI通過反匯編二進制碼, 改寫間接控制流轉移指令, 再將最終代碼放入新代碼段。對于每個指令, 它都維護一個原位置與新位置的映射表。當間接轉移發生時, BinCFI會使其跳轉至相應的地址轉換例程, 在地址轉換例程中完成對目標地址的查找和轉換。除此之外, BinCFI還對模塊化具有很好的支持。

CFG依賴二進制程序自身, 因此無法精確表示, 所以提出的CFI策略都是粗粒度的。雖然CFI策略能夠減少大量普通控制流攻擊, 但允許的策略仍然可能被攻擊者繞過。圖1顯示了針對CCFIR和BinCFI可能的攻擊方式。實線表示運行時的執行流, 虛線表示可能被攻擊的路徑。對CCFIR來說, 如圖1(a)所示, springboard中的目標, 只要滿足對齊到常量M_R, 任何調用地址都將被認為是合法的。同樣, 對于BinCFI, 一個ret能夠返回到二進制程序中的任何調用點, 如圖1(b)所示。這些返回都是不受保護的, 因此攻擊者能夠利用call-preceded gadget實施ROP利用攻擊。工作[10]已經證實了這種攻擊的可能。

3 方法框架

本文提出一種細粒度的CFI策略, 可以顯著的改善間接控制流轉移中的合法目標。通過復制一些必要代碼和執行靜態分析將二進制程序劃分為一些獨立代碼部分, 并將每個間接轉移定義為代碼塊內轉移和代碼塊間轉移。因此, 我們可以執行獨立、嚴格的策略來達到細粒度保護。下面, 我們將首先通過一個例子介紹代碼塊相關概念, 然后介紹CFI策略。

3.1 代碼塊

代碼塊是由函數的Super-CFG(超級控制流圖)構成的。對于一個函數, 其Super-CFG是由其CFG(控制流圖)構成的(Super-CFG構造算法將在第四章討論)。通過對函數的Super-CFGs進行合并, 構建每個代碼塊的有向圖, 而這些代碼塊之間兩兩互斥。

我們通過圖2的示例進行說明。示例中二進制代碼原始函數包含:,,,和。是二進制程序的入口點。圖中的節點代表代碼中相應的指令。在圖中,CC表示由函數的Super- CFG生成的代碼塊。由于函數在指令3處被直接調用, 所以, 由指令5、6和7組成的函數也被包含在CC中。CC表示由函數的Super-CFG生成的代碼塊, 包含有四個結點。

我們將包含一個代碼塊中的結點分為三類: 根結點, 邊緣結點和內部結點。根結點表示間接調用函數的入口, 在圖中用灰色表示。例如, 1和5是CC的根結點。邊緣結點代表間接轉移指令, 其目標無法在計算Super-CFG時識別, 在圖中用條紋表示。例如, 2、4和6都是CC的邊緣結點。那些既不是根結點也不是邊緣結點的結點稱為內部結點, 在圖中用白色表示。這些內部結點所表示的指令是那些非控制流轉移指令或在Super-CFG中具有確定目標的控制流轉移指令, 例如3和7。

通過劃分結點, 我們可以將間接轉移分為兩類, 即塊內轉移和塊間轉移。塊內轉移是由內部結點發起的間接轉移, 塊間轉移則是由邊緣結點發起的間接轉移。值得注意的是, 在本文方法中, 我們確保每個間接轉移僅為塊內轉移或塊間轉移的一種, 從而實施不同粒度的約束策略。為了達到該目標, 我們在代碼塊構造前, 首先需要完成對一部分代碼的復制。

通常二進制代碼中的函數分為兩類, 即間接被調函數(ICF, Indirect Called Function)和直接被調函數(DCF, Direct Called Function)。但可能存在一些函數既是ICF又是DCF。我們將兩類交集部分的函數進行復制, 并將這部分新函數視為ICF。這樣, 所有函數最終被歸進兩個互斥的集合中, 如圖3所示。當程序運行時, 在一個間接調用位置, 當原始函輸被調用時, 我們會實時調度來執行復制的函數。這樣, 在程序運行過程中, 一個函數只會按照一種特定的方式被調用, 即間接或直接。因此, ret僅返回到一個特定類型的調用點。ret也被分成兩類: 直接ret和間接ret。其中直接ret返回到直接調用點; 間接ret返回到間接調用點。

在圖2示例代碼中, ICF由、、、組成, 而DCF由組成。只有函數被兩種方式同時調用。假設從開始執行,函數首先在9處被間接調用, 接著在3處被直接調用, 因此返回指令7應該分別返回到這兩個調用點。在BinCC中,是一個通過復制函數生成的新函數, 它在指令9處被間接調用。這使得指令7變成塊內轉移, 指令7’變成塊間轉移, 也就是說7作為直接ret應該返回到3處, 而作為間接ret, 則返回到9處, 如兩個虛箭頭所示。

3.2 CFI策略

本小節, 我們提出塊內策略和塊間策略來分別約束塊內和塊間的間接轉移。

塊內策略

這類策略是用來限制內部節點描述間接轉移的, 其轉移目標是由靜態分析確定的。這些目標通常存在于代碼塊的內部, 并且在構成代碼塊的Super-CFG中確定。在一個代碼塊中, 我們只關心兩種間接控制流轉移。一種是直接ret, 另一種是與switch-case跳轉表相關的間接jmp。對于每個直接ret, 合法的目標是處于當前代碼塊內的對應目標調用點。對于每個間接跳轉而言, 相應跳轉表中所有條件分支都是合法的目標。跳轉表中的條件分支可以通過靜態分析確定。我們在構造Super-CFG時, 將間接跳轉與它的條件分支相關聯來確定其目標。

如示例所示, 這些代碼塊中有一個塊內間接轉移, 即CC中7處的ret, 它只允許返回至調用點3。

塊間策略

這類策略用于限制邊界結點, 這些結點的轉移目標不能從Super-CFG中靜態確定。根據不同的轉移類型, 我們提出如下策略。

1. 間接call結點只能到達表示ICF入口點的根結點。

2. 間接ret結點只能回到表示間接call的邊界結點。

3. 間接跳轉結點, 不能通過靜態分析得出其目標, 但可以達到根結點或表示間接call的邊界結點。

這些策略的合理性在于: 在函數分成兩個互斥集合之后, ICF只能被間接調用指令調用, 因此, ICF的間接返回只能回到間接調用點。間接jmp通常與跳轉表相關, 這些跳轉的目標可以通過靜態分析得到。但是也有一些間接jmp無法通過靜態分析得到, 它們的目標可能是ICF的入口點或調用點。

圖2示例中, 根結點是ICF(1,5,5’,8和12)的入口點, 邊界結點包含間接call結點(2,6和9)和間接ret結點(4,11,13和7’)。在BinCC中, 間接call結點(2,6和9)可以調用ICF(1,5,5’,8和13), 間接ret(4,11,13和7’)可以返回到間接call(2,6和9)。

4 代碼塊構造

為了構建代碼塊, 我們首先在二進制程序中識別所有的DCF和ICF, 然后通過控制流分析來獲得這些函數的CFG, 進而計算Super-CFG, 并基于Super-CFG構建代碼塊。下面我們將進行深入討論。

4.1 DCF識別

每個DCF是通過一個相關的call調用。函數的入口地址可以由call指令的地址和操作數計算得出。按照這種思路, 我們可以獲得所有DCF。

4.2 ICF識別

程序中ICF地址與程序中存在的常量有關, 函數地址是常量自身, 或由常量計算而來。根據這一特點, 我們在程序中找出所有與函數地址相關的常量。

如果二進制程序中包含重定位表, 則所有被間接調用函數的地址信息都將在重定位表中。在這種情況下, 我們計算重定位表中的每個常量與代碼基地址的和, 如果結果在此代碼區中, 那么這個和即是一個ICF的地址。

對于不包含重定位表的二進制程序, 可能有non-PIC或PIC兩種情況。在這些情況下, 我們采用同樣的方法, 在二進制程序的常量中篩選ICF。

對于non-PIC模塊, 我們使用一個窗口值(例如: 在32-bit的系統中為4字節)在data、.rodata、.init_array、exported symbol sections和其他可能存在表示合法代碼地址的程序段中掃描常量。同時, 我們也在代碼域中收集常量。結合反匯編的結果, 如果此常量, 或常量與代碼基址的和是有效代碼地址, 且滿足一個合法指令的邊界, 我們就認為它是一個候選的ICF。

對于一個PIC模塊, 函數可以被PC thunks調用。因此, 除了與non-PIC執行相同的方法來收集常量之外, 我們還要識別出所有的PC thunks, 并查看是否被用作函數調用。如果是, 那么也將這些計算出來的函數地址, 加入到候選ICF中。

需要注意的是, 最終得到的常量并不都是ICF。一些常量實際上是switch-case跳轉表中條件分支的地址或函數調用點的地址。這些顯然不是函數入口點的地址, 所以我們去掉這兩種候選常量, 剩下的則是最終的ICF。

4.3 控制流分析

接下來我們通過靜態控制流分析來計算所有函數的CFG。我們嘗試識別每個間接分支的所有可能的目標。這部分的主要困難在于如何分辨出間接跳轉中所有可能的目標。在大多數常見的情況下, 間接jmp是用于跳轉表的調度執行, 這個跳轉的所有合法目標是對應的條件分支, 這些可以通過之前發現的常量識別出來。因此, 對間接jmp來說, 檢查它是否與跳轉表相關, 如果相關則將它與對應條件分支聯系起來。

4.4 代碼復制

如上所述, 為了實現本文提出的CFI策略, 我們需要復制ICF和DCF的交集。然而, 某些情況下, 由于編譯器的優化, 一些ICF可能與DCF擁有相同的ret, 并且這些ret無法明確地定義其轉移類型(塊內或塊間)。為了解決這個問題, 如果ICF與DCF這兩個函數擁有同樣的ret, 我們復制ICF。這個復制的函數在二進制程序中會成為一個新的ICF。

接下來我們復制函數CFG中所有指令。在添加CFI校驗策略(在第五章討論)之后, 所有代碼被放置于新增的代碼段中。源代碼段被標記為不可執行, 而所有數據段都保持不變。被復制指令中的常量也沒有變動, 所以這些指令在執行時, 仍然能夠保證對數據段的正常訪問。

4.5 代碼塊構造

代碼塊由函數的Super-CFG構成。算法1說明了如何計算一個函數的Super-CFG。首先在函數中定位所有的直接調用, 然后通過在直接調用處加上被調用函數的Super-CFG。這個函數引入了兩條新的邊, 一條由直接調用點(如, 結點i)指向被調用點的入口, 另一條由被調用點返回到直接調用點。

算法1.SuperCFG(CFGfunc): 基于CFG計算func的Super-CFG

輸入: CFGfunc: 計算func函數的CFG

輸出: sg: func的super-graph

1: sg ← CFGfunc2: FOReachi∈Nodes(CFGfunc) DO3: IFiis a direct call to fTHEN4: sg = AddSuperCFG(sg,i,SuperCFG(f))5: END IF6: END FOR7: RETURN sg

算法2顯示了如何構造代碼塊和在代碼塊中劃分結點。此算法的輸入為所有ICF。首先為每個ICF計算Super-CFG, 然后基于共同邊(例如, 共同的被調用點)合并這些Super-CFG。這一過程通過實現。這個算法最終得到互斥的代碼塊集合, 每一個代碼塊包含了劃分好的根結點、邊界結點和內部結點。從此算法中可以看到, 根結點來自當前塊中ICF的入口點。邊界結點來自間接調用()、間接返回()和無法靜態確定目標的間接跳轉(ijmp)的結點。內部結點則由表示當前塊中非控制流指令和目標已確定的控制流轉移結點組成。

我們還考慮了一種特殊的情況, 即可能存在無法進行靜態控制流分析的孤立的代碼塊。例如, 一個間接跳轉未的目標分支或是dead code等??紤]到這部分代碼也可能執行, 所以也需要制定CFI約束。為此, 我們為每個孤立代碼塊生成了一個代碼塊。孤立代碼塊由Super-CFG組成, 此Super-CFG由算法1生成。其中, 代碼塊中的指令被看作“CFG”(記作CFGfunc)。并且, Super-CFG中的(如), 是為邊界結點, 它由、和ijmp組成。Super-CFG的(如)為內部結點??紤]到孤立塊不能作為函數, 因此不能被間接call調用。我們將孤立塊中的入口當作邊界結點, 而不是根結點。

算法2.ConstructCC(ICFs): 將ICFs作為輸入生成出代碼塊

輸入: 所有ICFs: ICFs

輸出: 所有代碼塊: CC

3:=(CFG);=

5: IFis aicall or aijmpor aTHEN

6:={}

7: ELSE

8:={}

9: END IF

10: ENDFOR

12: ENDFOR

14:cc=;SG=SG{}

15:cc.border=cc.border{}

16:cc.inner=cc.inner{}

17:cc.root=cc.root{}

19: IF(,) THEN

20:cc=(cc,)

21:cc.border=cc.border{}

22:cc.inner=cc.inner{}

23:cc.root=cc.root{}

24:SG=SG{}

25: END IF

26: ENDFOR

28: ENDFOR

29: RETURN

5 CFI實施

在構造代碼塊之后, 利用二進制重寫方法來寫入CFI約束規則。此部分主要通過對BinCFI進行擴展來實現。下面, 我們首先簡要闡述BinCFI的基本架構, 然后再詳細討論為實施本文CFI策略所做的擴展和修改。

5.1 基礎架構

BinCFI基于反匯編結果對間接指令進行改寫, 將最終的代碼插入到新生成的代碼段中, 并修改原代碼段使其不可執行。對于原代碼段中的每一條指令, BinCFI使用形如的地址對, 將這條指令在原代碼段中的地址關聯到新代碼段的地址。這些地址對用來進行從原代碼段到新代碼段中的地址轉換。BinCFI對所有間接轉移目標產生地址對, 并用兩個不同地址轉換哈希表存放, 。其中, 一個哈希表用于維護ret指令的合法目標, 另一個用于維護間接jmp和call的合法目標。所有地址轉換表都是只讀屬性。

BinCFI對間接call/jmp和ret進行改寫。對于與跳轉表相關聯的間接jmp, 它們的操作數被改寫為*(CE+Ind)+CE這種形式, 其中CECE為常量,CE表示跳轉表相關,*(CE+Ind)表示所有可能的條件分支。另外, BinCFI基于每個CE引入一個新的跳轉表, 用來存放轉換后的條件分支地址。對于剩下的間接轉移, 改寫過程如圖4所示, 首先將運行目標(例如, %eax)存到一個線程局部變量中(例如, %gs:0x40), 接著執行地址轉換例程。

圖5顯示的處理過程。首先檢查轉移是否違反CFI規則。如果沒有, 則進行地址轉換。%gs:0x40中存放的是原代碼段的地址, 即orig_addr。BinCFI通過相關地址轉換表尋找對應的轉換地址, 即new_addr。如果找到, 則跳轉到new_addr執行。否則, 調用全局地址轉換例程, 來完成不同模塊間的地址轉換。

全局地址轉換例程通過查詢GTT(全局轉換表)來進行地址轉換。對于每個加載模塊, GTT記錄了模塊基地址和地址之間的對應關系。在上述例子中, 通過%gs:40可以獲知目標模塊, 如果GTT中沒有找到該模塊, 則觸發警告; 如果找到, 控制流則轉移到目標模塊的例程, 接著進行地址檢測和轉換操作。全局地址轉換例程和GTT都被加入載入程序中, 且每次加載到不同的內存地址。另外, 當運行時加載一個新模塊, 也會更新GTT, 加入這個模塊的相關信息。

5.2 擴展架構

為實現本文CFI策略, 我們對BinCFI進行了擴展和修改。

擴展地址轉換表

我們對地址轉換表進行兩項擴展。首先, 通過代碼復制, 將新引入的間接轉移目標引入新表項, 其中包含復制的函數和返回調用點。接著, 修改復制函數的有關表項。因此, 如果原函數在運行時被調用, 則實際執行的是對應的新函數。

我們以圖2代碼中的foo為例進行說明。圖6(a)展示了BinCFI記錄的foo地址對, 圖6(b)顯示BinCC的擴展, 在表中加入了函數入口<5’, 5’>與調用點<6’, 6’>。其中, 5_new被修改成5’。當foo在被間接調用時, 將實際執行foo’。

雖然代碼復制會引入新的間接轉移目標, 但并不會影響保護效果。由于新加入目標的數量很少, 因此僅需復制很小比例(見6.1節)的函數。此外, 新的控制流轉移也使用的相同的方法, 實驗結果表明, 不會對保護效果造成影響。

塊內策略實施

這種策略用來約束代碼塊中代表間接轉移的內部結點, 如直接ret和跳轉表相關的間接jmp。它們的目標是確定的, 可以從代碼塊構成的Super-CFG中獲得。

為了約束直接ret, 我們為每個ret都準備了一個獨立的地址轉換哈希表來存儲合法目標。對每個直接ret, 其目標為與Super-CFG相關聯的調用點。對直接ret指令的改寫過程如圖7所示。插在兩個指令中的和表明了對應地址轉換表的具體位置。我們用另一個線程局部變量%gs:0x50來存儲第一個指令地址——。轉換例程在運行時使用%gs:0x50來獲取和以定位地址轉換表, 接著進行地址校驗和轉換操作。

對與跳轉表相關的間接jmp, 也可以使用相似的結構。然而, BinCFI已經實現了對這類jmp轉移的約束, 所以, 我們沿用BinCFI對這類jmp的改寫方式。

塊間策略實施

我們按照3.2節中所述的策略, 對相應執行進行改寫。改寫方式與圖4相似。每種控制流轉移都有自己的地址轉換表和地址轉換例程來實現地址檢查與轉換。

我們需要對PLT條目中的間接jmp進行特殊處理。這類跳轉用于動態符號解析。一般只有兩種合法目標, 一種是下一條指令的地址, 也就是在符號解析之前初始化的值; 另一種是在運行時確定的符號解析地址。在這些動態地址解析后, 對這些jmp做出約束, 使其只能跳轉至對應的動態解析符號地址。此外, 我們把間接跳轉的所有目標安排在一個地址轉換表中, 并將表放在新引入的只讀數據域中。另外, 通過修改加載器使其能將數據域的屬性改為可寫, 然后用符號解析后的解析地址更新對應表項, 最后將可寫屬性改回。

除此之外, , 我們將孤立塊中的間接ret定義為孤立ret。由于不能通過靜態分析識別孤立塊的調用方, 因此, 允許孤立ret返回至任何函數調用點。

C++異常

我們還需要考慮C++異常。在C++程序中, 異常處理的必要信息存儲在域中。當異常被觸發, 系統會使用當前執行的上下文來執行棧展開操作, 識別對應的catch分支。由于中沒有我們通過代碼復制引入新代碼的異常元數據, 當一個復制函數包含C++異常邏輯且異常被觸發時, 棧展開過程無法找到異常處理函數, 于是程序將會異常運行。為了避免這個問題, 我們不復制包含C++異常邏輯的函數, 允許這些函數中的ret返回至任意調用點。

6 實驗評估

我們通過從SPEC CPU 2006測試集編譯得到的二進制程序對BinCC進行評估。編譯器為GCC 4.6.1, 編譯優化級別為-O2。實驗環境為1.0GB內存、20G硬盤、單核的Ubuntu 11.10 32位虛擬機。

表1 復制函數統計數據

6.1 代碼復制評估

CFI策略實施的一個關鍵操作是代碼復制。我們需要評估一下復制的代碼量。

表1列出了ICF和DCF的數量, 以及每個程序的所有函數和復制函數的總量。從中可以看到, 為了實現本文CFI策略, 我們僅需要復制少量的函數(約占所有函數的3.4%)。

另外從表1中, 我們發現C++程序(如omnetpp和soplex)通常比C程序需要更多的函數復制。在這些C++程序中, 大多數的復制函數是C++虛擬函數。函數指針在虛擬表中被識別為ICF, 它們既能被間接調用, 也能被直接調用(例如, 通過同一個類的繼承函數), 所以我們對這些函數進行復制。

圖8顯示了示例中需要復制指令的百分比。平均而言, 只需要復制7%的二進制指令。

6.2 間接轉移的度量

BinCFI引入平均間接目標縮減量(AIR)來評估CFI防護粒度。公式定義如下。

在這個式子里,T表示間接轉換i的合法目標集。表示二進制編碼的長度。對于BinCC, 因為需要對代碼進行復制, 因此, 編碼的長度、間接轉換的數目, 以及合法目標集T有可能會增加??紤]到這些因素, 計算AIR結果表明BinCFI為98.86%, 而BinCC為99.54%。

AIR這個評估指標有失客觀, 因為二進制中S要遠高于T, 這使得粗粒度的CFI方案都可以獲得一個較高的AIR值。所以, 我們提出了一種新的度量方法RAIR(Relative AIR)。RAIR用來說明BinCC與BinCFI比較, 對間接控制流轉移合法目標的縮減程度。

其中,T’表示BinCFI中間接轉換i的合理目標,T表示BinCC中i的合理目標。圖9列出了關于這個指標的一些統計數據。平均來說, BinnCC比BinCFI減少了81.34%的間接控制流轉移目標。

相比BinCFI, BinCC縮減了每種間接轉換的合理目標。我們用下面的表達式來評估每種間接控制流轉移的合法目標的平均數量。

在上式中,i是某個特定種類的間接控制流指令;T表示在CFI執行的二進制代碼中i的合法目標集; N表示二進制程序中所有間接控制流轉移的數量。我們計算三種間接控制流轉移的AVG值, 并計算, 即BinCC與BinCFI相比, 對控制流轉移合法目標縮減的百分比。圖10, 11, 12分別展示了BinCC對間接call, 間接jmp和間接ret縮減的百分比。

在圖10中, 相比BinCFI, BinCC為每一個間接call縮減了40%的合法目標。根據BinCFI實現, 所有可能的常量代碼指針是間接調用的潛在目標。然而一些常量實際上是函數返回地址和跳轉表中的分支地址, 并不是函數。我們在實現中已經把它們從目標集中移除。

對于間接jmp, 很多是在PLT中, 這些跳轉有明確的目標, 即被解析的符號地址。因此, 相比BinCFI, BinCC為間接jmp減少了35%的合法目標集, 如圖11所示。

如圖12所示, 相比BinCFI, BinCC為ret指令平均減少了87%的合法目標。從圖中可以看到, gcc提升幅度最大。這是由于在這個二進制程序中, 直接ret比間接ret多, 且每一個直接ret的合法目標遠少于間接ret的合法目標。

6.3 ROP攻擊評估

由于ASLP和DEP的引入, ROP已成為攻擊者常用的攻擊手段。CCFIR和BinCFI等CFI解決方案能夠有效緩解傳統ROP攻擊, 然而, 研究工作[5,10]表明在一些情況下攻擊者仍然能夠通過call-preceded gadget繞過CFI的防護, 實施ROP攻擊。

為了評估BinCC在抵御ROP攻擊的能力, 我們提出評估指標——GS(GadgetSurvivability)來說明在CFI防護下, 利用call-preceded gadget實現ROP的難度。定義如下。

這個指標旨在評估: 假設在一個CFI加固的二進制程序中, 一條ret指令被攻擊者完全掌控, 那么這個ret能夠返回到一個call-preceded gadget的幾率。

定義中C表示在CFI策略下ret指令r的合法目標集,表示所有的ret指令構成的集合,表示由所有call-preceded gadget構成的集合。對于ret指令r, 能夠達到|C|個call-preceded gadget。所以, 它被控制并且能返回一個call-preceded gadget的概率是。在考慮所有ret指令之后, 平均概率是或。

對于BinCC來說, 對于每一個r, |C|等于||, 所以這個指標值是100%。這符合實際情況, 即: 攻擊者能夠在CFI下返回到任一call-preceded gadget。

表2 BinCC與BinCFI中可用的gadget比例對比

對于BinCC而言, GS值將很小。因為BinCC對直接ret和間接ret, 都顯著縮減了它們的合法目標。當被控制的ret是一個間接ret時, 它可以返回到任一間接調用點; 如果ret是一個直接ret, 則僅允許返回至以特定直接調用點起始的call-preceded gadget。表2展示了BinCC和BinCFI對所有的測試用例求得的GS值。從表中可知, BinCC顯著降低了利用 call-preceded gadget 實現ROP攻擊的可能。具體而言, 與BinCFI的100%相比, BinCC只有大約0.7%的可能。

6.4 性能開銷

我們對空間和時間的開銷進行評估。對于空間開銷, 主要比較BinCC和BinCFI地址轉換表的增長量, 同時對增加的編碼長度及所有文件比原始文件的增加量進行評估。對于時間的開銷, 我們先執行BinCC和BinCFI, 然后進行比較。

6.4.1 空間開銷

由于采用相同的BinCC架構來改寫二進制程序, 因此文件大小增加的原因和BinCFI的相同, 即新增的代碼段和地址轉換哈希表。對于BinCC, 新代碼段較原始代碼長度增加了1.4倍; 對于BinCFI, 這部分增加了1.2倍。BinCC引入了四種表存儲間接call, 間接jmp, 間接ret和直接ret的合法目標, 而BinCFI只使用兩種表來存儲間接call、jmp和ret??傮w而言, BinCC引入的表的大小比BinCFI減小了20%。這是由于哈希表大小是2的冪, 而BinCC顯著縮減了間接控制流的合法目標, 使得表的大小小于BinCFI中表的大小。另外, BinCC生成文件的大小比原始的二進制大小增加125%, 比BinCFI低了14%。

6.4.2 時間開銷

圖13展示了由BinCC和BinCFI加固的二進制程序的實際運行時間。在實驗環境中, BinCC加固的程序較原始程序在運行時間開銷上增加22%, 較BinCFI加固的程序僅增加4%。這是因為BinCC在對間接控制流指令進行替換時, 增加了額外的指令, 來實現更嚴格的CFI約束策略??紤]到數據和指令的對齊會對程序執行效率產生影響, 所以, 我們在二進制重寫操作前, 對一些數據和指令進行必要的對齊操作, 以此來提升訪問數據和指令執行的效率, 減少時間開銷。我們將在后續工作中進行完善。

7 討論

較已有的二進制CFI解決方案, BinCC對間接控制流轉移進行了更為細粒度的約束, 顯著縮減了ret的合法目標, 使得可利用的call-preceded gadget顯著減少。然而, 對于indirect call轉移目標的約束, 盡管已經將轉移目標限制在間接被調函數, 但是仍然存在被攻擊者利用的可能。攻擊者可以將indirect call操作數篡改為合法目標集合中的任何一個地址, 繞過對indirect call的CFI的檢測, 可能造成程序運行崩潰或拒絕服務攻擊等。這是針對二進制程序的CFI解決方案共同面臨的問題。由于不依賴程序源代碼和調試符號信息, 所以無法構造完整CFG, 也無法準確確定indirect call的目標函數。研究工作[18]針對特定一類的間接函數(C++虛函數)的調用提出了約束和檢驗方法, 具體是對調用虛函數的indirect call進行加固, 僅允許調用目標為它所在類或其他同類族(class hierarchy)中的虛函數。在下一步工作中, 我們可以結合這種方法進一步提升BinCC對控制流指令的約束粒度。

另外, 當前BinCC不支持動態生成代碼(例如JIT代碼, 或者混淆代碼等)。而這些問題也是面向二進制CFI解決方案存在的共同問題。我們將在下一步工作中對這部分內容進行系統的研究。

8 小結

對于面向二進制程序的CFI方法, 由于不依賴于程序源代碼和調試符號, 所以已有解決方案采用粗粒度的CFI策略, 雖然能夠防范普通控制流劫持攻擊, 但是對于復雜ROP攻擊防護能力有限。攻擊者仍然能夠構造利用繞過CFI規則的ROP鏈, 實現目的。

本部分提出了一種新的CFI解決方案BinCC, 為二進制程序實施細粒度的控制流完整性校驗。該方法通過復制部分程序代碼和靜態分析, 將二進制程序劃分為多個互斥的代碼塊。進一步將二進制程序中所有的控制流指令歸類為代碼塊內轉移指令和代碼塊間的轉移指令, 并為這兩種類型的控制流指令分別實施不同的轉移約束條件。BinCC嚴格地限定了間接控制流轉移的合法目標, 顯著提高控制流轉移的防護效果。為了檢驗我們所提出的控制流完整性約束的策略, 我們提出若干新的評估指標, 并將BinCC和BinCFI進行了比較。實驗結果表明, BinCC具有較低的時間開銷和空間開銷。

致謝 我們感謝幫助完成這篇論文的匿名評論者的反饋。這項工作是在Minghua Wang作為美國雪城大學的訪問學生時完成的。本研究由國家科學基金會資助# 1054605, 美國空軍研究實驗室資助# FA8750- 15-2-0106, 中國的國家基礎研究發展計劃資助# 2012 CB315804, 中國的國家自然科學基金資助# 91418206, 以及中國國家留學基金委(CSC)等機構出資支持。本材料中任何意見、發現和結論都屬于作者, 不代表資金會機構的觀點。

[1] M. Abadi, M. Budiu, ú. Erlingsson, and J. Ligatti. Control-flow integrity principles, implementations, and applications., 13(1): 4, 2009.

[2] S. Andersen and V. Abella. Data execution prevention. changes to functionality in microsoft windows xp service pack 2, part 3: Memory protection technologies, 2004.

[3] T. Bletsch, X. Jiang, and V. Freeh. Mitigating code-reuse attacks with control-flow locking.In Proceedings of the 27 Annual Computer Security Applications Conference, pages 353–362. ACM, 2011.

[4] T. Bletsch, X. Jiang, V. W. Freeh, and Z. Liang. Jump-oriented programming: a new class of code-reuse attack., pages 30–40. ACM, 2011.

[5] N. Carlini and D. Wagner. Rop is still dangerous: Breaking modern defenses., 2014.

[6] Y. Cheng, Z. Zhou, M. Yu, X. Ding, and R. H. Deng.Ropecker: A generic and practical approach for defending against rop attacks., 2014.

[7] L. Davi, A.-R. Sadeghi, and M. Winandy. Ropdefender: A detection tool to defend against return-oriented programming attacks., Computer and Communications Security, pages 40–51. ACM, 2011.

[8] U. Erlingsson, M. Abadi, M. Vrable, M. Budiu, and G. C.Necula. Xfi: Software guards for system address spaces., pages 75–88. USENIX Association, 2006.

[9] I. Evans, S. Fingeret, J. González, U. Otgonbaatar, T. Tang, H. Shrobe, S. Sidiroglou-Douskos, M. Rinard, and H. Okhravi. Missing the point (er): On the effectiveness of code pointer integrity1., 2015.

[10] E. Goktas, E. Athanasopoulos, H. Bos, and G. Portokalidis. Out of control: Overcoming control-flow integrity., 2014 IEEE Symposium on, pages 575–589. IEEE, 2014.

[11] D. Jang, Z. Tatlock, and S. Lerner. Safedispatch: Securing c++ virtual calls from memory corruption attacks., 2014.

[12] V. Kuznetsov, L. Szekeres, M. Payer, G. Candea, R. Sekar, and D. Song. Code-pointer integrity., 2014.

[13] S. McCamant and G. Morrisett. Evaluating sfi for a cisc architecture., page 15, 2006.

[14] V. Mohan, P. Larsen, S. Brunthaler, K. Hamlen, and M. Franz. Opaque control-flow integrity., 2015.

[15] B. Niu and G. Tan. Modular control-flow integrity., page 58. ACM, 2014.

[16] B. Niu and G. Tan. Rockjit: Securing just-in-time compilation using modular control-flow integrity., pages 1317–1328. ACM, 2014.

[17] M. Payer, A. Barresi, and T. R. Gross. Fine-grained control-flow integrity through binary hardening., and Vulnerability Assessment, 2015.

[18] A. Prakash, X. Hu, and H. Yin. vfguard: Strict protection for virtual function calls in cots c++ binaries., NDSS, volume 15, 2015.

[19] A. Prakash, H. Yin, and Z. Liang. Enforcing system-wide control flow integrity for exploit detection and diagnosis. In Proceedings of the 8th ACM SIGSAC symposium on Information,, pages 311–322. ACM, 2013.

[20] R. Roemer, E. Buchanan, H. Shacham, and S. Savage. Return-oriented programming: Systems, languages, and applications., 15(1): 2, 2012.

[21] H. Shacham. The geometry of innocent flesh on the bone: Return-into-libc without function calls (on the x86)., pages 552–561. ACM, 2007.

[22] P. Team. Pax address space layout randomization, 2003.

[23] C. Tice, T. Roeder, P. Collingbourne, S. Checkoway, ú. Erlingsson, L. Lozano, and G. Pike. Enforcing forward-edge control-flow integrity in gcc&llvm., 2014.

[24] R. Wahbe, S. Lucco, T. E. Anderson, and S. L. Graham.Efficient software-based fault isolation., volume 27, pages 203–216.ACM, 1994.

[25] B. Yee, D. Sehr, G. Dardyk, J. B. Chen, R. Muth, T. Ormandy, S. Okasaka, N. Narula, and N. Fullagar. Native client: A sandbox for portable, untrusted x86 native code., 2009 30th IEEE Symposium on, pages 79–93. IEEE, 2009.

[26] C. Zhang, T. Wei, Z. Chen, L. Duan, L. Szekeres, S. McCamant, D. Song, and W. Zou. Practical control flow integrity and randomization for binary executables., 2013 IEEE Symposium on, pages 559–573. IEEE, 2013.

[27] M. Zhang and R. Sekar. Control flow integrity for cots binaries., pages 337–352, 2013.

王明華 現為Baidu X-Lab高級研發工程師。2016年1月在中國科學院大學計算機應用技術專業獲得博士學位。研究興趣包括: 程序安全分析、軟件漏洞分析等。Email: wmhfzh@163.comHeng Yin 美國雪城大學副教授。研究領域包括惡意代碼檢測和分析、軟件漏洞分析、移動應用安全性分析、虛擬化安全等。Email: heyin@syr.edu Abhishek Vasisht Bhaskar 現為美國雪城大學碩士研究生。研究興趣包括:程序安全分析、虛擬化安全等。Email: abhaskar@syr.edu蘇璞睿 中國科學院研究員、博士生導師, 研究方向為可信計算與信息保障。研究興趣包括: 惡意代碼動態逆向分析、移動終端應用安全、軟件漏洞分析與評估等。Email: supurui@tca.iscas.ac.cn 馮登國 中國科學院研究員、博士生導師, 研究方向為可信計算與信息保障。研究興趣包括: 密碼算法設計與分析、安全協議設計與分析、PKI理論與關鍵技術、可信計算理論與關鍵技術、信息與網絡安全等。Email: feng@tca. iscas.ac.cn

Binary Code Continent: Finer-Grained Control Flow Integrity for Stripped Binaries

Minghua Wang1,2,4, Heng Yin2, Abhishek Vasisht Bhaskar2, Purui Su1,3, Dengguo Feng1

1Trusted Computing and Information Assurance Laboratory, Institute of Software, Chinese Academy ofSciences2Syracuse University3State Key Laboratory of Computer Science, Institute of Software, Chinese Academy of Sciences4University of Chinese Academy of Sciences

Control Flow Integrity (CFI) is an effective technique to mitigate threats such as code-injection and code-reuse attacks in programs by protecting indirect transfers. For stripped binaries, a CFI policy has to be made conservatively due to the lack of source code level semantics. Existing binary-only CFI solutions such as BinCFI and CCFIR demonstrate the ability to protectstripped binaries, but the policies they apply are too permissive, allowing sophisticated code-reuse attacks. In this paper, we propose a new binary-only CFI protection scheme called BinCC, which applies static binary rewriting to provide finer-grained protection for x86 stripped ELF binaries. Through code duplication and static analysis, we divide the binary code into several mutually exclusive code continents. We further classify each indirect transfer within a code continent as either an Intra-Continent transfer or an Inter-Continent transfer, and apply separate, strict CFI polices to constrain these transfers. To evaluate BinCC, we introduce new metrics to estimate the average amount of legitimate targets of each kind of indirect transfer as well as the difficulty to leverage call preceded gadgets to generate ROP exploits. Compared to the state of the art binary-only CFI, BinCFI, the experimental results show that BinCC significantly reduces the legitimate transfer targets by 81.34% and increases the difficulty for adversaries to bypass CFI restriction to launch sophisticated ROP attacks. Also, BinCC achieves a reasonable performance, around 14% of the space overhead decrease and only 4% runtime overhead increase as compared to BinCFI.

control flow integrity

TP309.7

王明華, 于2016年在中國科學院軟件研究所可信計算與信息保障實驗室獲得博士學位?,F為Baidu X-Lab高級研發工程師。研究領域為信息安全。研究興趣包括: 程序安全分析、軟件漏洞分析。Email: wmhfzh@163.com。

本課題得到國家科學基金會資助# 1054605, 美國空軍研究實驗室資助# FA8750-15-2-0106, 中國的國家基礎研究發展計劃資助# 2012 CB315804, 中國的國家自然科學基金資助# 91418206, 以及中國國家留學基金委(CSC)等機構出資支持。

2015-11-18; 修改日期: 2016-3-15; 定稿日期: 2016-4-23

DOI號 10.19363/j.cnki.cn10-1380/tn.2016.02.006

本文是Binary Code Continent: Finer-Grained Control Flow Integrity for Stripped Binaries(發表于ACSAC 2015)的擴展。

猜你喜歡
控制流二進制結點
LEACH 算法應用于礦井無線通信的路由算法研究
用二進制解一道高中數學聯賽數論題
基于八數碼問題的搜索算法的研究
抵御控制流分析的Python 程序混淆算法
基于返回地址簽名的控制流攻擊檢測方法
基于控制流的盒圖動態建模與測試
有趣的進度
二進制在競賽題中的應用
基于Petri網數據流約束下的業務流程變化域分析
二進制寬帶毫米波合成器設計與分析
91香蕉高清国产线观看免费-97夜夜澡人人爽人人喊a-99久久久无码国产精品9-国产亚洲日韩欧美综合