2015-03-23 08:11 Pchome電腦之家 顯示圖片
比預(yù)計時間晚了大約一個月后,微軟終于拿出了今年第二個Windows 10公開預(yù)覽版 build 10041,并通過Windows Update向公眾推送(ISO鏡像稍后)。新版改進(jìn)主要有更好的開始菜單用戶體驗、虛擬桌面加強、更多地區(qū)可以使用Cortana語音助手等。只不過,隨同新版一起到來的還有不少問題和bug,倒是真正體現(xiàn)出預(yù)覽版“應(yīng)有”的面貌來。
Windows 10 b10041預(yù)覽版
和上一版9926相比,Win10 Build 10041有了不少變化,其中一些在稍早前泄露的10036版中已有體現(xiàn)。作為第二個公開發(fā)布的Windows 10技術(shù)預(yù)覽版,10041比較重要的更新有:改進(jìn)的開始菜單體驗、虛擬桌面即任務(wù)視圖(Task View)增強、Cortana在更多地區(qū)啟用、部分內(nèi)置應(yīng)用和系統(tǒng)功能強化、鎖屏界面新嘗試(首先在美國和法國測試,其他地方稍后)。
開始菜單新體驗
在9926中,開始菜單采用了新的XAML語言來重新設(shè)計,在后續(xù)版本中微軟又持續(xù)對其進(jìn)行改進(jìn)和優(yōu)化。
開始菜單透明效果
現(xiàn)在build 10041(以及稍早的幾個泄漏版)開始菜單有了透明效果。左側(cè)所有應(yīng)用列表也得到改進(jìn),按鈕更易于觸控操作。另外一個改進(jìn)是,用戶可以從左側(cè)常用應(yīng)用列表以及所有應(yīng)用列表中直接拖拽某個應(yīng)用到右側(cè)并將其固定到開始菜單。
任務(wù)視圖(虛擬桌面)增強
經(jīng)過幾個版本改進(jìn)后,Build 10041里的任務(wù)視圖有了很大增強。首先,用戶可以直接將某個桌面已打開的應(yīng)用程序或窗口拖拽到其他桌面里,而在之前版本,你需要通過右鍵關(guān)聯(lián)菜單來執(zhí)行。更方便的是,你可以直接將一個窗口拖拽到任務(wù)視圖右下角的“加號”上,此時系統(tǒng)不僅會創(chuàng)建一個新桌面,還會同時將你剛才拖拽的窗口移動到新桌面上。
直接拖拽窗口到其他桌面
對于經(jīng)常需要打開很多窗口的用戶來說,10041版增加了任務(wù)欄篩選功能,只有指定桌面運行的窗口才會顯示在任務(wù)欄上,便于用戶管理。再有就是增加了Alt + Tab切換窗口時的篩選:開啟后只會在特定桌面的窗口里循環(huán)。這兩項設(shè)置可以在電腦設(shè)置--系統(tǒng)--多任務(wù)里更改。
語音助手可用了
Cortana現(xiàn)可以在美國以外的地區(qū)使用了,包括:中國、英國、法國、意大利、德國以及西班牙。若更新后Cortana未激活,請點擊搜索框并展開左上角的三橫線進(jìn)入設(shè)置,然后開啟Cortana即可。目前Cortana可以幫你搜索應(yīng)用、功能設(shè)置、文件以及從網(wǎng)絡(luò)上搜索。
Cortana語音助手可以用了
其他功能和應(yīng)用改進(jìn)
任務(wù)欄網(wǎng)絡(luò)圖標(biāo)彈出窗改進(jìn),增加了快速訪問面板(形式有點像通知中心),包含數(shù)個常用網(wǎng)絡(luò)設(shè)置選項,用戶可以借此快速加入無線網(wǎng)絡(luò)或是其他操作。
網(wǎng)絡(luò)連接快捷面板
Photos應(yīng)用的動態(tài)磁貼增加了對OneDrive圖片支持,并支持RAW格式文件瀏覽。此外Photos應(yīng)用也開始支持鍵盤快捷鍵操作了,目前有Tab、方向鍵、上頁/下頁鍵可用,后續(xù)還會加入更多快捷鍵。
屏幕手寫版識別改進(jìn),優(yōu)化了短句識別,在輸入時提供候選詞預(yù)測和多組相近短語選擇。
鎖屏界面實驗功能
為了讓更多用戶更好更快地融入新系統(tǒng),微軟準(zhǔn)備在鎖屏界面上添加有關(guān)Win10的幫助和提示,他們首先為美國和法國的Win10用戶加入了這個新元素,隨后再慢慢推廣到其他地區(qū)。
有喜也有憂,除了新功能變化,Windows 10 Build 10041也存在不少問題。
官方公布的已知問題包括:
·部分用戶在登錄時可能會遇到用戶名及密碼輸入框不顯示或無法輸入,從而導(dǎo)致無法登陸系統(tǒng)。若遭遇此問題,可以嘗試在登錄界面點擊“切換用戶”,按下Ctrl+Alt+Del,或是按電源按鈕讓系統(tǒng)睡眠再喚醒的方式重試。
·在剛剛安裝完系統(tǒng),還處在OOBE階段(對系統(tǒng)進(jìn)行初始化設(shè)置階段)時如果手動鎖定計算機會導(dǎo)致異常無法繼續(xù),需要硬重啟計算機,然后再重新開始OOBE階段。所以這個時段不要鎖定你的計算機。
·輕松使用存在一些問題,會導(dǎo)致講述人或第三方屏幕朗讀工具無法使用,另外放大鏡功能也存在問題有可能導(dǎo)致屏幕不可用。
·由于許可證問題會導(dǎo)致一些測試版商店中的應(yīng)用無法安裝或更新。
·在這個版本中,郵件,日歷和人脈應(yīng)用可能因為測試版商店的許可證問題而無法使用,若已遭遇這樣的問題,可嘗試以下方法修復(fù):以管理員身份啟動“PowerShell”,然后輸入【Get-appxprovisionedpackage –online | where-object {$_.packagename –like “*windowscommunicationSAPps*”} | remove-appxprovisionedpackage –online】并運行,完成后進(jìn)入正式版商店(綠色磁貼)重裝以上應(yīng)用。
·當(dāng)你使用開始菜單、任務(wù)視圖、Snap Assist或是在平板模式下重排列窗口時,可能會遇到某個窗口搶鏡頭的情況發(fā)生,干擾你正在執(zhí)行的操作。
·創(chuàng)建虛擬桌面可能會遇到新桌面丟失或是在任務(wù)視圖中呈現(xiàn)純黑色問題。
·在鎖屏界面下,你可能會屏幕右側(cè)看到一個Chess Knight(象棋騎士)圖標(biāo),這是微軟內(nèi)部團(tuán)隊所添加的一個區(qū)分性標(biāo)識,用于在截圖上區(qū)分新舊鎖屏畫面,在后續(xù)版本中會去除它。
·在高DPI設(shè)備上,鎖屏界面文字可能會很大。
·處于解決問題需要,10041版的平板模式切換通知默認(rèn)暫時關(guān)閉,若需要可以在電腦設(shè)置里重新開啟。
·當(dāng)“講述人”開啟時,登錄界面將無法呼出屏幕觸摸鍵盤,導(dǎo)致無法登陸。
·有些用戶可能會頻繁收到重啟以安裝更新的提示,即使沒有更新需要重新啟動也會收到,此提示可以忽略之。
·當(dāng)你調(diào)用相機應(yīng)用拍攝照片的時候,若點擊左上放按鈕查看剛剛拍攝的照片,會讓Photos應(yīng)用啟動,但是它會直接崩潰。
除了上述官方確認(rèn)的問題,你還有可能遇到這樣幾個bug:
·若你使用“中等”或“較大”比例的所有項目,可能會導(dǎo)致開始菜單無法呼出和使用,此時你需要進(jìn)入傳統(tǒng)控制面板--外觀和個性化--限制,將“更改所有項目的大小”更改為【較小(100%)】,然后重啟系統(tǒng)。
·同樣是傳統(tǒng)控制面板選項,網(wǎng)絡(luò)和Internet--網(wǎng)絡(luò)和共享中心下,點擊“以太網(wǎng)”無法彈出相應(yīng)窗口,進(jìn)入左側(cè)“更改適配器設(shè)置”,右鍵查看屬性會彈出“發(fā)生意外錯誤”的彈窗,其他諸如禁用、診斷、重命名等功能也都無響應(yīng)。
綜上,雖然Windows 10 Build 10041雖然帶來了一系列新功能和改進(jìn),但同樣也送上了不少問題,或許這才是預(yù)覽版的真是表現(xiàn)。因此在這里提醒各位,如果要嘗鮮Windows 10預(yù)覽版,最好不要在工作設(shè)備上安裝(至少也要在新磁盤分區(qū)安裝)并且不要用于日常工作或?qū)W習(xí)(測試除外)。
翻譯:myswsun
預(yù)估稿費:260RMB
投稿方式:發(fā)送郵件至linwei#360.cn,或登陸網(wǎng)頁版在線投稿
0x00 前言
隨著操作系統(tǒng)開發(fā)人員一直在增強漏洞利用的緩解措施,微軟在Windows 10和Windows 8.1 Update 3中默認(rèn)啟用了一個新的機制。這個技術(shù)稱作控制流保護(hù)(CFG)。
和其他利用緩解措施機制一樣,例如地址空間布局隨機化(ASLR),和數(shù)據(jù)執(zhí)行保護(hù)(DEP),它使得漏洞利用更加困難。毫無疑問,它將大大改變攻擊者的利用技術(shù)。就像ALSR導(dǎo)致了堆噴射技術(shù)的出現(xiàn),和DEP導(dǎo)致了ROP技術(shù)的出現(xiàn)。
為了研究這個特別的技術(shù),我使用了Windows 10 技術(shù)預(yù)覽版(build 6.4.9841)和使用Visual Studio 2015 預(yù)覽版編譯的測試程序。因為目前最新版的Windows 10 技術(shù)預(yù)覽版(build 10.0.9926)有了一點改變,我將指出不同之處。
為了完全實現(xiàn)CFG,編譯器和操作系統(tǒng)都必須支持它。作為系統(tǒng)層面的利用緩解措施,CFG的實現(xiàn)需要聯(lián)合編譯器、操作系統(tǒng)用戶層庫和內(nèi)核模塊。MSDN上面的一篇文章描述了支持CFG開發(fā)者需要做的步驟。
微軟的CFG實現(xiàn)主要集中在間接調(diào)用保護(hù)上。考慮下面測試程序中的代碼:
圖1 - 測試程序的代碼
讓我們看下CFG沒有啟用時的代碼情況。
圖2 - 測試程序的匯編代碼
在上圖中,有一個間接調(diào)用。它的目標(biāo)地址不在編譯時決定,而是在運行時決定。一個利用如下:
圖3 – 怎么濫用間接調(diào)用
微軟實現(xiàn)的CFG主要關(guān)注緩解間接調(diào)用和調(diào)用不可靠目標(biāo)的問題(在利用中,這是shellcode的第一步)。
不可靠的目標(biāo)有明顯特征:在大部分情況下,它不是一個有效的函數(shù)起始地址。微軟的CFG實現(xiàn)是基于間接調(diào)用的目標(biāo)必須是一個可靠的函數(shù)的起始位置。啟用CFG后的匯編代碼是怎樣的?
圖4 – 啟用CFG后的匯編代碼
在間接調(diào)用之前,目標(biāo)地址傳給_guard_check_icall函數(shù),在其中實現(xiàn)CFG。在沒有CFG支持的Windows中,這個函數(shù)不做任何事。在Windows 10中,有了CFG的支持,它指向ntdll!LdrpValidateUserCallTarget函數(shù)。這個函數(shù)使用目標(biāo)地址作為參數(shù),并且做了以下事情:
1.訪問一個bitmap(稱為CFGBitmap),其表示在進(jìn)程空間內(nèi)所有函數(shù)的起始位置。在進(jìn)程空間內(nèi)每8個字節(jié)的狀態(tài)對應(yīng)CFGBitmap中的一位。如果在每組8字節(jié)中有函數(shù)的起始地址,則在CFGBitmap中對應(yīng)的位設(shè)置為1;否則設(shè)置為0。下圖是CFGBitmap的一部分示例:
圖5 – CFGBitmap
2.將目標(biāo)地址轉(zhuǎn)化為CFGBitmap中的一個位。讓我們以00b01030為例:
圖6 – 目標(biāo)地址
高位的3個字節(jié)(藍(lán)色圈中的24位)是CFGBitmap(單位是4字節(jié)/32位)的偏移。在這個例子中,高位的3個字節(jié)相當(dāng)于0xb010。因此,CFGBitmap中指向字節(jié)單元的指針是CFGBitmap的基址加上0xb010。
同時,第四位到第八位(紅色圈中的)有值X。如果目標(biāo)地址以0x10對齊(目標(biāo)地址&0xf==0),則X為單位內(nèi)的位偏移值。如果目標(biāo)地址不以0x10對齊(目標(biāo)地址&0xf!=0),則X|0x1是位偏移值。
在這個例子中,目標(biāo)地址是0x00b01030。X的值為6。表達(dá)式0x00b01030&0xf值為0;這意味著位偏移也是6。
3.我們看到第二步定義的位。如果位等于1,意味著間接調(diào)用的目標(biāo)是可靠的,因為它是一個函數(shù)的起始地址。如果這個位為0,意味著間接調(diào)用的目標(biāo)是不可靠的,因為它不是一個函數(shù)的起始地址。如果間接調(diào)用目標(biāo)是可靠的,函數(shù)將不做任何事并且直接執(zhí)行。如果間接調(diào)用是不可靠的,將觸發(fā)異常阻止利用代碼運行。
圖7 – CFGBitmap中的值
值X取自第4位到第8位(上面紅圈中5位)。如果目標(biāo)地址以0x10對齊(目標(biāo)地址&0xf==0),X是單元中的位偏移值。如果目標(biāo)地址不以0x10對齊(目標(biāo)地址&0xf!=0),X|0x1是位偏移值。在這個例子中,目標(biāo)地址是0x00b01030,X是6(圖6中紅色圈)。0x00b01030&0xf==0,因此位偏移是6。
在第二步中,位偏移是6。以圖7為例,第6位(紅圈)為1。意味著間接調(diào)用的目標(biāo)是一個可靠的函數(shù)地址。
現(xiàn)在,我們已經(jīng)有了CFG工作機制的基本認(rèn)識。但是這個技巧帶來了下面的問題:
1.CFGBitmap的位信息來自哪里?
2.何時且怎么生成CFGBitmap?
3.系統(tǒng)怎么處理不可靠的間接調(diào)用觸發(fā)的異常?
0x01 深入CFG實現(xiàn)
我們能在PE文件(啟用CFG的VS2015編譯的)中發(fā)現(xiàn)另外的CFG信息。讓我們看下PE文件中的圖1的代碼。這個信息能用VS2015的dumpbin.exe轉(zhuǎn)儲出來。在PE文件的Load Config Table部分,我們能找到下面的內(nèi)容:
圖8 – PE信息
Guard CF address of check-function pointer:_guard_check_icall的地址(見圖4)。在Windows 10預(yù)覽版中,當(dāng)PE文件加載時,_guard_check_icall將被修改并指向nt!LdrpValidateUserCallTarget。
Guard CF function table:函數(shù)的相對虛擬地址(RVA)列表的指針,其包含了程序的代碼。每個函數(shù)的RVA將轉(zhuǎn)化為CFGBitmap中的“1”位。換句話說,CFGBitmap的位信息來自Guard CF function table。
Guard CF function count:函數(shù)RVA的個數(shù)。
CF Instrumented:表明程序中啟用了CFG。
在這里,編譯器完成了CFG的整個工作。剩下的是OS的支持使CFG機制起作用。
1.在OS引導(dǎo)階段,第一個CFG相關(guān)的函數(shù)是MiInitializeCfg。這個進(jìn)程是system。調(diào)用堆棧如下:
圖9 – 調(diào)用堆棧
MiInitializeCfg函數(shù)的前置工作是創(chuàng)建包含CFGBitmap的共享內(nèi)存。調(diào)用時間可以在NT內(nèi)核階段1內(nèi)存管理器初始化時找到(MmInitSystem)。如你所知,在NT內(nèi)核階段1的初始化期間,它調(diào)用MmInitSystem兩次。第一個MmInitSystem將進(jìn)入MiInitializeCfg。那么MiInitializeCfg做了什么?
圖10 – 函數(shù)的主要邏輯
步驟A:注冊表值來自HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\kernel: MitigationOptions
步驟B:MmEnableCfg是一個全局變量,它被用來表示系統(tǒng)是否啟用CFG功能
步驟C:MiCfgBitMapSection的DesiredAccess允許所有的權(quán)限;它的分配類型是“reserve”。在build 10.0.9926和build 6.4.9841中共享內(nèi)存的大小是不同的。對于build 6.4.9841,它按用戶模式空間大小計算。表達(dá)式是size=User Mode Space Size>>6。(>>X:右移X位)。對于build 10.0.9926,這個大小是0x3000000。CFG bitmap能表示整個用戶模式空間。MiCfgBitMapSection是CFG實現(xiàn)的核心組件,因為它被用來包含CFGBitmap。
2.獲得壓縮RVA列表信息的函數(shù)且保存到映像的Control_Area結(jié)構(gòu)。
PE映像第一次加載到系統(tǒng)。NT內(nèi)核將調(diào)用MiRelocateImage來重定位。MiRelocateImage將調(diào)用MiParseImageCfgBits。在函數(shù)MiParseImageCfgBits中,PE映像的壓縮的RVA列表被計算且存儲在PE映像節(jié)中的Control_Area數(shù)據(jù)結(jié)構(gòu)。在系統(tǒng)引導(dǎo)期間一個PE映像只發(fā)生一次。
當(dāng)PE再次加載進(jìn)進(jìn)程,NT內(nèi)核將調(diào)用MiRelocateImageAgain。因為它的壓縮的RVA列表已經(jīng)保存了(且不需要再次計算),MiRelocateImageAgain不需要調(diào)用MiParseImageCfgBits保存一些進(jìn)程的時間。MiParseImageCfgBits被用來計算壓縮的RVA列表以便在小的空間中保存RVA列表。微軟實現(xiàn)CFG有時間和空間的消耗。在MiRelocateImage中,它的CFG相關(guān)的部分被如下描述:
MiParseImageCfgBits被用來計算啟用CFG編譯的模塊的壓縮的RVA列表。在深入這個函數(shù)之前,我們將看一下這個函數(shù)調(diào)用的上下文。函數(shù)MiParseImageCfgBits將在MiRelocateImage函數(shù)中調(diào)用。
函數(shù)MiParseImageCfgBits有5個參數(shù):
a)映像節(jié)的Control_Area結(jié)構(gòu)的指針
b)映像文件內(nèi)容的指針
c)映像大小
d)包含PE可選頭結(jié)構(gòu)的指針
e)輸出壓縮的CFG函數(shù)RVA列表的指針
MiParseImageCfgBits的主要工作如下:
a)從映像的Load Config Table獲得函數(shù)RVA列表
b)使用壓縮算法壓縮列表,以便在小空間保存列表
c)創(chuàng)建壓縮的RVA列表作為輸出
3.在CFGBitmap共享內(nèi)存對象被創(chuàng)建后,CFGBimap共享內(nèi)存對象被映射來作為兩種用途:
a)用來寫共享模塊(.DLL文件等)的bits。這個映射是臨時的;在bits寫入完成后,映射將釋放。通過這個映射寫入的bits信息是共享的,意味著它能被操作系統(tǒng)內(nèi)所有的進(jìn)程讀取。這個映射發(fā)生在MiUpdateCfgSystemWideBitmap函數(shù)中。調(diào)用堆棧如下:
圖11 – 調(diào)用堆棧
b)用來寫私有的bits和讀取校驗間接調(diào)用的bits。通過這個映射寫入的bits是私有的,意味著它只能被當(dāng)前進(jìn)程讀取。這個映射的生存周期與進(jìn)程的生命周期相同。這個映射發(fā)生子MiCfgInitializeProcess中,調(diào)用堆棧如下:
圖12 – 調(diào)用堆棧
基于調(diào)用堆棧,我們知道它在一個正在初始化的進(jìn)程中被映射。Build 10.0.9926和6.4.9841的映射大小是不一樣的。對于6.4.9841,大小是基于用戶模式空間大小計算的。表達(dá)式為size=User Mode Sapce Size>>6(>>X:右移X位)。對于10.0.9926,這個大小是0x3000000。映射的空間在進(jìn)程生命周期內(nèi)總是存在的。映射的基址和長度將被保存在類型為MI_CFG_BITMAP_INFO的結(jié)構(gòu)體中,且地址被修改了(在6.4.9841中,基址是0xC0802144。在10.0.9926中,是0xC080214C)。我稍后將討論怎么將私有的bits寫入映射空間中。MI_CFG_BITMAP_INFO的結(jié)構(gòu)如下:
4.一旦PE映像的RVA列表準(zhǔn)備好了且CFGBitmap節(jié)也映射了,就可以將RVA列表翻譯為CFGBitmap中的bits。
圖13 – 更新CFGBitmap的bits
在幾種不同的場景下這個過程不太一樣:
在ReloadImage/ReloadImageAgain,通過 MiUpdateCfgSystemWideBitmap寫入共享模塊(如dll)的bits
在進(jìn)程初始化階段寫入私有模塊(如exe)的bits
寫入VM(虛擬內(nèi)存)操作的bits
寫入映像和數(shù)據(jù)段的映射的bits
在深入每個場景之前,我們需要搞清楚一些背景信息。在每個進(jìn)程中,包含CFGBitmap的空間被分為兩部分:共享和私有。
MiCfgBitMapSection是一個共享內(nèi)存對象,包含了CFGBitmap的共享的bitmap的內(nèi)容。它與每個進(jìn)程共享。當(dāng)它在自己的虛擬內(nèi)存空間中映射MiCfgBitMapSection時,每個進(jìn)程看見的內(nèi)容都相同。共享模塊(dll等)的bitmap信息將通過3.a節(jié)描述的映射方法寫入。
然而每個進(jìn)程需要CFGBitmap的一部分不是被所有進(jìn)程共享的。它需要私有寫入一些模塊的bitmap信息到CFGBitmap中。這個私有的部分將不和所有的進(jìn)程共享。EXE模塊的bitmap信息使用3.b節(jié)描述的方法寫入。下圖描述了一個通用的場景。
圖14 – 在MiCfgBitMapSection中的共享部分的bitmap內(nèi)容的3中過程和他們的私有節(jié)
a)在ReloadImage/ReloadImageAgain中,通過MiUpdateCfgSystemWideBitmap寫入共享模塊(dll等)的bits。
如第2節(jié)所見,在得到壓縮的函數(shù)的RVA列表并將它保存到Control_Area結(jié)構(gòu)后(在build6.4.9841中: _Control_Area ->SeImageStub->[+4]->[+24h];在build10.0.9926中: _Control_Area ->SeImageStub->[+0]->[+24h]),它將調(diào)用MiSelectImageBase。這個函數(shù)是ASLR實現(xiàn)的核心。它返回最終選擇的基址。選擇的基地址對于寫bit信息到CFGBitmap中非常重要。在得到基地址后,它將調(diào)用MiUpdateCfgSystemWideBitmap。
MiUpdateCfgSystemWideBitmap的主要任務(wù)是將壓縮的RVA列表翻譯為CFGBitmap中的“1”bit。通過這個函數(shù)寫入的bitmap信息是共享的,且被操作系統(tǒng)所有的進(jìn)程共享。這個函數(shù)只針對共享模塊有效(dll文件等)。
MiUpdateCfgSystemWideBitmap有3個參數(shù):
指向Control_Area結(jié)構(gòu)的指針
映像的基址
指向壓縮的RVA列表的指針
MiUpdateCfgSystemWideBitmap的主要邏輯如下:
圖15 – MiUpdateCfgSystemWideBitmap的主要邏輯
在步驟B中,它映射CFGBitmap共享內(nèi)存到系統(tǒng)進(jìn)程空間中。它不映射所有共享內(nèi)存的全部大小。它轉(zhuǎn)化映像的基址為CFGBitmap的偏移,且使用轉(zhuǎn)化的結(jié)果作為映射的起始地址。轉(zhuǎn)為過程如下:
Bitmap的偏移=基地址>>6。按這個公式,映射大小是映像大小右移6位。這個方法在映像需要重定位(ReloadImageAgain函數(shù))的時候也會被使用。
b)在進(jìn)程初始化階段寫私有模塊(exe文件等)的bits。它將調(diào)用MiCommitVadCfgBits,其是一個派遣函數(shù)。你能使用圖13作為參考。它在確定的場景被調(diào)用。這個函數(shù)的前置工作是在VAD描述的空間寫入bits。主要邏輯如下:
圖16 – MiMarkPrivateImageCfgBits函數(shù)處理寫入私有模塊的bits
MiMarkPrivateImageCfgBits函數(shù)實現(xiàn)向CFG Bitmap中寫入私有模塊(exe等)的bit信息。當(dāng)系統(tǒng)映射一個EXE的節(jié)或者啟動一個進(jìn)程時,這個函數(shù)被調(diào)用。
這個函數(shù)有2個參數(shù):
1)Cfg信息的全局變量地址
2)映像空間的VAD
VAD是用來描述虛擬內(nèi)存空間范圍的一個結(jié)構(gòu)。
函數(shù)的前置工作是將輸入的VAD的相關(guān)的壓縮的RVA列表轉(zhuǎn)化為bitmap信息,且在CFGBitmap中寫入bits。主要邏輯如下:
圖17 – MiMarkPrivateImageCfgBits的主要邏輯
在步驟A中,壓縮的RVA列表能從輸入的VAD關(guān)聯(lián)的Control_Area結(jié)構(gòu)中獲得,在MiRelocateImage中保存(參見第二節(jié))。
這個函數(shù)的主要步驟是步驟C。它實現(xiàn)私有寫入映射的MiCfgBitMapSection32節(jié)(在3.b節(jié)有描述)。寫入的私有的bits的映射是只讀的。向映射的空間寫入bits怎么實現(xiàn)?關(guān)鍵步驟如下:
i.獲得映射的空間地址的物理地址(PFN)
ii.申請一個空的頁表入口(PTE)并使用上步獲得的物理地址填充PTE,新的PTE被映射到相同的物理頁,其包含了映射的MiCfgBitMapSection32的虛擬地址。
iii.復(fù)制結(jié)果緩沖區(qū)(圖12)到新的PTE。物理頁將包含結(jié)果緩沖區(qū)的內(nèi)容
iv.釋放新的PTE
在上面步驟完成后,bitmap信息被拷貝到當(dāng)前進(jìn)程地址空間內(nèi)。但是不會影響MiCfgBitMapSection。換句話說,MiCfgBitMapSection不知道bitmap改變了。其他進(jìn)程也不會看到改變;新添加的bitmap信息對當(dāng)前進(jìn)程是私有的。
c)寫虛擬內(nèi)存操作的bits。如果一個進(jìn)程有虛擬內(nèi)存操作,它可能會影響CFGBitmap中的bitmap的bits狀態(tài)。從圖13的場景看,它將調(diào)用MiMarkPrivateImageCfgBits。函數(shù)的前置工作是復(fù)制“1”或“0”頁到CFGBitmap空間中。
i.對于NtAllocVirtualMemory函數(shù)
如果一個進(jìn)程調(diào)用NtAllocVirtualMemory函數(shù)來分配具有可執(zhí)行屬性的虛擬內(nèi)存,NT內(nèi)核將設(shè)置CFGBitmap中相關(guān)的位為“1”。但是如果分配的內(nèi)存的保護(hù)屬性有 SEC_WRITECOMBINE,NT內(nèi)核將使用“0”設(shè)置bitmap。
ii.對于MiProtectVirtualMemory函數(shù)
如果一個進(jìn)程調(diào)用MiProtectVirtualMemory來改變虛擬內(nèi)存的保護(hù)屬性為“可執(zhí)行”,NT內(nèi)核將設(shè)置CFGBitmap相關(guān)位為“1”。
d)寫映像和數(shù)據(jù)段映射的bits
i.對于映像(dll,EXE等)節(jié)的映射,如果映像不是共享的,處理過長如4.b節(jié)描述。如果是共享的,將由圖13中的MiMarkPrivateImageCfgBits函數(shù)處理。它遍歷映射空間中的每個頁且將頁地址轉(zhuǎn)化為CFGBitmap中的偏移。
i.如果CFGBitmap中的偏移不被PrototypePTE支持,相關(guān)的bits信息將被拷貝到CFGBitmap空間中。
ii.如果CFGBitmap中的偏移已經(jīng)有bitmap信息,CFGBitmap部分將改為只讀。
ii.對于數(shù)據(jù)段的映射,處理與4.c.i相同。
5.上面提到的步驟都發(fā)生在內(nèi)核模式下。但是對于用戶模式,CFGBitmap需要訪問LdrpValidateUserCallTarget函數(shù),它在上一部分已經(jīng)描述了。用戶模式下怎么知道CFGBitmap映射的地址?當(dāng)創(chuàng)建一個進(jìn)程,NT內(nèi)核調(diào)用PspPrepareSystemDllInitBlock函數(shù)來寫CFGBitmap映射的地址和全局變量的長度,其數(shù)據(jù)結(jié)構(gòu)是PspSystemDllInitBlock結(jié)構(gòu)。PspSystemDllInitBlock是修正過的地址并且從用戶模式和內(nèi)核模式都能訪問。
圖18 – 調(diào)用堆棧
用戶模式可以訪問硬編碼的PspSystemDllInitBlock全局變量的CFGBitmap字段。
6.在圖4中,_guard_check_icall函數(shù)指針將指向ntdll的LdrpValidateUserCallTarget。何時發(fā)生,如何發(fā)生?LdrpCfgProcessLoadConfig來完成這個工作。進(jìn)程創(chuàng)建過程將在用戶模式下調(diào)用LdrpCfgProcessLoadConfig。
圖19 – 在這個函數(shù)中,它將修改_guard_check_icall的值指向LdrpValidateUserCallTarget
7.在所有的準(zhǔn)備都完成后,如果間接調(diào)用的目標(biāo)地址相關(guān)的位在CFGBitmap中不是“1”,將觸發(fā)CFG。進(jìn)程將采取行動處理這個異常。處理函數(shù)是RtlpHandleInvalidUserCallTarget。這個函數(shù)使用間接調(diào)用的目標(biāo)為唯一的參數(shù)。函數(shù)的主要邏輯如下:
圖20 – RtlpHandleInvalidUserCallTarget的主要邏輯
函數(shù)的主要流程是校驗DEP狀態(tài)和觸發(fā)int 29中斷,這個內(nèi)核中斷處理例程是KiRaiseSecurityCheckFailure。它的行為是結(jié)束進(jìn)程。
如果一個間接調(diào)用的目標(biāo)地址的CFGBitmap中的相關(guān)的位不能訪問(如超出了CFGBitmap空間),意味著目標(biāo)地址是不可靠的。系統(tǒng)將拋出訪問異常。當(dāng)這個異常回到用戶模式的處理函數(shù)KiUserExceptionDispatcher時,它將調(diào)用RTLDispatchException。在RTLDispatchException中,它將校驗異常發(fā)生的地址。如果指令的地址能訪問CFGBitmap,它將繼續(xù)調(diào)用RtlpHandleInvalidUserCallTarget。
8.如果一個進(jìn)程需要自定義CFGBitmap,它能調(diào)用ntdll中的NtSetInformationVirtualMemory。在內(nèi)核中函數(shù)MiCfgMarkValidEntries實現(xiàn)了這個功能。MiCfgMarkValidEntries以一個緩沖區(qū)和長度作為參數(shù)。緩沖區(qū)中的每個單位是8字節(jié)。頭四個字節(jié)是目標(biāo)地址,其想在CFGBitmap中設(shè)置相關(guān)的位,且后四個字節(jié)是設(shè)置“0”或“1”的標(biāo)志。MiCfgMarkValidEntries自定義的CFGBitmap只在當(dāng)前進(jìn)程能看見。
9.如果一個攻擊者需要改變用戶模式下的CFGBitmap的內(nèi)容,是不可能的。因為CFGBitmap被映射為只讀(在3.b節(jié)討論過)。不管改內(nèi)存保護(hù)屬性還是向空間中寫值都將失敗。
0x02 CFG的弱點
當(dāng)然,這個機制不是沒有弱點的。我們指出了一些弱點如下:
CFGBitmap空間地址存儲在修正過的地址中,其能被用戶模式代碼獲得。這在CFG實現(xiàn)中討論過。這是很重要的安全問題,但是被簡單的放過了。
如果主模塊沒有開啟CFG,即使加載的啟用了CFG的模塊,進(jìn)程也不會受保護(hù)。
基于圖20,如果一個進(jìn)程的主模塊禁用了DEP(通過/NXCOMPAT:NO),能繞過CFG訪問處理,即使間接調(diào)用的目標(biāo)地址是不可靠的。
在CFGBitmap中的每個bit在進(jìn)程空間中表示8個字節(jié)。因此如果一個不可靠的目標(biāo)地址少于8個字節(jié),CFG將認(rèn)為是可靠的。
如果目標(biāo)函數(shù)是動態(tài)生成的(類似JIT技術(shù)),CFG的實現(xiàn)不能保護(hù)。這是因為NtAllocVirtualMemory將在CFGBitmap中為所有分配的可執(zhí)行的內(nèi)存空間設(shè)置為“1”(4.c.i描述)。通過MiCfgMarkValidEntries自定義的CFGBitmap解決這個問題是可能的。