目录

Windows WOW64原理

注意事项

本文仅会专注于x86、x64架构下的WOW64原理,不会涉及ARM、Intel Itanium等架构下的东西,不过原理是大同小异的。

动机

为了让32位程序可以运行在64位Windows上,Windows引入了WOW64(Windows 32-bit on Windows 64-bit),其实际为一组用户层DLL组成的模拟器。

在讲WOW64的具体实现前,先思考下,如果要让32位程序运行在64位Windows,我们该怎么实现?具体的,32位程序运行在64位Windows会出现什么问题?我们又该怎么解决这些问题?

系统服务调用的转换

第一个问题,怎么处理32位程序的系统服务调用?32位程序系统服务调用的参数大小往往和64位程序系统服务调用的参数大小是不一样的,比如最典型的,指针参数在前者为32位,在后者为64位,那怎么办?在内核中分两套函数,一套给32 位,一套给64位?还是就一套系统服务实现,然后加一层中转层,在传给系统服务前,把需要转换的32位参数转换成对应的64位参数,然后在返回的时候,把需要转换的64位参数转换成32位参数,注意,这里的32位参数不是指参数的大小是 32位,而是指32位程序传递的参数,参数大小则可以是8位、16位等,再者,这里只转换需要转换的,兼容的可以不用转换,同理,64位参数也是一个意思。两套函数的话,不用中转,性能比较高,但是代码重复量大,一个改动往往需要两处都改,不易维护,容易出BUG,一套函数+中转层的话,转换需要额外的开销,但是代码就一份,有统一的控制点,容易维护,考虑到转换的开销在现代CPU这么快的情况下,几乎不算什么,故后面这个方案比较合适。

那WOW64是如何实现这个中转层的呢?WOW64引入了三个DLL,分别是:

  1. Wow64.dll:提供一些核心的基础设施,以及实现 Ntoskrnl.exe提供的系统服务的中转函数,我们称为thunks,即桩函数,该命名的原因是桩函数不包含函数的实现,仅仅做转发、转换等简单的操作。
  2. Wow64Win.dll:提供Win32k.sys提供的系统服务的thunks。
  3. Wow64Cpu.dll:提供对x86程序运行在x64的支持,等下我们会看到,它实现了CPU特定的从兼容模式到64位模式来回转换的代码(IA-32e同时支持两个子模式,一个是兼容模式,一个是64位模式,前者用于运行32位代码,后者用于运行64位代码),由于是CPU特定的,故该DLL名字的后缀带CPU。

当创建一个32位程序的进程时,一开始CPU是运行于64位模式的,进程管理器直接映射64位Ntdll.dll(文件在C:\Windows\System32\ntdll.dll)到进程空间中,之后调用PE loader的初始化代码时,loader检测到该程序是32位程序,于是加载了Wow64.dll,Wow64.dll接着便映射32位Ntdll.dll(文件在 C:\Windows\SysWOW64\ntdll.dll)到进程空间中,此时,进程空间同时存在64 位和32位的Ntdll.dll,接着Wow64.dll便设置线程上下文到32位的Ntdll.dll中,接着便把CPU模式从64位模式切换到兼容模式去执行32位Ntdll.dll中PE loader 的初始化代码了,之后该进程便持续处于兼容模式了。

当CPU处于兼容模式时,32位的Loader会继续加载其他本进程所依赖的DLL,包括一些系统核心DLL,如User32.dll、Gdi32.dll等,比如加载User32.dll,加载路径会是:C:\Windows\System32\user32.dll,由于WOW64会对路径进行转换,所以实际上加载路径会是C:\Windows\SysWOW64\user32.dll,而64位Windows在 C:\Windows\SysWOW64目录中放了特殊版本的32位系统DLL,如:Ntdll.dll、 User32.dll、Gdi32.dll等,这些特殊的32位系统DLL会调用WOW64中对应的 thunks,而不会直接发起系统服务调用,当32位代码调用32位系统DLL的系统服务thunks时,这些thunks转而调入WOW64中去,由WOW64把CPU切换到64位模式,然后执行必要的参数转换,接着去执行64位系统DLL中的系统服务thunks,最终切换到内核模式,真正执行系统服务代码,最后一路返回到WOW64的thunks,再由thunks实现必要的返回值转换,最终把CPU切换回兼容模式,返回到32位调用者那里。

我们可以看一个例子,下图是32位Ntdll.dll中的NtGetContextThread的thunk:

WinDbg用 ntdll_32位基地址 的形式显示32位Ntdll.dll,64位Ntdll.dll则会直接显示为ntdll,以此作为区分。代码中,先把eax赋值为系统服务号,然后便调用了32位Ntdll.dll中的Wow64SystemServiceCall,其代码如下:

Wow64SystemServiceCall跳转到Wow64Transition变量中存的一个地址,值为774b7000,如下:

774b7000代码如下:

为Wow64Cpu.dll中的KiFastSystemCall,其跳转到段选择子为33的代码段,这里段选择子33对应的段描述符表的L位为1,表示是long mode,即64位模式,也就是说,Wow64Cpu.dll利用跳转到一个特殊段,把L位设置成1,实现从兼容模式转到64位模式。以下为段描述符的格式,从Intel Manual中截出来的,可以看到, L位代表目标代码段是否是64位代码段:

故Wow64Cpu.dll确实是通过CPU特定的方法来从32位模式(兼容模式)转换到64 位模式的,把Wow64Cpu.dll单独独立成一个DLL是因为:这里的转换方法是CPU特定的,不同的CPU方法不同,比如ARM架构就有不同的切换方法,ARM架构特定的转换代码也会放到ARM专用的DLL里面(即wowarmw.dll)。如果不把CPU特定的代码独立到一个单独的DLL里面,那么会产生太多的DLL版本,比如原本Wow64.dll 可能就变成Wow64X64.dll、Wow64ARM.dll以及Wow64Itanium.dll(用于支持 Intel Itanium)了,同理,Wow64Win.dll也会分3个版本,最终会有6个DLL(考虑下再增加一个系统架构会怎样?8个)。

综上,几个Wow64 DLL和系统DLL的关系如下:

32位的Ntdll.dll调用Wow64.dll和Wow64win.dll的thunks,而该过程依赖于 Wow64Cpu.dll对模式进行转换,WOW64的thunks则转而调用64位Ntdll.dll的 thunks,切换到内核模式,执行Ntoskrnl.exe或者Win32k.sys中的内核系统服务代码,最终一路返回。

文件系统重定向

为了减少代码的修改量, Windows仍然保持把系统文件放在 C:\Windows\System32等目录,而没有转而放到C:\Windows\System64等目录(根本没有System64这个目录),这意味着如果你代码中很多地方写死了 C:\Windows\System32等系统目录的路径,在将你的程序移植到64位系统时,你一般不需要动这些路径。

但是,考虑一个32位程序访问系统目录的情况,比如访问C:\Windows\System32 目录去读取里面系统文件的内容等,它想要访问的应该是32位版本的内容,而不是64位版本的内容,而前面说了,Windows为了减少移植程序时代码的修改量, 64位系统文件的存放路径仍保持和32位一样,这意味着C:\Windows\System32存放的是64位的文件,那怎么办呢?答案是WOW64会在32位程序调用文件API时,执行路径转换工作,比如会把C:\Windows\System32转换成C:\Windows\SysWOW64,从而让它访问到32位版本的内容,同理,其他系统路径也一样,不过仍有部分目录是没有转换的,这些路径要么不需要转换,要么不能转换,如下:

  1. %windir%\system32\catroot
  2. %windir%\system32\catroot2
  3. %windir%\system32\driverstore
  4. %windir%\system32\drivers\etc
  5. %windir%\system32\logfiles
  6. %windir%\system32\spool

异常分发

32位程序在64位系统中运行,异常分发的核心部分还是64位的,这意味着, CONTEXT等结构体中的内容是64位的内容,这些64位的结构体,在传递在32位的调试器或者SEH等处理之前,需要转换成32位的结构,WOW64通过Hook KiUserExceptionDispatcher,即用户层的异常分发枢纽,来实现这些转换。

进程空间

64位Winodws中,由于2-4GB的内存段不用存内核代码(64位内核的代码存在更高的地址处),故运行在64位Windows中的32位程序,可以在传统的2GB地址空间以及4GB地址空间之间进行选择,后者需要在映像头中设置large address space aware标志位。

其他

其他还有注册表重定向,I/O控制请求等的特殊处理,本文不包含这部分内容,读者感兴趣的话可以进一步阅读本文的参考文章。

参考文章

  1. Running 32-bit Applications
  2. Microsoft Windows Internals, Fourth Edition
  3. WOW64!Hooks: WOW64 Subsystem Internals and Hooking Techniques
  4. Intel 64 and IA-32 Architectures Software Developer’s Manual Combined Volumes: 1, 2A, 2B, 2C, 2D, 3A, 3B, 3C, 3D and 4 Order Number: 325462-075US June 2021