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

第四章 探索 Windows 2000 的内存管理机制
翻译: Kendiv( fcczj@263.net)
更新: Tuesday, February 22, 2005
声明:转载请注明出处,并保证文章的完整性,本人保留译文的所有权利 。
请求式分页动作
在讨论 Spy 设备的 SPY_IO_MEMORY_DATA 函数时,我提到过该函数可以读取已被置换到页面文件中的内存页 。要证明这一点,首先,必须让系统处于低内存状态,以强迫它将不马上使用的数据置换到页面文件中 。我喜欢采用的方法如下:
1. 使用 PrintKey,将 Windows 2000 的桌面复制到剪切板中 。
2. 将该图片粘贴到一个图形处理程序中 。
3. 将该图片的尺寸放到最大 。
现在,执行命令: w2k_memd #16 0xC02800000 0xA0000000 0xA0001000 0xA0002000 0xC0280000,察看它在屏幕上的输出 。你可能会惊讶 。在触及某些 PTE 所引用的页之前,它会获取这些 PTE 的快照 。在地址 0xC0280000 处发现的四个 PTE 与地址范围: 0xA0000000---0xA0003FFF 相关,这是内核模块 win32k.sys 的一部分 。如 示列 4-11 所示,该地址范围已经被置换出去了,因为在地址 0xC0280000 的四个 DWord 都是偶数,这意味着它们的最低位(即 PTE 的 P 位)为零,这表示没有存在于物理内存中的页 。接下来的三块 16 进制 Dump 信息属于 0xA0000000 、 0xA0001000 、 0xA0002000,w2k_mem 可以毫无问题的访问这些页(系统会根据请求将它们再次换入内存) 。
示列 4-11 观察 PTE 的状态变化
在开始下一节之前,请再次研究一下 示列 4-11 中的第一栏 。位于地址 0xC0280000 的四个 PTE 看上去都很像 。但事实上,它们仅有最低的三个位不同 。如果你检查所有位于页面文件中的 PNPE,你会发现它们的第 10 位都为 1。这就是为什么我在 列表 4-3 中,将该位的名字取为 PageFile。如果该位为 1,除 P 位外的所有位都将用来表示该页在页面文件中的位置 。
更多的命令选项
示列 4-1 给出的某些命令选项还没有解释过 。例如,系统状态选项:o 、c 、g 、i 和b,我会在本章的最后一节介绍它们,在那儿我们将发现几个 Windows 2000 内存系统的秘密 。
Spy 设备的接口
现在你已经知道如何使用 w2k_mem 了,该是介绍它是如何工作的了 。现在来看看这个程序是如何与 w2k_spy.sys 中的 Spy 设备通讯的 。
【8 《Undocumented Windows 2000 Secrets》翻译 --- 第四章】回顾 ----- 设备 I/O 控制( Device I/O Control )
IOCTL 通讯的内核模式端已经由 列表 4-6 和 列表 4-7 给出了 。Spy 设备只是简单的等待 IRP 并处理其中的某些 IRP,尤其是标识为 IPR_MJ_DEVICE_CONTROL,其中的一些请求在用户模式下是被禁止的 。调用 Win32 API 函数 DeviceIoControl(),列表 4-27 给出了该函数的原型 。可能你已经熟悉了 dwIocontrolCode 、 lpInBuffer 、 nInBufferSize 、 lpOutBuffer 、 nOutBufferSize 和 lpBytesReturned 参数 。事实上,它们一一对应于: SpyDispatcher() 的 dCode 、 pInput 、 dInput 、 pOutput 、 dOutput 和 pdInfo 参数,SpyDispatcher 定义于 列表 4-7。剩下的参数很快就会解释 。hDevice 是 Spy 设备的句柄,lpOverlapped (可选的)指向一个 OVERLAPPED 结构,异步 IOCTL 需要该结构 。我们不需要发送异步请求,所以该参数总是 NULL。
列表 4-28 列出了所有执行基本 IOCTL 操作的外包函数 。最基本的一个是: IoControl(),该函数调用 DeviceControl() 并测试返回的输出数据的大小 。因为 w2k_mem.exe 精确的提供了输出缓冲区的大小,所以,输出的字节数应该总是等于缓冲区的大小 。ReadBinary() 是 IoControl() 的简单版本,它不需要输入数据 。ReadCPUInfo() 、 ReadSegment() 和 ReadPhysical() 专用于 Spy 函数 SPY_IO_CPU_INFO 、 SPY_IO_SEGEMNT 和 SPY_IO_PHYSICAL,因为它们会经常被用到 。将它们封装为 C 函数,可读性会更好些 。

推荐阅读