CFG——ControlFlowGuard 控制流保护
CFG防護機制的簡要分析 - 先知社區(qū) (aliyun.com)
Exploring Control Flow Guard in Windows 10 (trendmicro.com)
本文主要來自上面兩篇文章,自己做一個記錄罷了。
CFG 通過在間接跳轉(zhuǎn)(Indirect Call)前插入校驗代碼(比如 call dword ptr ss:[ebp-8] 等等 ),檢查目標地址的有效性,進而可以阻止執(zhí)行流跳轉(zhuǎn)到預期之外的地點, 最終及時并有效的進行異常處理,避免引發(fā)相關的安全問題。
CFG 的實現(xiàn)需要聯(lián)合編譯器、操作系統(tǒng)用戶層庫和內(nèi)核模塊,它是一個匯編層面的保護。
首先,一個正常的函數(shù)地址(x86)的前24位會被取出,作為一個偏移值(OFFSET)。CFGBitMapBase + OFFSET*4 地址中存儲著一個計算值(每4字節(jié)作為) ,_guard_check_icall_fptr 函數(shù)會對傳入的參數(shù)進行計算,然后與存儲著的計算值進行比較:
|
① 最后一字節(jié)的最后4位全為0,則取最后一字節(jié)的前5位作為一個偏移值,若對應的存儲值(從0開始數(shù))的對應位是1,則函數(shù)地址有效。 ② (windows 10 pro 2018年)最后一字節(jié)的最后4位不全為0,則取最后一字節(jié)的前5位作為一個偏移值。若對應的存儲值(從0開始數(shù))的對應位是1,則函數(shù)地址有效并返回; Win 10 1909(OS內(nèi)部版本18363.1198) 中,最后一字節(jié)的最后4位不全為0,則取最后一字節(jié)的前5位并將這5位的最后一位置0,再作為一個偏移值。若對應的存儲值(從0開始數(shù))的對應位是1,則執(zhí)行下一步 ③ 若前兩個都不滿足,或第二個滿足,則將前5位的最后一位置1,作為一個偏移值。若對應的存儲值(從0開始數(shù))的對應的位是1,則函數(shù)地址有效。否則判定無效,并進行異常處理。 |
借助上面提到的文章,自己添加了一些函數(shù)進去,對編譯的程序進行調(diào)試:
因為是用 C++ 寫的,使用的是 __cdecl (C 規(guī)范的) 調(diào)用約定,參數(shù)從右到左入棧,由調(diào)用者負責清除棧。
可以看到 _guard_check_icall_fptr 程序?qū)嶋H調(diào)用了 ValidateUserCallTarget 函數(shù)
系統(tǒng):Windows 10 1909 編譯器:VS 2019 win32 release版
mov edx,dword ptr ds:[77D112F8] // 獲得 CFGBitMapBase 地址 mov eax,ecx // ecx = 要調(diào)用的函數(shù)地址 shr eax,8 // 取函數(shù)地址的前24位作為 OFFSET mov edx,dword ptr ds:[edx+eax*4] // 到 CFGBitMapBase+OFFSET*4 取值 mov eax,ecx shr eax,3 // eax 右移三位 test cl,F // 函數(shù)地址的最后4位是否全為0
jne ntdll.77C79DBE // 不全為0則跳轉(zhuǎn)到后面執(zhí)行 bt edx,eax // Bit Test 指令,第一個操作數(shù)(edx)是寄存器且
// eax > 32時,將會對 eax = eax mod 32 (AX 時模數(shù)是16)
// 然后再取 edx 相對應的位的值放入 CF 中
// 因為右移了3位,最后一字節(jié)前5位能表示的最大值是31,
// 因此其實取余后的值也還是由最后一字節(jié)的前5位表示
jae ntdll.77C79DC7 // 若edx對應的位是0,則跳轉(zhuǎn),否則正常返回 ret
77C79DBE:
btr eax,0 // 取 eax 最后一位,放入 CF 并將 eax 最后一位置 0 bt edx,eax // 取 edx 對應的位放入 CF,與前面原理相同 jae ntdll.77C79DD0 // 若 CF == 0,則進入異常處理
77C79DC7:
or eax,1 // 將 eax 最后一位置 1 bt edx,eax // 取對應位 jae ntdll.77C79DD0 ret
77C79DD0: // 異常處理 push ecx lea esp,dword ptr ss:[esp-80] movups xmmword ptr ss:[esp],xmm0 movups xmmword ptr ss:[esp+10],xmm1 movups xmmword ptr ss:[esp+20],xmm2 movups xmmword ptr ss:[esp+30],xmm3 movups xmmword ptr ss:[esp+40],xmm4 movups xmmword ptr ss:[esp+50],xmm5 movups xmmword ptr ss:[esp+60],xmm6 movups xmmword ptr ss:[esp+70],xmm7 call <ntdll.@RtlpHandleInvalidUserCallTarget@4> movups xmm0,xmmword ptr ss:[esp] movups xmm1,xmmword ptr ss:[esp+10] movups xmm2,xmmword ptr ss:[esp+20] movups xmm3,xmmword ptr ss:[esp+30] movups xmm4,xmmword ptr ss:[esp+40] movups xmm5,xmmword ptr ss:[esp+50] movups xmm6,xmmword ptr ss:[esp+60] movups xmm7,xmmword ptr ss:[esp+70] lea esp,dword ptr ss:[esp+80] pop ecx ret
同時,若在編譯過程中不開啟 ASLR ,則自己編寫的函數(shù)的 CFGBitMap 值會是 FFFFFFFF,而系統(tǒng)函數(shù)的值則不會是
使用不同的運行庫也會導致某些函數(shù)的 CFGBitMap 值為 FFFFFFFF,猜測如下:如果代碼寫死在程序中,不需要調(diào)用外部 DLL 庫中的文件,則值為 FFFFFFFF,如果需要調(diào)用外部 DLL 庫中的文件,則值不為全 F。
總結(jié)
以上是生活随笔為你收集整理的CFG——ControlFlowGuard 控制流保护的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 12.基于canel的网络策略
- 下一篇: 美团实习生电面之谈(成功拿到offer)