基本信息
早先的抑制技術如地址空間布局隨機化(ASLR)和數據執行保護(DEP),雖然並不完美,但已經成功地使得漏洞利用變得更加困難。ASLR導致了堆噴射的發展,而DEP導致了漏洞利用代碼中返回導向編程(ROP)技術的發展
為了探索CFG這種特殊的技術,我使用Windows 10技術預覽版(build6.4.9841)系統,並使用VS2015預覽版來創建測試程式。最新的Windows 10技術預覽版(build10.0.9926)系統的CFG實現有一個細微的變化。
為了完全實現CFG機制,編譯器和作業系統都必須恰當地支持它。作為一種系統級的漏洞利用抑制機制,CFG的實現需要編譯器、作業系統用戶模式庫和核心模組的配合。MSDN上的一篇博文介紹了開發者需要怎樣做才能支持CFG機制。
機制分析
微軟的CFG是專注於間接調用的保護機制,以下面我所創建的測試程式為例,來講解該機制的大致原理。
首先,我們看一下在CFG機制未啟用時,上圖紅線所圈部分的代碼編譯之後的結果:
在上圖中顯示了一種間接調用類型,在編譯時期它的目標地址並未確定,而是在運行時才確定該地址。這樣,我們就能通過以下方式來進行漏洞利用代碼的調用:
微軟的CFG實現主要集中於抑制問題,即如果間接調用被利用來調用一個無效的目標(在exploit中,這可能是shellcode的第一步),CFG則可以有效抑制。
這個無效目標有一個與眾不同的特徵:在大多數情況下,它不是一個有效函式的起始地址。微軟CFG的實現基於這樣一個理念:間接調用的目標必須是一個有效函式的起始地址。那么,啟用了CFG之後生成的彙編代碼如何呢?
在間接調用之前,目標地址會傳遞給函式_guard_check_icall,而CFG機制正是在該函式中實現。在之前不支持CFG機制的Windows系統版本中,該函式未做任何事。在支持CFG的Windows 10中,_guard_check_icall調用了_guard_check_icall函式,該函式以目標地址作為參數,並按以下流程執行:
1、訪問一個點陣圖(調用CFGBitmap),該點陣圖代表進程空間所有函式的開始位置,進程空間中每8個位元組的狀態對應CFGBitmap中的一個位。如果在每8個位元組組中有一個函式起始地址,則CFGBitmap中對應的位會被設定為1,否則會被設定為0。下圖是一個例子用來展示CFGBitmap的一部分。
2、將目標地址轉換為CFGBitmap中的一個位,以00b01030為例:
最高位的3個位元組(上圖中藍線圈中的24位)代表CFGBitmap的偏移量(以4位元組為單元)。在該例子中,最高位的3個位元組值等於0xb010。因此,指向CFGBitmap中一個4位元組單元的指針就是CFGBitmap的基地址加上0xb010。
與此同時,第4到第8位(上圖紅線圈中的5位)代表值X。如果目標地址與0×10對齊(目標地址&0xf==0),則X就是單元內的位偏移值。如果目標地址沒與0×10對齊(目標地址&0xf!=0),則X|0×1就是位偏移值。
在這個例子中,目標地址是0x00b01030,X的值為6。表達式0x00b01030&0xf的值等於0,這表明位偏移也是6。
3、接下來我們看下第2步中所提到的位。如果這個位等於1,這就表明該間接調用目標有效,因為它是一個函式的起始地址;如果該位等於0,表明該間接調用目標無效,因為它不是一個函式的起始地址。如果間接調用目標有效,則函式將什麼也不做。如果目標無效,將會拋出一個異常,以此阻止利用代碼的執行。