6 《Undocumented Windows 2000 Secrets》翻译 --- 第四章( 二 )


{
X86_PE pe;
DWord dSize;
BOOL fPresent;
}
SPY_PAGE_ENTRY, *PSPY_PAGE_ENTRY, **PPSPY_PAGE_ENTRY;
#define SPY_PAGE_ENTRY_ sizeof (SPY_PAGE_ENTRY)
// -----------------------------------------------------------------
BOOL SpyMemoryPageEntry (PVOID pVirtual,
PSPY_PAGE_ENTRY pspe)
{
SPY_PAGE_ENTRY spe;
BOOL fOk = FALSE;
spe.pe = X86_PDE_ARRAY [X86_PDI (pVirtual)];
spe.dSize = X86_PAGE_4M;
spe.fPresent = FALSE;
if (spe.pe.pde4M.P)
{
if (spe.pe.pde4M.PS)
{
fOk = spe.fPresent = TRUE;
}
else
{
spe.pe = X86_PTE_ARRAY [X86_PAGE (pVirtual)];
spe.dSize = X86_PAGE_4K;
if (spe.pe.pte4K.P)
{
fOk = spe.fPresent = TRUE;
}
else
{
fOk = (spe.pe.pnpe.PageFile != 0);
}
}
}
if (pspe != NULL) *pspe = spe;
return fOk;
}
列表 4-22. 查询 PDE 和 PTE
需要注意的是,SpyMemoryPageEntry() 不能识别被置换出物理内存的 4MB 页 。如果 PDE 指向的 4MB 页并不存在,将无法判断给定的线性地址是否有效的,以及该页是否还保存在当前页面文件中 。4MB 页仅用于内核内存范围: 0x80000000----0x9FFFFFFF。不过我从来没见过这样的一个页被置换出去,即使物理内存极端少的时候也没有过,因此我不需要检查任何与此相关的 page-not-present entrIEs。
IOCTL 函数 SPY_IO_MEMORY_DATA
SPY_IO_MEMORY_DATA 函数是重量级函数中的一个,因为它可以复制任意数量的内存数据到调用者提供的缓冲区中 。正如你可能还记得的那样,用户模式下的应用程序很容易传入一个无效的地址 。因此,该函数在触及源地址之前,会非常谨慎的检验这些地址的有效性 。记住,蓝屏可以潜伏在内核模式的任何地方 。
调用程序通过传入一个 SPY_MEMORY_BLOCK 结构来请求一个内存块中的数据,在 列表 4-23 的顶部给出了该结构体的定义,该结构体会指定内存块的地址和大小 。为了方便,此处的地址被定义为一个 union,以允许将其解释为一个字节类型的数组( PBYTE pbAddress )或解释为一个无类型的指针( PVOID pAddress ) 。列表 4-23 中的 SpyInputMemory() 函数将从 IOCTL 的输入缓冲区中复制该结构 。其搭档函数 SpyOutputMemory() (在 列表 4-23 的末尾处)只是 SpyMemoryReadBlock() 的一个外包而已,列表 4-24 给出了 SpyMemoryReadBlock() 函数 。SpyOutputMemory() 的主要职责是在 SpyMemoryReadBlock() 读取数据后,返回适当的 NTSTATUS 值 。
SpyMemoryReadBlock() 通过一个 SPY_MEMORY_DATA 结构返回它读到的内存数据 。该结构定义于 列表 4-25。我选择了一中不同的定义方式,因为 SPY_MEMORY_DATA 是一个针对变量大小的数据类型 。基本上,它包含一个名为 smb 的 SPY_MEMORY_BLOCK 结构,随后是一个 WORD 类型的数组,名为 awData[]。该数组的长度由 smb 的 dBytes 成员给出 。为了允许方便的按预定大小定义 SPY_MEMORY_DATA 的全局或局部实体,该结构的定义采用了一个宏 ----SPY_MEMORY_DATA_N()。该宏的唯一参数用于指定 awData[] 数组的大小 。实际的结构体定义在宏定义之后,它提供的结构体中包含一个长度为 0 的 awData[] 数组 。SPY_MEMORY_DATA__() 宏首先计算 SPY_MEMORY_DATA 结构的全部大小,然后按这一大小分配结构中的数组,剩下的定义允许将 WORD 型的数据加入数组或从数组中取出 。显然,每个 WORD 的低半位包含内存数据的字节数,高半位作为标志位 。现在,仅有第 8 位有意义,用于表示位于 0—7 位的内存字节数是否有效 。
typedef struct _SPY_MEMORY_BLOCK
{
union
{
PBYTE pbAddress;
PVOID pAddress;
};
DWORD dBytes;
}
SPY_MEMORY_BLOCK, *PSPY_MEMORY_BLOCK, **PPSPY_MEMORY_BLOCK;

推荐阅读