2023年1月


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

 

WoW64是什么?

 

来自MSDN:

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

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

 

两个NT层,一个内核

 

在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位进程。看起来像这样:



STATUS_STACK_BUFFER_OVERRUN异常一般是指栈缓冲区溢出的溢出,代码为0xC0000409,消息提示一般为“Security check failure or stack buffer overrun”。微软给出的解释如下:系统在应用程序中检测到基于堆栈的缓冲区溢出。此溢出可能允许恶意用户获得此应用程序的控制权。/GS编译器开关允许创建堆栈“cookie”值,该值用作检测堆栈缓冲区溢出的金丝雀。如果cookie已损坏,则进程会立即终止自身,状态代码为STATUS_STACK_BUFFER_OVERRUN。

在做如上描述的时后,STATUS_STACK_BUFFER_OVERRUN是一个好名字。但是,随着时间的推移,随着我们遇到和处理问题越来越多,我们会发现其他立即终止进程的原因。其中一些与安全有关其他的没有。比如在遇到更糟糕的情况下希望在进程终止前立即停止,用fail fast这个词是用来形容这种情况的就非常好。甚至还有一个编译器内部函数来终止使用这个特殊状态代码的当前进程

似乎人们无法就是fail fast还是fast fail达成一致,所以在实践中,你可以看到这两种变化。这意味着现在当你得到一个STATUS_STACK_BUFFER_OVERRUN时,实际上并不意味着存在堆栈缓冲区溢出。这只意味着应用程序决定以极快的速度自行终止。

如果你查看winnt.h文件,您将看到各种快速失败代码的列表。这些代码指示发生了哪种类型的快速故障。

#define FAST_FAIL_LEGACY_GS_VIOLATION         0
#define FAST_FAIL_VTGUARD_CHECK_FAILURE       1
#define FAST_FAIL_STACK_COOKIE_CHECK_FAILURE  2
#define FAST_FAIL_CORRUPT_LIST_ENTRY          3
#define FAST_FAIL_INCORRECT_STACK             4
#define FAST_FAIL_INVALID_ARG                 5
#define FAST_FAIL_GS_COOKIE_INIT              6
#define FAST_FAIL_FATAL_APP_EXIT              7

当在C++语言中引入异常时,引入了相应的throw(…)动态异常说明符,注释了哪些异常可以由函数抛出。比如:

// this function might throw an integer or a pointer to char,
// but nothing else.
void foo() throw(int, char*);

windows系统的快速失败机制---fastfail,是一种用于“快速失败”请求的机制 — 一种潜在破坏进程请求立即终止进程的方法。 无法使用常规异常处理设施处理可能已破坏程序状态和堆栈至无法恢复的的严重故障。 使用 __fastfail 终止进程开销最少。

快速失败请求是独立的请求,通常只需执行两个指令。 一旦执行快速失败请求后,内核就会采取相应的行动。 在用户模式代码中,引发速快速失败事件时,除指令指针本身外不存在任何内存依赖项。 即使存在严重的内存破坏,也可最大限度地提高其可靠性。

用户模式开始失败显示为第二个机会非持续性异常,异常代码为 0xC0000409,至少包含一个异常参数。 第一个异常参数为 code 值。 此异常代码对 Windows 错误报告 (WER) 和调试基础结构指示进程已损坏并且应采取最少的进程内操作以响应故障。 内核模式快速失败请求可以通过使用专用检错代码 KERNEL_SECURITY_CHECK_FAILURE (0x139) 实现。 在这两种情况下,都没有调用异常处理程序,因为程序预期处于损坏状态。 如果存在调试程序,就有机会在终止进程之前检查程序的状态。

Windows 8 开始支持本机快速失败机制。 不支持本机快速失败指令的 Windows 操作系统通常会将快速失败请求视为访问冲突,或视为 UNEXPECTED_KERNEL_MODE_TRAP 错误检查。 在这些情况下,仍然会终止程序,但并不一定会快速终止。

windows提供了一个内部专用API来实现这种机制---__fastfail,

原型如下:

void __fastfail(unsigned int code);