引用註明>> 【做者:張佩】【原文:www.yiiyee.cn/blog】算法
最近我發現爲Win8開發的驅動程序,有些能安裝在Win7上(包括更早系統),有些則不能。那些不能安裝的狀況很可怕:一旦安裝並加載驅動,系統就會馬上藍屏。針對這個問題,作了一番調查研究。發現了一個簡單的規律:windows
若是開發時使用的是WDM驅動框架,則存在此問題;若是使用KMDF驅動框架,則正常。cookie
這是爲何呢?一塊兒來看看吧。框架
Visual Studio 2012(簡稱VS2012)中包含了Windows驅動程序編譯器,使得Windows驅動也能夠在Visual Studio的集成開發環境中進行開發了。安裝VS2012後,再安裝Win8 WDK,打開VS2012會發現多了兩種新的「平臺工具集」,支持Windows內核和用戶驅動程序的編譯。以下圖所示:yii
新建的內核驅動項目,會自動選擇合適的工具集:WindowsKernelModeDriver8.0。更改工做集,會影響相關的工程項目設置,好比包含目錄的查找路徑等。因此,若是把驅動項目的工做集改爲用戶程序相關的話,編譯器會報不少找不到頭文件的錯誤,好比找不到<ntddk.h>。函數
和之前的控制檯編譯環境相似,在IDE環境中,咱們也能夠選擇不一樣的目標系統:操做系統,硬件平臺。經過工具欄上的列表框進行選擇。以下圖所示:工具
咱們能夠經過工程嚮導來產生一個WDM內核驅動項目,這裏將項目名稱設爲Test。測試
編譯器默認開啓/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上就藍屏的緣由。
那爲何使用KMDF編譯的驅動,又沒有問題呢?緣由很簡單,KMDF框架連接的靜態庫文件仍是BufferOverflowK.lib。不保證KMDF之後的版本不會連接新庫文件的可能。
雖然KMDF兼容性良好,但不少狀況下,仍是會用WDM框架編寫驅動,並且還有不少小端口驅動也可能存在相似問題。如何避免呢。有三種簡單的方法:
其一是爲Win7及之前系統和Win8+系統產生不一樣的鏡像文件。這也是比較推薦的方法。
其二是在Win8+系統上安裝爲Win7系統編譯的驅動程序。通過測試,大多數爲Win7編譯的驅動程序,都能正常運行在Win8和Win blue系統上。但不保證更新的Windows系統出來後,這種向前兼容性仍然有效。
其三是乾脆關閉GS編譯選項。但這樣就缺乏了棧保護,不推薦。
最後介紹一種MSDN中介紹的比較高級的修改編譯選項的辦法,可讓編譯器在爲Win8+目標系統編譯驅動程序時,仍選擇舊版本的棧保護庫文件BufferOverflowK.lib:
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
<KernelBufferOverflowLib>$(DDK_LIB_PATH)\BufferOverflowK.lib<KernelBufferOverflowLib>