1本文系原創,轉載請說明出處
關注微信公眾號 信安科研人,獲取更多的原創安全資訊
網上已經有很多介紹angr的官方檔案的部落格,但是怎麼去用angr做一次有意義且成就感滿滿的分析的教學很少,目前比較常見的就是CTF中的應用,很少有從二進位制程式分析技術角度介紹。
同時,也沒有幾篇文章介紹angr對應的論文,這篇論文很棒很經典。
因此,本部落格從理論與實踐兩個方面介紹angr,以求讓讓自己更熟練掌握這個庫是怎麼用的,並應用到自己的工作中。
目錄
angr是加州大學聖巴巴拉分校Giovanni Vigna大佬領銜的安全實驗室的傑作,對應的論文發表在2016年的網路安全頂會IEEE S&P上,原論文連結
在這篇文章出來之前,安全界對程式進行漏洞分析的技術存在以下兩種問題:
(1)研究的重複工作太多。每一個安全分析研究工作都要重新實現並呼叫一些以前的技術,浪費時間。
(2)其次,由於複製這些系統所需的工作量不可接受,複製其他人的結果變得不切實際。結果,單個二進位制分析技術相對於其他技術的適用性變得模糊不清。再加上現代作業系統固有的複雜性,很難建立一個共同的比較基礎。
批註:可以看出來,發表在CCF A上的文章所解決的問題往往是能夠推動整個領域前進的問題,或者是解決一個領域內的巨大阻礙。同時,可以看到,真正的科研,往往是問題驅動,就是說解決一個問題而去做科研,往往是能夠做出比為了科研而去解決問題更多的成功。
因此,angr的實現的意義來了:
大佬們建立了angr以解決這些技術共用性的問題,整合了眾多文獻中最先進的二進位制分析技術。這樣做的目的是通過以一種可存取、開放和可用的方式實施當前研究工作中的有效技術,使該領域系統化,並鼓勵開發下一代二進位制分析技術,以便能夠輕鬆地相互比較。
angr 使用靜態和動態分析技術為多種型別的分析提供構建分析塊,以便可以輕鬆實現所提出的研究方法並比較這些方法之間的有效性。此外,這些構建的分析塊的組合能夠利用它們的不同的組合優勢。
angr做出的三個貢獻:
1) 在一個單一、連貫的框架中再現了攻擊性二進位制分析中的許多現有方法,以瞭解當前攻擊性二進位制分析技術的相對有效性。
2) 展示了將各種二進位制分析技術結合起來並大規模應用的困難(以及解決這些困難的方法)。
3) 將angr開源,以供後代研究二進位制程式碼分析之用。
靜態技術在不執行程式的情況下對程式進行推理。通常,程式會被抽象表示。例如,諸如記憶體佈局或甚至所採用的執行路徑之類的程式構造也可以被抽象表示。
這篇文章將靜態分析分為兩種樣式:
(1)將程式屬性建模成圖
(2)對程式資料本身進行建模的正規化
靜態分析的兩個缺點:
(1)首先,結果不可重複:靜態分析的檢測必須手動驗證,因為無法恢復有關如何觸發檢測到的漏洞的資訊。
(2)其次,這些分析往往在更簡單的資料域上操作,降低了對語意的洞察。簡言之,分析過於近似:雖然通常可以權威性地推理某些程式屬性(例如漏洞)的缺失,但在就漏洞是否存在而證明時,靜態分析的誤報率很高。
控制流圖 (CFG) 的恢復是幾乎所有用於漏洞發現的靜態技術的先決條件,其中圖節點是基本的指令塊,邊緣是指令塊之間可能的控制流傳輸。
主流的CFG圖的生成方法為遞迴演演算法,例如,從基本塊BA開始,分解並分析BA基本快,識別可能的出口點BC、BD,那麼就將BC、BD接到BA上,然後從BC、BD遞迴地重複分析,直到識別不出新的出口點。
CFG控制流恢復有一個到目前為止都存在的挑戰點:指令的間接跳轉。 與直接跳轉不同,在直接跳轉中,目標被編碼到指令本身中,因此很容易解析,而間接跳轉的目標可以基於許多因素而變化,如連結庫。
具體來說,間接跳轉分為幾類:
- 計算跳轉。如跳轉表,跳轉表中的索引地址需要依據當時執行的暫存器或者記憶體的值來確定。
- 上下文敏感。間接跳轉可能取決於應用程式的上下文。常見的例子是標準C庫中的qsort()——該函數接受一個回撥,用於比較傳入的值。因此,qsort()中基本塊的一些跳轉目標取決於其呼叫方,因為呼叫方提供回撥函數。
- 物件敏感(object-sensitive)。上下文敏感的一種特殊情況是物件敏感。在物件導向的語言中,物件多型性需要使用虛擬函式,通常實現為在執行時查詢的函數指標的虛表,以確定跳轉目標。因此,跳轉目標取決於其呼叫方傳遞給函數的物件的型別。
angr對如上的CFG圖恢復工作的難點一個個攻克,並定義了CFG恢復技術的兩個屬性:
(1)可靠性。如果在生成的圖中表示所有潛在控制流傳輸的集合,則認定這個CFG圖恢復技術是合理的。
(2)完整性。指CFG圖恢復成一個其中所有邊都表示實際可能的控制流傳輸的CFG圖。
通過分析程式屬性圖可以發現程式中的一些漏洞。程式屬性圖(例如,控制流圖、資料流圖和控制依賴圖)可用於識別軟體中的漏洞。最初應用於原始碼的相關技術
一種代表方法:Ø值集分析(Value-Set Analysis, VSA)。VSA嘗試識別程式中任何給定點的程式狀態(即記憶體和暫存器中的值),這可以用來解決間接跳轉的可能目標,或記憶體寫入操作的可能目標識別問題。雖然這些近似值缺乏準確性,但它們是比較可靠的。也就是說,它們可能過近似,但決不可能能欠近似。
例如:使用值集分析,通過分析記憶體讀寫的近似存取模式,可以在二進位制檔案中識別變數和緩衝區的位置。完成後,可以分析恢復的變數和緩衝區位置,以找到重疊的緩衝區。例如,這種重疊緩衝區可能是由緩衝區溢位漏洞引起的,因此每次檢測都是一個潛在漏洞。
該論文將動態分析技術分為兩大類:
具體執行和(動態)符號執行。
具體執行的代表方案就是Fuzzing,也就是我們常說的模糊測試。這裡不多介紹其概念,這裡介紹幾種現有的fuzzing的種類:
(1)基於覆蓋率的fuzzing。基於程式碼覆蓋率的模糊測試技術嘗試產生的輸入用例,使目標應用程式中執行的程式碼量最大化,因為執行的程式碼越多,執行易受攻擊程式碼的可能性越高。經典的工具有AFL。
基於覆蓋的Fuzzing缺乏對目標應用程式的語意洞察。這意味著,雖然它能夠檢測到某段程式碼尚未執行,但它無法理解輸入的哪些部分發生變異以導致程式碼被執行。
(2)基於汙點分析的Fuzzing。這樣的模糊測試工具分析應用程式如何處理輸入,以瞭解輸入的哪些部分在未來執行時需要修改。
符號技術彌合了靜態和動態分析之間的差距,並提供了一種解決方案來應對模糊測試的有限語意洞察力。動態符號執行是符號執行的一個子集,是一種動態技術,因為它在模擬環境中執行程式。
然而,這種執行發生在符號變數的抽象域中。當這些系統模擬應用程式時,它們在整個程式執行過程中跟蹤暫存器和記憶體的狀態以及對這些變數的約束。每當到達條件分支時,執行分支並遵循兩條路徑,將分支條件儲存為對採用分支的路徑的約束,並將分支條件的逆作為對未採用分支的路徑的約束。
給定一個特定的程式,angr執行一個從程式的切入點開始的迭代的CFG圖恢復技術,並進行一些必要的優化。angr利用強制執行、向後切片和符號執行的組合,在可能的情況下,恢復每個間接跳轉的所有跳轉目標。此外,它還生成並儲存了關於目標應用程式的大量資料,這些資料可以用於以後的其他分析,如資料依賴跟蹤。
該演演算法有三個主要缺點:它速度慢,不能自動處理「死程式碼」,而且可能會錯過只有通過未恢復的間接跳轉才能到達的程式碼。
為了解決這個問題,angr建立了一個輔助演演算法,該演演算法使用二進位制檔案的快速反組合技術(不執行任何基本塊),然後使用啟發式演演算法來識別函數、函數內控制流和直接的函數間控制流轉換。然而,次要演演算法的準確性要低得多——它缺乏關於函數之間可達性的資訊,對上下文不敏感,並且無法恢復複雜的間接跳躍。
在本節將討論angr稱為 CFGAccurate 的高階恢復演演算法,以及快速演演算法 CFGFast。
angr首先對被測試的二進位制檔案做了幾個假設,以優化演演算法的執行時間:
做這些假設的目的是假設這些被測試的程式能夠正常執行, 在分析混淆或異常的二進位制檔案時(如惡意程式碼),可以不考慮這些假設,但這會導致 CFG 恢復的執行時間更長。
具體來說,使用了四種技術:強制執行、輕量級反向切片、符號執行和值集分析。 要通過這些技術迭代恢復的 CFG,在應用程式的入口點使用基本塊進行初始化。
在 CFG 恢復過程中,CFGAccurate 維護一個間接跳轉列表 Lj,其跳轉目標尚未確定。當分析識別出這樣的跳轉時,將被新增到 Lj中。
在每輪迭代技術結束後,CFGAccurate 觸發列表中的下一個跳轉。 下一輪的技術可能會解決 Lj 中的跳轉問題,可能會向 Lj 新增新的未解決跳轉,並且也可能會向 CFG中新增基本塊和邊。
當所有技術的執行使得 Lj 或 C 沒有變化時,CFGAccurate 終止,因為這意味著任何可用的分析都無法解決進一步的間接跳轉。
定義: 強制執行確保條件分支的兩個方向都將在每個分支點執行,就是說,if(a>0)會有兩個分支,這兩個分支都會執行。angr中參考的強制執行技術的原論文
定義的引數:基本塊的工作列表 Bw 和分析塊的列表 Ba,CFG圖 C。
分析開始時,使用 C 中但不在 Ba 中的所有基本塊初始化其工作列表。每當 CFGAccurate 分析此工作列表中的基本塊時,基本塊和來自該塊的任何直接跳轉都會新增到 C 中。
但是,不能以這種方式處理間接跳轉。在強制執行下,間接跳轉的目標可能與程式實際執行的目標不同,因為強制執行會以意想不到的順序執行程式碼。因此,每個間接跳轉都儲存在列表 Lj 中以供後續分析。
由於它無法解決任何間接跳轉,因此該分析用作 快速處理的 CFG 恢復分析,以快速地為其他分析提供檢測到的基本塊和未解決的間接跳轉。
對於每個跳轉J∈ Lj,CFGAccurate向後(一般來說是從下往上)遍歷CFG,直到找到第一個合併點(即,在通往間接跳轉的路上「萬路歸一」的點)或達到閾值塊數(根據研究經驗,發現合理的閾值為8)。在此基礎上,CFGAccurate對間接跳轉執行正向符號執行,並使用約束求解器檢索間接跳轉目標的可能值。
CFGAccurate中規定:如果計算出的可能的目標集小於閾值大小,則跳轉成功解決。angr規定這個計算閾值為256,但在實踐中,在跳轉未成功解決的情況下,該值是不受約束的(這意味著,可能的目標集僅受地址中的位數限制)。
如果跳轉目標的問題解決,則從Lj中刪除J,併為跳轉目標的每個可能值將邊和節點新增到CFG中。
上面兩種方法是針對上下文不敏感的方式進行,如果引數為函數指標,並用該指標作為簡介跳轉的目標,則上面兩種分析是無法解決的。
這意味著CFG的生成需要一個具備上下文敏感的元件。angr通過後向切片來實現這一點。切片擴充套件到上一個呼叫上下文的開頭。也就是說,如果正在分析的間接跳轉位於從Fb和Fc呼叫的函數Fa中,則切片將從Fa中的跳轉向後延伸,幷包含兩個開始節點:Fb開始處的基本塊和Fc開始處的基本塊。
然後,CfgAccurate使用Angr的符號執行引擎執行該切片,並使用約束引擎來識別符號跳轉的可能目標,對於跳轉目標的解集的大小具有相同的閾值256。如果跳轉目標被成功解析,則從Lj和表示控制流轉換的邊中移除跳轉,並且將目標基本塊新增到恢復的CFG中。
前面三階段的CFG圖恢復技術知識能夠勉勉強強的識別二進位制檔案中函數的位置和內容,但是缺乏控制流的表示。
CFGFast旨在快速恢復具有高覆蓋率的CFG,而不必考慮瞭解函數之間的可達性。分為三個步驟:
上面四個階段為一個完整的CFG恢復技術流程。
值集分析是一種二進位制程式的靜態分析技術,該技術結合數值分析和指標分析,使用一個成為值集抽象域來近似暫存器或抽象位置在每個程式點可能儲存的值,這個程式點一般稱為固定點(fix-point)。例如,對於向地址A的記憶體寫入操作,查詢並計算出的固定點中的A的值將包含所有可能的寫入目標的完整列表。
VSA演演算法原文
VSA的基本資料型別,即跨距間隔(strided interval),本質上是一組數位的近似值。它非常適合近似一組正常的具體值。然而,如果這些值在程式中被用作跳轉的目標,則跨步間隔的過度近似性質,會通過建立指向不應成為跳轉目標的地址的控制流轉換,在恢復的 CFG 過程中產生不合理性。
為了有效解決這個問題,angr開發了一種稱為「跨步區間集」的新資料型別,它表示一組未聯合在一起的跨步區間。
僅當跨步區間集合包含多於K個元素時,跨步區間集合才會被合併為單個跨步區間,其中K是可以調整的閾值。較高的K值能夠保持高精度,但代價是增加了分析的複雜性。
跟蹤分支條件有助於在條件退出後或合併過程中約束狀態中的變數,從而產生更精確的分析結果,跟蹤分支條件對應的技術為 ,然而,它不僅實現起來很複雜(通常導致在約束表示式中支援很少的算術運算),而且在現實中計算成本很高。
angr的解決方案是實現一個輕量級的代數求解器,該求解器基於處理一些仿射關係的模算術在跨步區間域上工作。當發現新的路徑謂詞時(即,當遵循條件分支時),嘗試簡化並求解它,以獲得路徑謂詞中涉及的變數的數位範圍。
然後,執行新生成的數位範圍與每個相應變數的原始值之間的交集。這能夠在遇到新的分支條件時不斷改進值集分析的結果,從而提高最終固定點的精度。
angr基於
分三個階段使用 VSA 進行記憶體損壞檢測:
angr中對應的VFG圖(CFG的增強版),就包含每個程式位置出的VSA固定點的資訊。VFG中包含的程式狀態以SimuVEX提供的抽象佈局(特別是SimAbstractMemory記憶體模型)呈現記憶體,記憶體中的值由Claripy提供的值集表示。angr通過分析記憶體存取可能採用的值範圍,對這些程式狀態中包含的資料執行了緩衝區重疊分析。
angr的動態符號執行模組主要基於
angr實現了在
UCSE標記在狀態中缺少上下文,為欠約束。當將此類欠約束資料用作指標時,將建立一個新的欠約束區域,並將指標指向新區域。這種「按需」記憶體分配允許對管理複雜資料結構的程式碼進行分析。當識別出安全違規(即寫入堆疊上儲存的返回地址)時,將檢查所涉及的值是否處於欠約束狀態。在某些情況下(即,如果所有涉及的資料都未得到充分約束),違規將被過濾為假陽性。
angr的改進如下:
最初的UCKLEE的實現並沒有將全域性記憶體存取視為受限。然而,這種記憶體是UCSE無法預測的程式上下文的一部分,因為在分析給定函數時,全域性資料可能已經被覆蓋。因此,angr將所有全域性資料標記為欠約束,從而降低了誤報率。
最初的UC-KLEE實現有幾個內建限制,以防止路徑爆炸。例如,它們將限制欠約束指標解除參照的深度,以避免通過從不終止的欠約束連結串列進行搜尋。angr增加了一個額外的限制器:當發現函數導致路徑爆炸時,中止對函數的分析。angr通過寫死限制來檢測這一點,當單個函數分支到這麼多條路徑上時,用立即返回來替換函數,並從該函數的呼叫位置回放分析。這通過避免路徑爆炸保持了分析的可操作性,但使分析更加不準確。
以上是angr的核心技術實現的介紹,可以說我這篇博文介紹的還是比較淺顯,因為從某種程度上來說,我只是在原論文翻譯的基礎上,新增了一點我的理解以及別人的理解,後面的我的部分博文將會針對部分angr的核心技術,進行相應的基礎知識介紹,以方便能理解angr是個多麼偉大的工作。
(本篇終)