以win10 preview 9926中IE11的Spartan html解析模塊為例,看一下CFG的具體情況:
這里就是被編譯器插入的CFG校驗函數
但是靜態(tài)情況下默認的檢測函數是一個直接return的空函數,是微軟在和我們開玩笑嗎?
通過動態(tài)調試看一下
從上圖我們可以看出,實際運行時的地址和我們通過IDA靜態(tài)看到的地址是不一樣的,這里就涉及到CFG和操作系統相關的那部分。支持CFG版本的操作系統加載器在加載支持CFG的模塊時,會把這個地址替換成ntdll中的一個函數地址。不支持CFG版本的操作系統不用理會這個檢測,程序執(zhí)行時直接retn。
這是ntdll中的檢測函數
原理是在進入檢測函數之前,把即將call的寄存器值(或者是帶偏移的寄存器間接尋址)賦值給ecx,在檢測函數中通過編譯期間記錄的數據,來校驗這個值是否有效。
檢測過程如下:
首先從LdrSystemDllInitBlock+0x60處讀取一個位圖(bitmap),這個位圖表明了哪些函數地址是有效的,通過間接調用的函數地址的高3個字節(jié)作為一個索引,獲取該函數地址所在的位圖的一個DWORD值,一共32位,證明1位代表了8個字節(jié),但一般來說間接調用的函數地址都是0x10對齊的,因此一般奇數位是不使用的。
通過函數地址的高3個字節(jié)作為索引拿到了一個所在的位圖的DWORD值,然后檢查低1字節(jié)的0-3位是否為0,如果為0,證明函數是0x10對齊的,則用3-7bit共5個bit就作為這個DWORD值的索引,這樣通過一個函數地址就能找到位圖中所對應的位了。如果置位了,表明函數地址有效,反之則會觸發(fā)異常。
這里有個有趣的東西,雖然使用test cl,0Fh檢測是否0x10對齊,如果對齊的話實際上用3-7位作為索引,也就是說第3位一定是0。但如果函數地址不是0x10對齊的話,則會對3-7位 or 1,然后再作為索引。這樣就有一個弊端,如果一個有效的間接調用的函數地址是8字節(jié)對齊的,那么其實是允許一個8字節(jié)的一個錯位調用的,這樣可能導致的結果就是可能造成雖然通過了校驗,但是實際調用的地址并不是原始記錄的函數地址。