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