首页
社区
课程
招聘
[翻译]Windows是如何将64位Ntdll映射到32位进程的
发表于: 2018-3-14 18:18 8261

[翻译]Windows是如何将64位Ntdll映射到32位进程的

2018-3-14 18:18
8261

今天我们探索一个问题: 64位的ntdll是如何被加载到WoW64下的32位进程?今天的旅程将会带领我们进入到Windows内核逻辑中的未知领域,我们将会发现32位进程的内存地址空间是如何被初始化的。

来自MSDN:

WOW64是允许32位Windows应用程序无缝运行在64位Windows的模拟器。

换句话说,随着64位版本Windows的引进,Microsoft需要拿出一种允许在32位时代的Windows程序与64位Windows新的底层组件无缝交互的解决方案。特别是64位内存寻址和与内核直接交流的组件。

在32位的Windows系统中,要调用Windows API的应用程序需要经过一系列的动态链接库(DLL)。然而,所有的系统调用最终会定向到ntdll.dll,它是在用户模式下将用户模式API传递给内核的最高层。以调用CreateFileW为例,这个API调用源于用户模式下的kernel32.dl,随后它以NtCreateFile传递给ntdll,随后NtCreateFile通过系统调度程序将控制权传递给内核。


在32位Windows下这是非常简单的,然而,在WoW64下需要额外的步骤。32位的ntdll不可以直接将控制权交给内核,因为内核是64位的,只接受遵循64位ABI的类型(译者注:ABI,Application Binary Interface,应用二进制接口)。正因为如此,一个翻译层以几个标准的命名为wow64.dll,wow64cpu.dll和wow64win.dll的DLL的形式被添加到64位Windows。这几个DLL负责将32位调用转换成64位调用。那些调用最终被定向到映射到每个32位进程中的64位ntdll。许多关于这种从32位系统调用到64位系统调用(1)的神奇转换的信息是可获得的,所以我们不会从这里进入。我们最关注的是内核何时和怎样将64位版本的ntdll映射到一个32位进程。看起来像这样:

我们特别关注倒数第二项。我们能发现ntdll被映射到地址是64位地址范围(7FFFFED40000-7FFFFEF1FFFF),而且它的位置在Windows 64位系统文件所在的System32\路径下。然而,我们知道32位进程不可以访问或者运行在64位内存空间。

为了理解上面输出的内容,我们首先讨论VAD(Virtual Address Descriptor,虚拟地址描述符)是什么和它将如何帮助我们理解加载64位dll到32位进程的机制的。

VAD是Windows操作系统跟踪系统中可用物理内存的许多方法之一。VAD专门跟踪每个进程用户模式范围的保留的和提交的地址。任何时候一个进程请求一些内存,一个新的VAD实力被创建用来跟踪内存。

VAD被构造成一个自平衡树,每个节点描述了一段内存范围。每个节点至多包含两个子节点,左边是低地址,右边是高地址。每个进程被分配一个VadRoot,之后通过遍历VadRoot来分辨额外用来描述保留或提交的虚拟地址范围的额外节点。我们需要关注WindDBG中的!vad命令的输出,因为这是我们将大量使用来跟踪64位Windows中32位进程的映射的输出。对于这个练习,不是所有的域对我们来说都是特别有趣的。我们考虑测试程序HelloWorld.exe的输出。通过!process ProcessObject 命令的输出来分辨我们进程的VadRoot。 

一旦我们确定了VadRoot,我将地址输入到 !vad 命令。(输出为了容易分析已被截断)

我们看到五列: "VAD", "Level", "Start", "End", 和"Commit".!vad命令 接受VAD实例的地址;在我们的例子中,我们已经为它提供了在此进程中通过使用!process命令获得的VadRoot。

VAD地址是当前VAD结构体或实例的地址:

进程初始化的早期,在主可执行文件被映射和初始化之前,Windows为特殊区域确定和保留一些地址范围。其中包含初始进程地址空间,共享系统空间(_KUSER_SHARED_DATA),控制流守护位图区域,和NT本地子系统(ntdll)。由于进程初始化整体的复杂性,我们只关注最后一块,它包含32位ntdll和64位ntdll加载到32位进程地址空间的逻辑。我们关注一系列的API调用和在每个点的内存区域的虚拟地址描述符(VAD)。为了让内核区分怎样映射一个新进程,它需要知道是否这是一个WoW64进程。当进程对象最初被创建,内核通过读取名为_EPROCESS.Wow64Process的未文档化结构体_EPROCESS结构体的值来实现此操作。

PspAllocateProcess是我们探索开始的地方,但是更具体的说,我们开始在MmInitializeProcessAddressSpace()。MmInitializeProcessAddressSpace()负责与一个新进程地址空间有关的初始化。它调用MiMapProcessExecutable,该函数创建了定义初始进程可寻址内存空间的VAD项,随后将新创建的进程映射到它的基虚拟地址。

一个特别有趣的函数是PspMapSystemDlls。我们关注在调用PspMapSystemDlls之前的进程地址空间的样子。在WinDBG中确保我们当前处于我们测试应用程序的上下文中(.process),并寻找当前VadRoot(!vad output)。


到目前为止我们可以观察到,我们的进程在32问地址空间中被映射和分配了一个基地址(1200),内核共享内存(0x7FFE0000-0x7FFE0FFF) 和64KB保留内存区域(0x7FFE1000-0x7FFEFFFF) 也已经被映射到他们各自的虚拟地址。

PspMapSystemDlls通过一个包含多个平台子系统模块的全局指针迭代。对于x86和x64Windows,这些是分别位于C:\Windows\SysWow64 和C:\Windows\System目录中的ntdll.dll。

一旦PspMapSystemDlls发现要加载的DLL,它调用PspMapSystemDll 来映射他们(DLLs)到进程的地址空间。该函数非常简短,下面展示了一个片段。为了正确映射本地子系统,需要满足一些条件。


PspMapSystemDll通过调用MmMapViewOfSection实现实际的本地DLL的映射,并保存所占的基地址。在这两个DLL映射完成并且他们的VAD项初始化完成后,我们的32位进程地址空间看起来像这样:

所以现在,我们映射完我们的进程(0xc40000-0xcf2fff),内核共享内存空间(0x7ffe0000-0x7ffe0fff),32位地址空间的有效结束区域(0x7ffe1000-0x7ffeffff),和我们的两个NT子系统DLL。


[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2018-3-17 20:25 被sudozhange编辑 ,原因:
收藏
免费 1
支持
分享
最新回复 (12)
雪    币: 286
活跃值: (67)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
xjj
2
这个机制楼主已经讲的很详细了,那么我说点我利用这个机制分析得到的东西:
利用这个机制可以达到R3层拦截所有驱动交互的目的,可以实现在R3层就可以处理驱动层函数调用前的参数以及驱动层处理完返回的结果,由于在R3层完成,不碰触R0层,所以对技术要求不高,稳定性好,但达到的效果却可以控制R0层。
2018-3-16 11:29
0
雪    币: 285
活跃值: (1095)
能力值: ( LV13,RANK:405 )
在线值:
发帖
回帖
粉丝
3
xjj 这个机制楼主已经讲的很详细了,那么我说点我利用这个机制分析得到的东西: 利用这个机制可以达到R3层拦截所有驱动交互的目的,可以实现在R3层就可以处理驱动层函数调用前的参数以及驱动层处理完返回的结果, ...
理解的比我理解的要深刻的多
2018-3-16 13:50
0
雪    币: 2250
活跃值: (180)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
4
xjj 这个机制楼主已经讲的很详细了,那么我说点我利用这个机制分析得到的东西: 利用这个机制可以达到R3层拦截所有驱动交互的目的,可以实现在R3层就可以处理驱动层函数调用前的参数以及驱动层处理完返回的结果, ...
求详解
2018-3-17 16:49
0
雪    币: 3739
活跃值: (3877)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
5
好文!
2018-3-17 18:40
0
雪    币: 12848
活跃值: (9147)
能力值: ( LV9,RANK:280 )
在线值:
发帖
回帖
粉丝
6
阻止wow64程序的32代码访问64位地址空间是依赖GDT和cs段寄存器实现的,这里好像没讲?
最后于 2018-3-18 14:32 被hzqst编辑 ,原因:
2018-3-18 14:32
0
雪    币: 1485
活跃值: (1135)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
@xjj  r3不能拦截驱动吧?怎么实现?
2018-3-19 17:13
0
雪    币: 286
活跃值: (67)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
xjj
8
冰雄 @xjj r3不能拦截驱动吧?怎么实现?
基于楼主说的这个机制的情况下,是可以的,R3不能触碰R0的代码,,但他可以改变传递给R0的参数,以及R0执行完返回给R3的结果。
2018-3-19 17:30
0
雪    币: 286
活跃值: (67)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
xjj
9
岁月别催 求详解
https://bbs.pediy.com/thread-225317.htm
已开贴解答
2018-3-20 17:57
0
雪    币: 222
活跃值: (15)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
打包PDF,供收藏用
上传的附件:
2018-4-26 14:24
0
雪    币: 0
活跃值: (150)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
受教了
2018-9-5 17:10
0
雪    币: 248
活跃值: (3789)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
路过,mark一下
2018-9-5 17:19
0
雪    币: 11110
活跃值: (17638)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
非常有参考价值的文章,来支持
2018-9-5 20:52
0
游客
登录 | 注册 方可回帖
返回
//