3 《Undocumented Windows 2000 Secrets》翻译 --- 第四章

第四章 探索 Windows 2000 的内存管理机制
【3 《Undocumented Windows 2000 Secrets》翻译 --- 第四章】翻译: Kendiv ( fcczj@263.net)
更新: Sunday, February 17, 2005
声明:转载请注明出处,并保证文章的完整性,本人保留译文的所有权利 。
Memory Spy Device 示例
微软对 Windows NT 和 2000 说的最多的就是它们是 安全的操作系统。它们不但在网络环境中加入了用户验证系统,同时还加强了系统的稳健性( robustness ),以进一步降低错误应用程序危及系统完整性的概率,这些错误的程序可能使用了非法的指针或者在其内存数据结构以外的地方进行了写入操作 。这些在 Windows 3.x 上都是十分让人头疼得问题,因为 Windows 3.x 系统和所有的应用程序共享单一的内存空间 。Windows NT 为系统和应用程序内存以及并发的进程提供了完全独立的内存空间 。每个进程都有其独立的 4GB 地址空间,如 图 4-2 所示 。无论何时发生任务切换,当前的地址空间都会被换出( switch out ),同时另一个被映射进来,它们各自使用不同的段寄存器、页表和其他内存管理结构 。这种设计避免了应用程序无意中修改另一个程序所使用的内存 。由于每个进程必然会要求访问系统资源,所以在 4GB 空间中总是包含一些系统数据和代码,并采用了一个不同的技巧来保护这些内存区域不被恶意程序代码所覆写( overwritten ) 。
Windows 2000 的内存分段
Windows 2000 继承了 Windows NT 4.0 的基本内存分段模型,默认情况下,该模型将 4GB 地址空间划分为相等的两块 。低一半的地址范围是: 0x00000000 ---- 0x7FFFFFFF,其中包含运行于用户模式(用 Intel 的术语来说是,是特权级 3 或 Ring 3 )的应用程序的数据和代码 。高一半的地址范围是: 0x80000000 --- 0xFFFFFFFF,默认全部保留给系统使用,位于这一范围的代码运行于内核模式(即特权级为 0 或 Ring 0 ) 。特权级决定了代码可以执行什么操作以及可以访问那一个块内存 。这意味着对于低特权级的代码来说,会被禁止执行某些 CPU 指令或访问某些内存区域 。例如,如果一个用户模式下的程序触及了任何 0x80000000 (即 4GB 地址空间中的高一半)以上的地址,系统会抛出一个异常并同时终止该程序的运行,不会给其任何机会 。
图 4-5. 用户模式下不能访问 0x80000000 以上的地址
图 4-5 展示了程序试图读取 0x80000000 地址时的情况 。这种严格的访问限制对于系统的完整性来说是好事,但对于调试工具就不是什么好消息了,因为调试工具需要访问所有可用内存 。幸运的是,存在着一个简单的方法:采用内核驱动程序,和系统本身类似,它也运行于高特权级(即 Ring 3 ),因此它们可以执行所有的 CPU 指令,可访问所有的内存区域 。这其中的诀窍就是将一个 Spy 驱动程序注入系统,用它来访问需要的内存,并将读到的内容发送到它的搭档程序,该搭档程序会在用户模式下等待 。当然,内核驱动程序不能读取虚拟内存地址,而且得不到分页机制的支持 。因此,这样的驱动程序必须在访问一个地址之前小心的检查它,以避免出现蓝屏死机( Blue Screen Of Death,BSOD ) 。相对于应用程序引发的异常(仅会终止出现问题的程序),驱动程序引发的异常会停止整个系统,并强迫进行重启 。
设备 I/O 控制 Dispatcher ( Device I/O Control Dispatcher )
本书光盘上有一个通用 Spy Device 的源代码,该 Spy Device 作为内核驱动程序实现 。可以在 srcw2k_spy 目录下找到它的源代码 。这个设备基于第三章的驱动向导所产生的驱动程序骨架 。其用户模式下的接口为 w2k_spy.sys,w2k_spy.sys 采用 Win32 的设备 I/O 控制( IOCTL ),在第三章中曾简要的谈过 IOCTL。Spy Device 定义了一个名为 Devicew2k_spy 的设备和一个符号链接 DosDevicesw2k_spy,定义符号链接是为了能在用户模式下访问该设备 。非常可笑的是符号链接的名字空间居然是 DosDevice,而在这儿,我们使用的可不是一个 DOS 设备驱动 。这就像历史上有名的 root,原本是叫做石头的 J。安装好符号链接后,驱动程序就可以被用户模式下的任何模块打开了,方法是:使用 Win32 API 函数 CreateFile(),路径为 .w2k_spy。字符串 . 是通用转义符,表示本地设备 。例如,.C : 指向本地硬盘上的 C :分区 。从 SDK 的文档中可了解 CreateFile() 的更多细节 。

推荐阅读