Win8驅動的兼容性問題

引用註明>> 【做者:張佩】【原文:www.yiiyee.cn/blog算法

最近我發現爲Win8開發的驅動程序,有些能安裝在Win7上(包括更早系統),有些則不能。那些不能安裝的狀況很可怕:一旦安裝並加載驅動,系統就會馬上藍屏。針對這個問題,作了一番調查研究。發現了一個簡單的規律:windows

若是開發時使用的是WDM驅動框架,則存在此問題;若是使用KMDF驅動框架,則正常。cookie

這是爲何呢?一塊兒來看看吧。框架

VS2012集成開發環境

Visual Studio 2012(簡稱VS2012)中包含了Windows驅動程序編譯器,使得Windows驅動也能夠在Visual Studio的集成開發環境中進行開發了。安裝VS2012後,再安裝Win8 WDK,打開VS2012會發現多了兩種新的「平臺工具集」,支持Windows內核和用戶驅動程序的編譯。以下圖所示:yii

toolset

新建的內核驅動項目,會自動選擇合適的工具集:WindowsKernelModeDriver8.0。更改工做集,會影響相關的工程項目設置,好比包含目錄的查找路徑等。因此,若是把驅動項目的工做集改爲用戶程序相關的話,編譯器會報不少找不到頭文件的錯誤,好比找不到<ntddk.h>。函數

targetOs

和之前的控制檯編譯環境相似,在IDE環境中,咱們也能夠選擇不一樣的目標系統:操做系統,硬件平臺。經過工具欄上的列表框進行選擇。以下圖所示:工具

咱們能夠經過工程嚮導來產生一個WDM內核驅動項目,這裏將項目名稱設爲Test。測試

Security Cookie致使的不兼容

編譯器默認開啓/GS編譯選項,用來保護內核棧的完整性。編譯器會在程序開始的地方,保存一個cookie值到棧上;在程序退出時再檢查這個cookie值是否被破壞,若是被破壞,說明棧溢出,代表系統遭到了破壞從而須要藍屏保護。ui

GS是一種被廣泛運用的保護機制。開啓了GS選項後,編譯器會連接一個GS相關的庫文件來實現GS功能,當目標系統爲Win7時庫文件是BufferOverflowK.lib,當目標系統爲Win8+時庫文件是BufferOverflowFastFailK.lib。連接器是如何實現棧保護的呢?它先在驅動加載的時候,也就是GsDriverEntry函數內初始化cookie。而後在每一個驅動函數的開始和結束的地方,添加cookie檢查的代碼。spa

連接器爲了對cookie進行初始化,會爲驅動程序從新生成一個名爲GsDriverEntry的入口函數,初始化Cookie後再調用驅動程序本身的DriverEntry入口函數。下面是一個典型的GsDriverEntry的實現:

Test!GsDriverEntry:
82ea403e 8bff            mov     edi,edi
82ea4040 55              push    ebp
82ea4041 8bec            mov     ebp,esp
82ea4043 e8bdffffff      call    Test!__security_init_cookie// 初始化cookie
82ea4048 5d              pop     ebp
82ea4049 e9b8fff7ff      jmp     Test!DriverEntry (82e24006)//調用DriverEntry
82ea404e cc              int     3

下面是Win7版本security_init_cookie函數的邏輯:

test!__security_init_cookie:
82eb3005 a10050e382      mov     eax,dword ptr [test!__security_cookie (82e35000)] ds:0023:82e35000=00300083
82eb300a b94ee640bb      mov     ecx,0BB40E64Eh
82eb300f 85c0            test    eax,eax
82eb3011 7404            je      test!__security_init_cookie+0x12 (82eb3017) // 判斷是否等於0
82eb3013 3bc1            cmp     eax,ecx                                     // 判斷是否等於0BB40E64Eh
82eb3015 751a            jne     test!__security_init_cookie+0x2c (82eb3031)
82eb3017 a13040e382      mov     eax,dword ptr [test!KeTickCount (82e34030)] // 獲取當前系統時間
82eb301c 8b00            mov     eax,dword ptr [eax]
82eb301e 350050e382      xor     eax,offset test!__security_cookie (82e35000)
82eb3023 a30050e382      mov     dword ptr [test!__security_cookie (82e35000)],eax
82eb3028 7507            jne     test!__security_init_cookie+0x2c (82eb3031)
82eb302a 8bc1            mov     eax,ecx
82eb302c a30050e382      mov     dword ptr [test!__security_cookie (82e35000)],eax // 生成新cookie值

// 返回
82eb3031 f7d0            not     eax
82eb3033 a30450e382      mov     dword ptr [test!__security_cookie_complement (82e35004)],eax
82eb3038 c3              ret

這段邏輯先檢查security_cookie的當前值,若是不等於0且不等於0xBB40E64E,就當即退出;不然根據當前的系統時間,產生一個隨機的cookie值。這裏的值0xBB40E64E是個特徵值,多是系統默認生成的。大部分的鏡像在加載的時候,其cookie都會被初始化成這個特徵值。

當目標OS爲Win8時,編譯器默認連接BufferOverflowFastFailK.lib文件,它使用新的Cookie算法。正是這個新算法致使了不兼容性。看看Win8中security_init_cookie函數的邏輯,它硬是和0xBB40E64E較上了勁:

mov     eax, __security_cookie
test    eax, eax
jz      short loc_404029
cmp     eax, 0BB40E64Eh // 若是等於0BB40E64Eh,跳到下面產生0x29中斷,產生藍屏
jz      short loc_404029
not     eax
mov     __security_cookie_complement, eax 
retn

loc_404029:                             ; CODE XREF:
push    6
pop     ecx
int     29h               // 藍屏

它判斷當前的cookie值是否等於特徵值0xBB40E64E,若是相等,馬上藍屏。神奇的是,在 Win7及之前的OS上,大部分的驅動程序的cookie值都會被加載器初始化爲0xBB40E64E。而在Win8+系統上則永遠不會這樣。這就是爲何能在Win8+正常運行的驅動,一放到Win7上就藍屏的緣由。

WDM和KMDF的區別

那爲何使用KMDF編譯的驅動,又沒有問題呢?緣由很簡單,KMDF框架連接的靜態庫文件仍是BufferOverflowK.lib。不保證KMDF之後的版本不會連接新庫文件的可能。

如何避免

雖然KMDF兼容性良好,但不少狀況下,仍是會用WDM框架編寫驅動,並且還有不少小端口驅動也可能存在相似問題。如何避免呢。有三種簡單的方法:

其一是爲Win7及之前系統和Win8+系統產生不一樣的鏡像文件。這也是比較推薦的方法。

其二是在Win8+系統上安裝爲Win7系統編譯的驅動程序。通過測試,大多數爲Win7編譯的驅動程序,都能正常運行在Win8和Win blue系統上。但不保證更新的Windows系統出來後,這種向前兼容性仍然有效。

其三是乾脆關閉GS編譯選項。但這樣就缺乏了棧保護,不推薦。

最後介紹一種MSDN中介紹的比較高級的修改編譯選項的辦法,可讓編譯器在爲Win8+目標系統編譯驅動程序時,仍選擇舊版本的棧保護庫文件BufferOverflowK.lib:

  1. 手動編譯,手動設定KernelBufferOverflowLib的路徑:
msbuild /p:KernelBufferOverflowLib="C:\Program Files (x86)\Windows Kits\8.1\Lib\win8\km\x64\BufferOverflowK.lib" /p:platform=x64 /p:Configuration="Win8 Release" myDriver.sln
  1. 用記事本軟件打開驅動項目的工程文件,並在合適的地方添加下面的屬性:
<KernelBufferOverflowLib>$(DDK_LIB_PATH)\BufferOverflowK.lib<KernelBufferOverflowLib>
相關文章
相關標籤/搜索