WDM驱动针对Win8前后系统的兼容性问题

引用注明>> 【作者:张佩】【原文:www.yiiyee.cn/blog

最近我写了一个WDM驱动的demo工程,选择target OS是Win8,在VS2012上编译通过后,安装在Win8系统上能正常运行。换了一台Win7系统,安装驱动的过程中即遇到蓝屏。后来发现问题不在安装上,蓝屏乃发生在驱动加载的时候。

因为驱动特别简单,我先排除代码出错的可能。为了比较,我接下来又创建了一个KMDF框架的驱动demo工程,测试过程如法炮制,却没有碰到任何问题

我立刻想到的是,WDK 8.0对WDM框架可能做了兼容性方面的修改。到底怎么回事呢?一起来看看吧。

集成开发环境

Visual Studio 2012(下称VS2012)中包含了Windows驱动程序编译器,使得Windows驱动开发也可以在集成开发环境中进行了。安装VS2012后,再安装Win8 WDK,打开VS2012会发现多了两种新的“平台工具集”,支持Windows内核和用户驱动程序的编译。如下图所示:

toolset

新建的内核驱动项目,会自动选择合适的工具集:WindowsKernelModeDriver8.0。更改工作集,会影响到工程的设置,如包含目录。如果把驱动项目的工具集改成用户程序的工具集的话,编译器会报很多找不到WDK头文件的错误(如<ntddk.h>)。

targetOs

和控制台编译环境类似,在IDE环境中我们也可以选择目标系统,即操作系统和硬件平台。如右图所示:

Security Cookie导致的不兼容

编译器默认开启/GS编译选项,用来保护内核栈的完整性。编译器会在程序开始的地方,保存一个cookie值到栈上;在程序退出时再检查这个cookie值是否被破坏,如果被破坏,说明栈溢出,表明系统遭到了破坏从而需要蓝屏保护。

GS是一种被普遍运用的保护机制。开启了GS选项后,编译器会链接一个GS相关的库文件来实现GS功能,当目标系统为Win7时库文件是BufferOverflowK.lib,当目标系统为Win8+时库文件是BufferOverflowFastFailK.lib。链接器是如何实现栈保护的呢?它先在驱动加载的时候,也就是GsDriverEntry函数内初始化cookie。然后在每个驱动函数的开始和结束的地方,添加cookie检查的代码。

链接器为了对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编译的驱动没问题呢?原因很简单:KMDF框架链接的静态库文件还是BufferOverflowK.lib。可使用depends工具进行确认。

如何避免

虽然KMDF兼容性良好,但我有些时还是会写WDM驱动的。因为框架有时显得臃肿,WDM有时显得简单。如何避免呢。下面先介绍三种简单的方法:

  1. 其一是为Win7及以前系统和Win8+系统产生不同的镜像文件,区别对待。这也是比较推荐的方法。
  2. 其二只为Win7系统编译驱动程序,它也可以在Win8+系统上被正确地安装、加载和运行,没任何问题。但不保证以后的Windows系统仍有此兼容性。
  3. 其三是干脆关闭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. 用记事本打开驱动项目的工程文件(.vcproj)并添加下面的内容:
<KernelBufferOverflowLib>$(DDK_LIB_PATH)\BufferOverflowK.lib<KernelBufferOverflowLib>

 

9,654 total views, 5 views today

《WDM驱动针对Win8前后系统的兼容性问题》有2个想法

发表评论

电子邮件地址不会被公开。