探索 Windows 10的執行流保護

作業系統廠商都會持續地加強漏洞防護技術,就像微軟在Windows 10和Windows 8.1 Update 3(於去年11月發布)引入的新技術。這項技術被稱為執行流保護(CFG)。

之前的防護技術像「位址空間隨機載入(ASLR)」和「資料執行防止(DEP)」都成功地讓漏洞攻擊變得更加困難,雖然這些技術還不完美。ASLR讓駭客開發了Head-Spray攻擊方式,DEP讓「返回指標程式設計(ROP)」技術出現在漏洞攻擊碼裡。

為了探索此一新技術,我使用Windows 10技術預覽版(build 6.4.9841)測試,用Visual Studio 2015預覽版來製作測試用程式。因為最新的Windows 10技術預覽版(10.0.9926)內的CFG實作方式有些許變化,我會指出其不同之處。

若要完全實現CFG,編譯程式和作業系統都必須正確地加以支援。因為是系統層級的漏洞防護措施,要實現CFG必須靠編譯程式、作業系統使用者模式程式庫和核心模式組件間共同合作。MSDN上的一篇部落格文章概述了開發人員支援CFG所需要的步驟。

微軟實作CFG的重點在於間接呼叫保護。看看我所建立測試用程式的程式碼:

圖1、測試用程式的程式碼

 

讓我們看看如果不啟用CFG,紅圈內的程式碼會編譯成什麼樣子。

 

圖2、測試用程式的組合語言程式碼

在上圖中,有一種類型的間接呼叫其目標位址不是在編譯時決定,而在執行時決定。讓漏洞攻擊可以加以利用,如下圖所示:

 

圖3、如何利用間接呼叫

 

微軟實作CFG著重在解決利用間接呼叫及呼叫無效三個位元組是﷽﷽﷽﷽﷽﷽﷽﷽﷽﷽目標所帶來的問題(在漏洞攻擊中,這會是第一步)。

 

無效目標有個顯著的特徵:在大多數情況下,它不是個有效的函數起始位址。微軟實作CFG的想法是基於間接呼叫目標必須是個有效的函數起始位址。啟用 CFG後,組合語言程式碼會變成如何?

 

圖4、啟用CFG的組合語言程式碼

 

在進行間接呼叫前,目標位址會被傳遞給_guard_check_icall函數,這是實作CFG的地方。在沒有支援CFG的Windows版本上,這函數不會做任何事情。在支援CFG的Windows 10上,它會指向ntdll!LdrpValidateUserCallTarget。此函數接收目標位址作為參數並執行以下動作:

 

  1. 存取一個點陣圖(稱為CFGBitmap),它代表程序空間內所有函數的起始位置。程序空間中每8個位元組的狀態對應CFGBitmap的一個位元。如果在8個位元組內有個函數起始位址,則CFGBitmap中的對應位元會被設為1;否則設為0。下圖範例展示CFGBitmap的一部分:

 

圖5、CFGBitmap表現方式

 

  1. 將目標位址轉換為CFGBitmap中的一個位元。讓我們以00b01030為例:

 

圖6、目標位置

 

最高位的3個位元組(藍圈內的24個位元)的目標位址代表CFGBitmap的偏移量(單位為4個位元組/32個位元)。在此範例中,最高位的3個位元組相當於0xb010。因此,指向CFGBitmap內一個4位元組單位的指標就是CFGBitmap的基礎位址加上0xb010。

同時,第4位元到第8位元(紅圈內的5個位元)代表值X。如果目標位址與0x10對齊(目標位址 & 0xf == 0),則X是單位內的位元偏移量。如果目標位址沒有與0x10對齊(目標位址 & 0xf != 0),則X | 0x1是位元偏移量。

在此範例中,目標位址是0x00b01030。X的值為6。公式0x00b01030 & 0xf 結果為零;這代表位元偏移量也是6。

 

  1. 讓我們看看在第2步中所確認的位元。如果該位元等於1,意味著間接呼叫目標有效,因為它是一個函數起始位址。如果該位元為0,這代表間接呼叫目標無效,因為它不是一個函數起始位址。如果間接呼叫目標有效,則函數什麼也不做。如果間接呼叫目標無效,則會觸發異常以防止漏洞攻擊碼被進一步執行。

 

圖7、CFGBitmap內的值

 

實作CFG應該有助於防止某些類型的漏洞。長遠來看將有助於減少軟體漏洞所帶來的威脅。

 

@原文出處:Exploring Control Flow Guard in Windows 10作者:Jack Tang(威脅分析師)