CFI/CFG 安全防護原理詳解


1. 簡介

  • CFI: Control-Flow Integrity(控制流完整性)
  • CFG: Control Flow Guard(Windows的CFI實現)
  • CFG: Control-Flow Graph(控制流圖)
  • LTO: Link Time Optimization(鏈接時優化)

1.1 控制流攻擊歷史

控制流劫持是一種危害性極大的攻擊方式,攻擊者能夠通過它來獲取目標機器的控制權,甚至進行提權操作,對目標機器進行全面控制。當攻擊者掌握了被攻擊程序的內存錯誤漏洞后,一般會考慮發起控制流劫持攻擊。

  • shellcode

早期的攻擊通常采用代碼注入(shellcode)的方式,通過上載一段代碼,將控制轉向這段代碼執行。
在這里插入圖片描述

為了阻止這類攻擊,后來的計算機系統中都基本上都部署了NX/DEP(Data Execution Prevention)機制,通過限定內存頁不能同時具備寫權限和執行權限,來阻止攻擊者所上載的代碼的執行。

  • Return2libc/ROP

為了突破DEP的防御,攻擊者又探索出了代碼重用攻擊方式,他們利用被攻擊程序中的代碼片段,進行拼接以形成攻擊邏輯。代碼重用攻擊包括Return-to-libc、ROP(Return Oriented Programming)、JOP(Jump Oriented Programming)等。研究表明,當被攻擊程序的代碼量達到一定規模后,一般能夠從被攻擊程序中找到圖靈完備的代碼片段。
在這里插入圖片描述
在這里插入圖片描述

Return2libc/ROP利用return間接訪問,繞過了NX/DEP訪問。因為代碼並不會直接在堆棧上執行,而只是根據堆棧中的地址,間接跳轉到對應正常代碼段執行。

  • DOP

DOP(Data Oriented Programming)攻擊。隨着防護技術的發展,針對控制流的攻擊變得愈發困難。而不通過劫持控制流,而是針對數據流來進行攻擊的方式,如Non-control data(非控制數據)攻擊雖然顯示出了其潛在的危害性,但目前對針對數據流的攻擊還知之甚少,長久以來該攻擊手段可實現的攻擊目標一直被認為是有限的。實際上,非控制數據攻擊可以是圖靈完備的,這就是DOP攻擊。

類似於ROP,DOP攻擊的實現也依賴於gadgets。但二者有以下兩點不同:
1、DOP的gadgets只能使用內存來傳遞操作的結果,而ROP的gadgets可以使用寄存器。
2、DOP的gadgets必須符合控制流圖(CFG),不能發生非法的控制流轉移,而且無需一個接一個的執行。而ROP的gadgets必須成鏈,順序執行。

  • CFI

為了應對這些新型的控制流劫持攻擊,加州大學和微軟公司於2005年提出了控制流完整性(Control Flow Integrity, CFI)的防御機制。其核心思想是限制程序運行中的控制轉移,使之始終處於原有的控制流圖所限定的范圍內。具體做法是通過分析程序的控制流圖,獲取間接轉移指令(包括間接跳轉、間接調用、和函數返回指令)目標的白名單,並在運行過程中,核對間接轉移指令的目標是否在白名單中。控制流劫持攻擊往往會違背原有的控制流圖,CFI使得這種攻擊行為難以實現,從而保障軟件系統的安全。

CFI從實現角度上,被分為細粒度粗粒度兩種。細粒度CFI嚴格控制每一個間接轉移指令的轉移目標,這種精細的檢查,在現有的系統環境中,通常會引入很大的開銷。而粗粒度CFI則是將一組類似或相近類型的目標歸到一起進行檢查,以降低開銷,但這種方法會導致安全性的下降。

CFI對非控制數據的攻擊無能為力,但是這不妨礙我們詳細研究CFI的實現原理。

1.2 CFI的基本概念

了解CFI(Control-Flow Integrity),需要從CFG(Control-Flow Graph)講起。這里的CFG是基於靜態分析的用圖的方式表達程序的執行路徑(函數級別,非指令級別?):
在這里插入圖片描述

CFI並不會檢測CFG中所有的邊,為了降低開銷受檢測的邊應該越少越好。因此在CFG中只考慮將可能受到攻擊的間接call、間接jmp和ret指令作為邊。
在這里插入圖片描述

如上圖,綠色的靜態控制流路徑並不易受到攻擊,紅色的動態控制流路徑容易受到攻擊。靜態路徑就是直接跳轉,動態路徑就是間接跳轉的路徑。

  • 直接跳轉和間接跳轉

直接跳轉指令的示例如下所示:

CALL 0x1060000F

在程序執行到這條語句時,就會將指令寄存器的值替換為0x1060000F。這種在指令中直接給出跳轉地址的尋址方式就叫做直接轉移。在高級語言中, 像if-else,靜態函數調用這種跳轉目標往往可以確定的語句就會被轉換為直接跳轉指令。

間接跳轉指令則是使用數據尋址方式間接的指出轉移地址,比如:

JMP EBX

執行完這條指令之后, 指令寄存器的值就被替換為EBX寄存器的值。它的轉換對象為作為回調參數的函數指針等動態決定目標地址的語句。

  • 前向轉移(forward)和后向轉移(backward)
    在這里插入圖片描述

將控制權定向到程序中一個新位置的轉移方式, 就叫做前向轉移, 比如jmpcall指令。
而將控制權返回到先前位置的就叫做后向轉移, 最常見的就是ret指令。

將以上兩種分類方式結合起來:
前向轉移指令calljmp根據尋址方式不同, 又可以分為直接jmp, 間接jmp,直接call,間接call四種。
后向轉移指令ret沒有操作數,它的目標地址計算是通過從棧中彈出的數來決定的。正因為ret指令的特性,引發了一系列針對返回地址的攻擊。

CFI(Control-Flow Integrity)關注的就是間接jmp間接callret這幾種指令控制流的完整性。

1.3 CFI發展歷史

Control-Flow-Integrity這篇文章詳細的描述了CFI的發展歷史。
在這里插入圖片描述

上圖是CFI技術發展的歷史路線圖,其中代表性的有四種CFI技術,下圖是這四種技術在各個維度的一個比較:
在這里插入圖片描述

上圖,是各個CFI技術在安全性能四個維度的得分:

  • 一個是支持的控制流傳輸方案,比如前向后向、間接返回等等,用CF表示。
  • 二是性能數值,用1-10來區別,10為最高分,用RP來表示。
  • SAP.F是對前向控制流的靜態分析精度,
  • SAP.B是對后向控制流的分析精度。

2. Orig CFI

原始CFI技術都來源於這篇文章:Control-Flow Integrity Principles, Implementations, and Applications

這種技術的思想就是在就是間接jmp間接callret這幾種指令的控制流中插樁,在間接跳轉之前判斷跳轉地址是否合法。
在這里插入圖片描述

用上圖來解釋,利用左側的代碼生成了右側的CFG控制流圖。其中的直接call路徑是不用關注的,針對間接callret指令的控制流路徑,插入代碼進行判斷:

  • 1、在間接callret目標地址插入一個獨有的label id
  • 2、在間接callret指令之前插入一段樁代碼,來檢查目的地址的id是否合法。合法才能間接跳轉,不合法則出錯返回。
  • 3、還約定如果指向兩個目標地址的邊擁有相同的源集合的話,那么這兩個目標地址就是等價的,等價的目標用同一label表示。所以我們看到兩個相同的label 55和兩個相同的label 17。這就是一種粗粒度的CFI,它將多個不同的目標地址合在一起減少需要檢測目標地址的數量。為了降低性能開銷,是以犧牲安全性為前提的。

下圖是上述理論在x86上的一個具體實現:
在這里插入圖片描述

  • 原始狀態:ecx保存了目的地址,jmp ecx間接跳轉到目的地址執行
  • 插樁方式(a):首先在目的地址插入一個4字節ID 12345678h,然后在jmp跳轉前插入一段樁函數判斷,判斷目的地址的值是否為12345678h。不合法則出錯處理,合法則間接跳轉到[ecx + 4]地址執行原來的目的指令。
  • 插樁方式(b):在方式(a)的基礎上做了優化,首先在目的地址插入一個4字節的lable指令prefetchnta + 4字節ID 12345678h,然后在jmp跳轉前插入一段樁函數判斷,判斷[ecx + 4]地址的值是否為12345678h。不合法則出錯處理,合法則間接跳轉到[ecx]地址執行label ID指令。注意這里的技巧是判斷合法后,還是跳轉到ecx原地址,但是這時這個地址上存儲的是label ID指令,這條命令沒啥副作用,緊接着才會繼續執行原有的命令。

下圖是間接jmpret指令路徑,都被cfi插樁的情況:
在這里插入圖片描述

CFI確保運行時執行沿着給定的CFG進行,例如,保證典型功能的執行始終從頭開始,並從頭到尾進行。 因此,CFI可以提高任何基於CFG的技術的可靠性(例如,增強現有技術以防止緩沖區溢出和入侵檢測[32,58])。下面介紹基於CFI的其他應用,內聯參考監視器IRM(Inlined Reference Monitors)、SFI(Software Fault Isolation)、軟件內存訪問控制SMAC(Software Memory Access Control),我們在此介紹它們。 它還顯示了如何依靠SMAC或標准x86硬件支持來加強CFI實施。

下圖還展示了一個影子調用堆棧(shadow call stack)的原理,這是ret路徑上的另一種cfi保護形式:
在這里插入圖片描述

  • shadow call stack 在 ret路徑上不再使用判斷id是否正確的方式,而是把返回地址在另外一個堆棧另存了一份,這樣棧溢出漏洞無法覆蓋,就算堆棧溢出但是函數還是返回到原來的調用位置。
  • 在函數調用前的時候,把返回地址備份到shadow call stack
  • 在函數返回前,從shadow call stack中彈出備份的返回地址,廢棄掉原堆棧中的返回地址,這樣ret返回地址的安全性多了一層保障。

實現CFI,三個假設成立至關重要。 這三個假設是:

  • UNQ. 唯一ID:在CFI檢測之后,除了ID和ID檢查之外,選擇為ID的位模式不得出現在代碼存儲器中的任何位置。通過使ID足夠大(例如32位,對於合理大小的軟件)並且通過選擇ID使得它們不與軟件的其余部分中的操作碼字節沖突,可以容易地實現該屬性。
  • NWC. 不可寫代碼:程序必須無法在運行時修改代碼內存。否則,攻擊者可能能夠繞過CFI,例如通過覆蓋ID檢查。除了在加載動態庫和運行時代碼生成期間,NWC在大多數當前系統中已經是正確的。
  • NXD. 不可執行數據:程序必須不能像執行代碼那樣執行數據。否則,攻擊者可能會導致執行標有預期ID的數據。最新的x86處理器上的硬件支持NXD,Windows XP SP2使用此支持來強制分離代碼和數據[Microsoft Corporation 2004]。 NXD也可以用軟件實現[PaX Project 2004]。NXD本身(沒有CFI)阻止了一些攻擊,但不適於那些利用預先存在的代碼的攻擊,例如“jump-to-libc”攻擊。

2.1 Windows CFG的實現

Windows利用以上思想建立了自己的CFI防護機制CFG(Control Flow Guard)。在Win10安全特性之執行流保護繞過Windows Control Flow Guard思路分享等文章中有對CFG的實現原理進行過描述。

以win10 preview 9926中IE11的Spartan html解析模塊為例,看一下CFG的具體情況:
在這里插入圖片描述

最終實際運行的CFG檢查函數為ntdll!LdrpValidateUserCallTarget(),其檢測過程如下:

  • 1、首先從LdrSystemDllInitBlock+0x60處讀取一個位圖(bitmap),這個位圖表明了哪些函數地址是有效的。通過間接調用的函數地址的高3個字節作為一個索引,獲取該函數地址所在的位圖的一個DWORD值,一共32位,證明1位代表了8個字節,但一般來說間接調用的函數地址都是0x10對齊的,因此一般奇數位是不使用的。
  • 2、通過函數地址的高3個字節作為索引拿到了一個所在的位圖的DWORD值,然后檢查低1字節的0-3位是否為0,如果為0,證明函數是0x10對齊的,則用3-7bit共5個bit就作為這個DWORD值的索引,這樣通過一個函數地址就能找到位圖中所對應的位了。如果置位了,表明函數地址有效,反之則會觸發異常。

對win CFG的防護思路沒有完全理解透徹,反正原理就是根據跳轉的目的地址去查bitmap表來確定是否合法。針對

3. CCFIR

在CFI被提出后過了很長時間都沒有被廣泛應用到實際生產中去,主要原因還是插樁引起的開銷過大。因此在2013年又提出了CCFIR,在同一年提出的還有binCFI,ModularCFI等等,但CCFIR是非常典型的一個實現。

與上面我們所講的機制將目標集合按照指向邊的源集合是否相同來划分不一樣,CCFIR更加簡潔的將目標集合划分為了三類:

  • 所有的間接call和jmp指令的目標被歸為一類,稱為函數指針;
  • ret指令的目標被歸為兩類,一類是敏感庫函數(比如libc中的額system函數),另一類是普通函數。

下面我們以下圖中的例子來說明CCFIR的工作原理:
在這里插入圖片描述

左邊是原始的控制流,右邊是CCFIR機制下的控制流。CCFIR提出了通過Springboard段(下方灰色部分)存放有效間接轉移目標的地址,在這段控制流中,5和3節點節點分別是call eax指令和ret指令這兩個間接轉移指令的目標地址,因此都會被存在Springboard段中。在程序執行到節點2’時,會檢測接下來的跳轉地址是否在Springboard段中,是則跳轉,否則出錯,從節點6跳轉到3也是一樣。
在這里插入圖片描述

Springboard段的內存布局如上圖所示,通過將某一位設置成0/1來區分普通段和Springboard段。這樣在跳轉檢測時檢查某一個目標是否在Springboard段,只要檢測某一位的值就可以了。

再進一步擴展,由於目標地址主要被分為三類,那么這三類又可以通過幾位的不同來區分,如下圖。第27bit為0則表示是Springboard段,第3位為1則屬於函數指針,為0屬於ret地址,並通過26位區分是敏感函數地址還是普通函數地址。
在這里插入圖片描述

CCFIR的主要貢獻在於它降低了CFI機制的開銷,希望能將CFI投入實際使用中去。

4. VTV

2014年 Google 間接函數調用檢查(第6篇文章)。隨着對堆棧的保護越來越完善,出現了很多基於非堆棧的前向轉移攻擊,尤其是call指令。例如利用UAF漏洞覆蓋vtable指針等等。這篇文章的主要貢獻不是提出了什么新的機制,而是將CFI真正用到了生產編譯器中,僅針對於前向轉移。以下是主要工作:

  • Vtable Verification (VTV), in GCC 4.9,主要是對vtable調用進行檢測,VTV在每個調用點驗證用於虛擬調用的vtable指針的有效性。
  • Indirect Function Call Checker (IFCC), in LLVM。它通過為間接調用目標生成跳轉表並在間接調用點添加代碼來轉換函數指針來保護間接調用,從而確保它們指向跳轉表條目。任何未指向相應表的函數指針都被視為CFI違規。 - - Indirect Function Call Sanitizer (FSan), in LLVM是一個可選的間接調用檢查器。

LLVM Clang Control Flow Integrity Design Documentation一文詳細的描述了Forward-Edge CFI for Virtual Calls的實現原理。

5. Kernel CFI

Linux 內核的代碼量比較少但是內核權限更大,一旦被攻擊會更加致命,所以kernel也需要擁有自己CFI防護方案。

在Andriod上google投入了大量精力來防止代碼重用攻擊(ROP),主要的防護思路是通過基於編譯器的安全緩解措施:

  • 代碼重用攻擊(ROP)利用內核的常用方法是使用錯誤來覆蓋存儲在內存中的函數指針,例如存儲了回調函數的指針,或已被推送到堆棧的返回地址。這允許攻擊者執行任意內核代碼來完成利用,即使他們不能注入自己的可執行代碼。這種獲取代碼執行能力的方法在內核中特別受歡迎,因為它使用了大量的函數指針,以及使代碼注入更具挑戰性的現有內存保護機制。
  • CFI 嘗試通過添加額外的檢查來確認內核控制流停留在預先設計的版圖中,以便緩解這類攻擊。盡管這無法阻止攻擊者利用一個已存在的 bug 獲取寫入權限,從而更改函數指針,但它會嚴格限制可被其有效調用的目標,這使得攻擊者在實踐中利用漏洞的過程變得更加困難。

Google 的 Pixel 3 將是第一款在內核中實施 LLVM 前端控制流完整性(CFI)的設備,已經實現了 Android 內核版本 4.9 和 4.14 中對 CFI 的支持。

Android 內核控制流完整性CFI in Android Kernel Security介紹了Android下實現kernel CFI的大概情況。
Control Flow Integrity (CFI) in the Linux kernelLLVM Clang Control Flow Integrity Design Documentation介紹了Kernel CFI的詳細實現原理。

5.1 forward-edge protection CFI(Control-Flow Integrity)

Kernel CFI 前向邊沿的防護。

  • 1、將前向間接跳轉的目的地址搜集到一起組成一張表,在跳轉前判斷目的地址的合法性。因為合法的目的地址都是實際存在的函數,所以表的大小是有限的。當然也不會把所有的目的函數都集中到一張表里,會根據函數的原型把原型相同的函數搜集到同一張表中。

函數原型一致:

int do_fast_path(unsigned long, struct file *file)
int do_slow_path(unsigned long, struct file *file)

函數原型不一致:

void foo(unsigned long)
int bar(unsigned long)

在這里插入圖片描述

如上圖根據google的研究統計,使用原型法來分類函數。LLVM 的 CFI 將 55% 的間接調用限制為最多 5 個可能的目標,80% 限制為最多 20 個目標。

因為linux kernel有時並未嚴格遵守函數指針和函數原型絕對一致的約定,所以在開啟CFI特性時需要修復這類問題。

  • 2、在鏈接時進行間接調用目的函數表的分類和創建,以及調用前的插樁。這要求連接器具有LTO功能。

llvm的CFI模塊會用LTO來決定所有valid call targets,必須使用llvm的整體的匯編器來進行inline匯編,必須使用LTO-aware的鏈接器,比如說 GNU gold linker或者是llvm的ld。

下圖為LLVM的LTO原理簡介:
在這里插入圖片描述

為了確定每個間接分支的所有有效調用目標,編譯器需要立即查看所有內核代碼。傳統上,編譯器一次處理單個編譯單元(源代文件),並將目標文件合並到鏈接器。LLVM 的 CFI 要求使用 LTO,其編譯器為所有 C 編譯單元生成特定於 LLVM 的 bitcode,並且 LTO 感知鏈接器使用 LLVM 后端來組合 bitcode,並將其編譯為本機代碼。

幾十年來,Linux 一直使用 GNU 工具鏈來匯編,編譯和鏈接內核。雖然我們繼續將 GNU 匯編程序用於獨立的匯編代碼,但 LTO 要求我們切換到 LLVM 的集成匯編程序以進行內聯匯編,並將 GNU gold 或 LLVM 自己的 lld 作為鏈接器。在巨大的軟件項目上切換到未經測試的工具鏈會導致兼容性問題,我們已經在內核版本 4.9 和 4.14 的 arm64 LTO 補丁集中解決了這些問題。

  • 3、具體實例

實例的c語言代碼:(action()間接調用,可以調用do_simple()或者do_fancy())
在這里插入圖片描述

對應的匯編代碼如下:
在這里插入圖片描述

開啟cfi保護以后的匯編代碼:
在這里插入圖片描述

5.2 backward-edge protection SCS(Shadow Call Stack)

Kernel CFI 后向邊沿,使用影子調用堆棧的方式來防護。

  • 方式1:專用寄存器用於單獨的返回堆棧:“影子調用堆棧”

x86已經不使用這種方式,因為它運行緩慢且存在競爭條件。arm64可以為所有影子堆棧操作保留寄存器(x18),陰影堆棧的位置需要保密。
在這里插入圖片描述

結果出現在兩個堆棧寄存器中:sp和未緩存的x18僅將來自影子堆棧(由x18指向)的返回地址(鏈接)寄存器的負載用於返回:
在這里插入圖片描述

  • 方式2:使用專屬硬件完成(x86: CET, arm64:Pointer Authentication)

Intel CET: 基於硬件的只讀影子調用堆棧。在調用和退出指令期間隱式使用否則為只讀的影子堆棧。
ARM v8.3a Pointer Authentication (“signed return address”)。新指令:paciaspautiasp。Clang and gcc: -msign-return-address

以下是使用arm PA實現的硬件影子堆棧:
在這里插入圖片描述

5.3 Shared library support(Cross-DSO)

因為內核是全解析的,所有不會有間接調用外部模塊的情況。在用戶態的共享庫中還有間接調用還是穿越DSO模塊。LLVM Clang Control Flow Integrity Design Documentation一文中詳細描述了這些技術的實現。

6. 利用硬件來提升CFI的效率

我們相信上述設計可以在硬件中有效地實現。添加到ISA的一條新指令將允許以每次檢查更少的字節(更小的代碼大小開銷)執行前向CFI檢查(可能會更有效)。當前的純軟件檢測要求每個檢查至少32字節(在x86_64上)。硬件指令可能小於12個字節。這樣的指令將檢查參數指針是否入站且已正確對齊,並且如果檢查失敗,它將捕獲(在單片方案中)或調用慢路徑函數(跨DSO方案)。對於硬件實現而言,位矢量查找可能太復雜了。

注意,這種硬件擴展將補充被叫方的支票,例如。英特爾ENDBRANCH。而且,CFI將從ENDBRANCH具有兩個好處:a)精度和b)防止多態類型之間無效轉換的能力。

為了能夠在性能和防御方面取得更好的效果,一些研究着手於利用現有的硬件機制,來降低CFI的開銷。

Vasilis Pappas提出利用硬件性能計數器,在運行時觀察執行流的思路,該方法被稱為kBouncer[10]。他們利用LBR(Last Branch Register)來捕獲最近的16次跳轉信息。具體做法是在敏感系統調用處,對捕獲的16次跳轉進行安全性判斷,即return指令需要跳轉到調用點的后繼位置,indirect-call指令的目標是函數入口,其余跳轉指令目標基本塊長度不能全部少於20條指令。為了避免攻擊者利用庫函數調用來完成攻擊,文章在所有的庫函數調用點,來進行上述合法性檢查。為了驗證kBouncer的防御效果,作者對IE瀏覽器、Adobe Flash Player和Adobe Reader進行了實驗(利用已知安全漏洞,組織ROP payload攻擊這三種應用),實驗結果表明該方法能夠有效緩解ROP攻擊。同時,該方法的性能開銷低於~4%。

Yueqiang Chen等人設計了一種與kBouncer類似的方法,稱作ROPecker[11],也是利用LBR捕獲程序控制流的方式進行ROP攻擊監測。但不同之處在於判斷是否遭受ROP攻擊的邏輯和觸發監測的時機。1、判斷邏輯:在運行時檢測過去(利用LBR)和未來的執行流(模擬執行)中是否存在長gadget鏈(5個比較短的gadget),若存在,則認為這是一次ROP攻擊。Gadget信息是通過靜態分析二進制程序和共享庫得到的。2、運行時監測是事件驅動的,具體時機是調用敏感系統調用和執行流跳出滑動窗口觸發異常。ROPecker設計了一個滑動窗口,因為代碼本身具有時間和空間的局部性,但是gadget鏈卻是散列的,利用這一特性,系統保證該窗口內的gadget數目不足以構成一次ROP攻擊,窗口內的代碼設置可執行權限,窗口外的代碼不可執行,當執行流跳出滑動窗口時,便會觸發異常,進行運行時檢測。該方法利用代碼本身具有的時間和空間局部性,針對gadget鏈是散列的前提,提出了滑動窗口機制,使用事件驅動的檢測方法,具有較高的准確性和高效性。為了驗證該方法的安全性,ROPecker選取了有棧溢出的真實世界應用(Linux Hex-editer)進行攻防演練。實驗結果證明,ROPecker能夠有效的阻止ROP攻擊。同時,SPEC CPU2006 benchmark顯示了該方法的開銷非常低(~2%)。

Yubin Xia等人設計的CFIMon[12],也是采用性能計數器來捕獲程序執行流,並進行合法性判斷。但他們采用的是BTB(Branch Trace Buffer),來捕獲受保護程序運行過程中所有跳轉指令的信息。BTB與LBR不同之處在於,BTB可以把程序整個執行過程中所有的跳轉指令的歷史信息都記錄下來,LBR只能記錄16條。但是BTB需要CPU向指定的一個緩沖區內寫入跳轉信息,當緩沖區滿時,CPU會觸發異常交給操作系統處理(將緩沖區內容寫入文件中),LBR是循環的寄存器。使用BTB的程序性能明顯比LBR性能低。CFIMon檢查BTB的時機在兩個階段:一是當緩沖區滿時,操作系統將所有歷史信息寫入另一個進程,由另一個進程進行合法性判斷;二是當受保護進程執行敏感系統調用時,另一個進程也進行歷史信息的合法性判斷。合法性判斷主要檢查間接控制轉移的跳轉目標是合法目標集合內。如果所有間接控制轉移的歷史跳轉目標在合法目標集合中,認為當前受保護進程沒有收到攻擊;如果有至少一個間接控制轉移的歷史跳轉目標在合法目標集合中,那么認為受保護進程受到攻擊。合法目標的集合是在線下通過靜態分析獲得的,並且存儲在檢查進程中。

參考文檔:

1.繞過Windows Control Flow Guard思路分享
2.Control-Flow Integrity(控制流完整性) 的原理
3.控制流完整性的發展歷程
4.Control-Flow-Integrity
5.關於OpenSSL“心臟出血”漏洞的分析
6.DOP(Data Oriented Programming)攻擊
7.Win10安全特性之執行流保護
8.Control Flow Integrity (CFI) in the Linux kernel PPT
9.面向二進制程序的細粒度控制流完整性校驗方法
10.Android 內核控制流完整性
11.CFI in Android Kernel Security
12.Android 控制流完整性
13.ROP原理
14.Clang Control Flow Integrity Design Documentation
15.Control-Flow Integrity Principles, Implementations, and Applications
16.linux漏洞緩解機制介紹
17.手把手教你棧溢出從入門到放棄(上)
18.手把手教你棧溢出從入門到放棄(下)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM