分享
 
 
 

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

王朝system·作者佚名  2006-11-24
窄屏简体版  字體: |||超大  

第四章 探索 Windows 2000 的内存管理机制

翻译: Kendiv( [url=http://www.pccode.net].net"fcczj@263.net )

更新: Sunday, February 17, 2005

声明:转载请注明出处,并保证文章的完整性,本人保留译文的所有权利。

IOCTL 函数 SPY_IO_PDE_ARRAY

SPY_IO_PDE_ARRAY 是另一个普通的函数,它只是简单的把整个页目录(开始于地址 0xC0300000 )复制到调用者提供的输出缓冲区中。该缓冲区采用 列表 4-21 所示的 SPY_PDE_ARRAY 结构。你可能已猜到,该结构的大小正好是 4KB ,它由 1,024 个 32 位的 PDE 组成。 X86_PE 结构将在这里使用, X86_PE 结构代表一个一般化的页项( page entry ),可在 列表 4-3 中找到该结构的定义,常量 X86_PAGES_4M 定义在 列表 4-5 。 SPY_PDE_ARRAY 的结构体成员总是页目录项( PDE ), X86_PE 结构可以是 X86_PDE_4M 类型,也可以是 X86_PDE_4KB 类型,这取决于 PDE 的 PS 位的取值。

在无法保证源数据页存在于物理内存时,就开始复制内存中的数据通常并不是一个好主意。不过,页目录是少数列外中的一个。在当前任务处于运行状态时,它的页目录总是存在于物理内存中。它不会被置换到页面文件中,除非另一个任务被置换进来。这就是为什么 CPU 的页目录基地址寄存器( PDBR )没有 P ( present )位的原因, PDE 和 PTE 也类似。请参考 列表 4-3 中的 X86_PDBR 结构的定义,以验证这一点。

typedef struct _SPY_PDE_ARRAY

{

X86_PE apde [X86_PAGES_4M];

}

SPY_PDE_ARRAY, *PSPY_PDE_ARRAY, **PPSPY_PDE_ARRAY;

#define SPY_PDE_ARRAY_ sizeof (SPY_PDE_ARRAY)

列表 4-21. SPY_PDE_ARRY 结构的定义

IOCTL 函数 SPY_IO_PAGE_ENTRY

如果你对给定线性地址的 page entry 感兴趣的话,这个函数就是一个很好的选择。 列表 4-22 给出了 SpyMemoryPageEntry() 的内部细节,该函数就是用来处理 SPY_IO_PAGE_ENTRY 请求的。该函数返回的 SPY_PAGE_ENTRY 结构本质上是一个 X86_PE page entry (定义于 列表 4-3 ),不过这里增加了两个新成员(为了使用方便): dSize 和 fPresent 。其中 dSize 成员用于说明页的大小(以字节为单位),其值不是 X86_PAGE_4KB ( 4,096 字节)就是 X86_PAGE_4MB ( 4,194,304 字节); fPresent 成员用来说明页是否存在于物理内存中。这个标志必须和 SpyMemoryPageEntry() 自身的返回值进行对比,即使 fPresent 为 FALSE ,函数自身的返回值也可为 TRUE 。此时,提供的线性地址时有效的,但它指向的数据页已被置换到了页面文件中。这种情况可通过设置 page entry 的第 10 位(即 列表 4-22 中出现的 PageFile )来表示。当 P 位(该位属于 X86_PNPE 结构)被置 0 时, PageFile 就会被设置。请参考本章稍早讨论过的 X86_PNPE 结构的细节。 X86_PNPE 结构代表一个 page-not-persent entry ,该结构定义于 列表 4-3 。

SpyMemoryPageEntry() 首先假定目标页是 4MB 页,然后,从系统的 PDE 数组(此数组起始于 0xC0300000 )中复制指定线性地址的 PDE 到 SPY_PAGE_ENTRY 结构体的 pe 成员。如果 P 位不为 0 ,则肯定存在下一级的页或页表,所以接下来检查 PS 位以确定页面大小。如果 PS 位不为 0 ,则表示此 PDE 指向一个 4MB 数据页,工作到此就可结束了 ------SpyMemoryPageEntry() 返回 TRUE ,并且 SPY_PAGE_ENTRY 结构体的 fPresent 成员也同时被设为 TRUE 。如果 PS 位为 0 ,则 PDE 指向的是一个 PTE ,所以代码从起始于 0xC0000000 的数组中提取该 PTE ,并检查它的 P 位。如果不为 0 ,则包含指定线性地址的 4KB 页存在于物理内存中,此时, SpyMemoryPageEntry() 和 fPresent 都会报告 TRUE 。否则,找到的必定是一个 page-not-present entry ,因此 SpyMemoryPageEntry() 返回 TRUE ,不过仅当 PageFile 位不为 0 时, fPresent 成员才会被设为 FALSE 。

typedef struct _SPY_PAGE_ENTRY

{

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;

#define SPY_MEMORY_BLOCK_ sizeof (SPY_MEMORY_BLOCK)

// -----------------------------------------------------------------

NTSTATUS SpyInputMemory (PSPY_MEMORY_BLOCK psmb,

PVOID pInput,

DWORD dInput)

{

return SpyInputBinary (psmb, SPY_MEMORY_BLOCK_, pInput, dInput);

}

// -----------------------------------------------------------------

NTSTATUS SpyOutputMemory (PSPY_MEMORY_BLOCK psmb,

PVOID pOutput,

DWORD dOutput,

PDWORD pdInfo)

{

NTSTATUS ns = STATUS_BUFFER_TOO_SMALL;

if (*pdInfo = SpyMemoryReadBlock (psmb, pOutput, dOutput))

{

ns = STATUS_SUCCESS;

}

return ns;

}

列表 4-23. 处理内存块

DWORD SpyMemoryReadBlock (PSPY_MEMORY_BLOCK psmb,

PSPY_MEMORY_DATA psmd,

DWORD dSize)

{

DWORD i;

DWORD n = SPY_MEMORY_DATA__ (psmb->dBytes);

if (dSize >= n)

{

psmd->smb = *psmb;

for (i = 0; i < psmb->dBytes; i++)

{

psmd->awData [i] =

(SpyMemoryTestAddress (psmb->pbAddress + i)

? SPY_MEMORY_DATA_VALUE (psmb->pbAddress [i], TRUE)

: SPY_MEMORY_DATA_VALUE (0, FALSE));

}

}

else

{

if (dSize >= SPY_MEMORY_DATA_)

{

psmd->smb.pbAddress = NULL;

psmd->smb.dBytes = 0;

}

n = 0;

}

return n;

}

// -----------------------------------------------------------------

BOOL SpyMemoryTestAddress (PVOID pVirtual)

{

return SpyMemoryPageEntry (pVirtual, NULL);

}

// -----------------------------------------------------------------

BOOL SpyMemoryTestBlock (PVOID pVirtual,

DWORD dBytes)

{

PBYTE pbData;

DWORD dData;

BOOL fOk = TRUE;

if (dBytes)

{

pbData = (PBYTE) ((DWORD_PTR) pVirtual & X86_PAGE_MASK);

dData = (((dBytes + X86_OFFSET_4K (pVirtual) - 1)

/ PAGE_SIZE) + 1) * PAGE_SIZE;

do {

fOk = SpyMemoryTestAddress (pbData);

pbData += PAGE_SIZE;

dData -= PAGE_SIZE;

}

while (fOk && dData);

}

return fOk;

}

列表 4-24. 复制内存块中的数据

SpyMemoryTestAddress() 用于测试数据的有效性, SpyMemoryReadBlock() 针对要读取的每个字节都会调用 SpyMemoryTestAddress() 。 SpyMemoryTestAddress() 在 列表 4-24 的下半部分给出,该函数只是简单的调用 SpyMemoryPageEntry() ,不过传入的第二个参数为 NULL 。 SpyMemoryPageEntry() 在讨论 SPY_IO_PAGE_ENTRY 时已经介绍过( 列表 4-22 )。将其 PSPY_PAGE_ENTRY 指针参数设为 NULL ,意味着调用者不关心指定线性地址对应的 page entry ,因此,如果线性地址有效,函数将返回 TRUE 。在 SpyMemoryPageEntry() 的上下文中,仅当一个线性地址对应的数据页存在于物理内存中,或者位于页面文件中,该地址才是有效的。注意,这种行为与 ntoskrnl.exe 中的 API 函数 MmIsAddressValid() 并不一致,当指定的页不存在于物理内存中时, MmIsAddressValid() 总是返回 FALSE ,即使这个有效的数据据页位于页面文件中也会如此。 列表 4-24 中的另一个函数 SpyMemoryTestBlock() 是 SpyMemoryTestAddress() 的增强版。它可测试一个内存区域的有效性,它每次可测试指定块中的 4,096 个字节,直到测试完区域中的所有页为止。

#define SPY_MEMORY_DATA_N(_n) \

struct _SPY_MEMORY_DATA_##_n \

{ \

SPY_MEMORY_BLOCK smb; \

WORD awData [_n]; \

}

typedef SPY_MEMORY_DATA_N (0)

SPY_MEMORY_DATA, *PSPY_MEMORY_DATA, **PPSPY_MEMORY_DATA;

#define SPY_MEMORY_DATA_ sizeof (SPY_MEMORY_DATA)

#define SPY_MEMORY_DATA__(_n) (SPY_MEMORY_DATA_ + ((_n) * WORD_))

#define SPY_MEMORY_DATA_BYTE 0x00FF

#define SPY_MEMORY_DATA_VALID 0x0100

#define SPY_MEMORY_DATA_VALUE(_b,_v) \

((WORD) (((_b) & SPY_MEMORY_DATA_BYTE ) | \

((_v) ? SPY_MEMORY_DATA_VALID : 0)))

列表 4-25.

[1] [2] 下一页

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有