5.1 64位系统新增机制

5.1.1 WOW64子系统

刚刚接触Windows 64位系统的读者可能都会有一个疑问:在64位系统上,之前的32位应用程序还能正常工作吗?答案是肯定的;那么系统是如何保证这点的呢?这就是接下来要介绍的WOW64子系统。

WOW64子系统(Windows-on-Windows 64-bit),是64位Windows系统为了兼容32位应用程序而新增的子系统。简单来说,读者可以先把它理解成一个轻量级的兼容层,这个兼容层主要工作在应用层,由三个DLL实现,分别为Wow64.dll、Wow64Win.dll 和Wow64Cpu.dll;三个DLL的功能和本章节内容关系不大,读者可以自行阅读MSDN资料,这里不详细介绍了。

当一个32位应用程序发起系统调用时,WOW64子系统拦截到这个系统调用,如果这个系统调用的参数包含指针,WOW64子系统就会先把这些指针的长度转换成合适的长度,然后再把系统调用请求提交给内核。通常把这个“拦截—转换”的过程称为“thunking”。试想一下,如果WOW64子系统没有thunking过程,当32位应用程序发起系统调用时,32位程序指针长度为4字节,而系统内核为64位,规定指针长度为8字节,这样内核在获取系统调用参数时,就会乱套。所以说thunking的存在,是32位应用程序能够运行在64位系统下的关键。

WOW64子系统有两个重要的模块,分别为文件系统重定向器(File System Redirector)模块和注册表重定向器(Registry Redirector)模块。

Windows 64位系统存在两个System32目录,分别为%windir%\System32和%windir%\SysWOW64目录;第一个目录下主要包含64位系统二进制文件,第二个目录下主要包含32位系统二进制文件。一般来说,32位应用程序只能加载32位的DLL,64位应用程序只能加载64位的DLL;读者可以考虑一下这种情况:一个32位应用程序在32位系统下运行时需要到System32目录下加载所依赖的系统DLL,当把这个32位应用程序放在64位系统下运行时,这个32位应用程序同样会到System32目录下加载所需的系统DLL,而64位系统的System32目录下包含的是64位的系统DLL文件,这样会导致32位应用程序无法启动。为了解决这个问题,WOW64子系统的文件系统重定向器对System32目录做了透明的重定向。在绝大多数情况下,32位应用程序访问%windir%\System32目录会被WOW64子系统重定向到%windir%\SysWOW64目录。请看下列示例代码。

上面代码用CreateFile函数在System32目录下创建了一个testfile.txt文件,如果代码被编译成32位应用程序,那么由于WOW64机制的存在,32位应用程序创建的testfile.txt会被重定向到SysWOW64目录下;如果代码被编译成64位应用程序,testfile.txt会在真正的System32目录下被创建。由此可见,相同的一份代码,编译成不同的平台应用,其最终效果也不一样。

一般来说,32位应用程序使用%windir%\SysWOW64目录,64位应用程序使用%windir%\System32目录,两个目录下的数据相互独立和隔离。

如果32位应用程序需要访问真实的System32目录,则需要调用系统API来关闭文件重定向功能:

Wow64DisableWow64FsRedirection函数只有一个参数,用于保存原来文件重定向器的状态,当需要恢复重定向器的状态时,可以使用Wow64RevertWow64FsRedirection函数:

Wow64RevertWow64FsRedirection函数根据OldValue参数来恢复文件重定向器的状态,而这个参数就是Wow64DisableWow64FsRedirection返回的参数。

请看下列示例代码。

上面的代码在创建文件前先关闭了文件重定向功能,所以无论代码编程成32位应用程序还是64位应用程序,行为都是在System32目录下创建testfile.txt文件。

读者需要注意一点,文件重定向器并非把所有%windir%\System32下的目录和文件都重定向,有一些特殊目录是不重定向的,它们分别是:

●%windir%\System32\catroot

●%windir%\System32\catroot2

●%windir%\System32\drivers\etc

●%windir%\System32\logfiles

●%windir%\System32\spool

注册表重定向器(Registry Redirector)和文件系统重定向器功能类似,但是注册表重定向器提供的功能更为复杂,除了提供重定向功能,还提供注册表反射功能(Registry Reflection)。反射功能与本章关系不大,不做介绍。和文件系统重定向器类似,在一般情况下,32位应用程序访问HKEY_LOCAL_MACHINE\SOFTWARE会被重定向到HKEY_LOCAL_MACHINE\SOFTWARE\ Wow6432Node。读者可以做一个小实验,在%windir%\SysWOW64目录下找到regedit.exe程序,这是32位的注册表编辑器,运行regedit后,在HKEY_LOCAL_MACHINE\SOFTWARE下创建一个hello项,关闭注册表编辑器,接着在%windir%\System32目录下打开64位注册表编辑器regedit.exe查看,读者可以发现,刚才创建的hello的注册表项存在于HKEY_LOCAL_ MACHINE\SOFTWARE\Wow6432Node下。

和文件重定向类似,操作注册表的API也提供了类似于开启和关闭重定向的功能。请看下列代码。

如果上面的代码编译成32位应用程序,那么程序会在HKEY_LOCAL_MACHINE\ SOFTWARE\Wow6432Node下创建hello子项;如果编译成64位应用程序,那么程序会在HKEY_LOCAL_MACHINE\SOFTWARE下创建hello子项。如果想明确在指定的位置下创建子项,那么32位应用程序可以这样实现:

使用KEY_WOW64_64KEY标志,可以通知WOW64子系统当前打开的为64位注册表,WOW64子系统不再对这个路径进行重定向。同理,如果64位应用程序想打开32位注册表,则可以使用KEY_WOW64_32KEY标志:对于KEY_WOW64_64KEY和KEY_WOW64_32KEY标志的使用,建议读者亲自编程测试,以加深理解。

5.1.2 PatchGuard技术

对于32位系统来说,驱动程序模块可以对内核的数据结构或者关键的函数进行挂钩和修改,但这种技术不再适用于Windows 64位系统,原因是微软在64位系统中引入了PatchGuard机制。

简单来说,PatchGuard机制就是系统会定时检查系统关键的位置,如SSDT(系统服务描述表)、GDT(全局描述表)、IDT(中断描述表)、系统模块(如ntoskrnl.exe、hal.dll)等,一旦发现这些关键位置的数据或代码被篡改,系统就会被触发蓝屏(BSOD)。正是因为如此,一些在32位系统上能使用的SSDT HOOK、INLINE HOOK、IDT HOOK等技术,在64位系统下全部不再适用。

引入PatchGuard的初衷是为了避免一些恶意的内核模块破坏系统内核。但凡事具有两面性,虽然PatchGuard可以避免系统内核被破坏,但是也阻碍了内核模块很多功能的实现。

初学者可以先简单了解PatchGuard机制给编程带来的限制,对于PatchGuard具体的实现和如何绕开,有兴趣的读者可以在网上查找相关资料。

5.1.3 64位驱动的编译、安装与运行

本节结合64位系统的机制,深入介绍64位驱动的编译、安装和运行的注意事项。64位驱动的编译和32位驱动的编译类似,在WDK的开始菜单中,找到64位编译环境的命令行工具,如x64 Checked Build Environment,打开后,切换到驱动代码Sources文件目录,和编译32位驱动相同,执行build命令即可开始编译;使用VS编译方式更为简单,直接在工具栏上选择x64,然后进行编译即可。

当使用32位应用程序来安装64位驱动,并且希望把驱动文件放在%windir%\System32\ drivers目录下时,需要考虑前面讨论的文件重定向机制。因为在默认情况下,32位应用程序把驱动文件复制到%windir%\System32\drivers目录,实际上是复制到%windir%\SysWOW64\drivers目录下,这时如果驱动的对应注册表项的ImagePath值设置成System32\drivers目录,其结果会导致驱动启动失败,原因是找不到对应文件。解决这个问题的方法很简单,只要在将驱动文件拷贝到driver32目录前,关闭文件重定向功能,确保文件真实被拷贝的路径和注册表中ImagePath设置的路径一致即可。

此外,从Windows Vista 64位系统开始,要求驱动文件必须经过微软指定的证书颁发机构颁发的微软代码签名证书签名,只有被签名的驱动文件才可以被加载运行,其目的有两个:一是确保驱动代码没有被非法篡改;二是确保驱动代码来源的可靠性,从而保护驱动代码的完整性。然而在开发调试驱动时,驱动文件往往是没有签名的,对于如何解决该问题,请读者复习第2章。