《Undocumented Windows 2000 Secrets》翻译 --- 第四章(9)
第四章 探索Windows 2000的内存管理机制
翻译:Kendiv( fcczj@263.net )
更新:
声明:转载请注明出处,并保证文章的完整性,本人保留译文的所有权利。
Windows 2000的分段和描述符
w2k_mem.exe的另一个很棒的选项是+e,该选项将显示和说明处理器的段寄存器和描述表的内容。示列4-13给出了其典型输出。CS、DS和ES段寄存器的内容非常清晰的证明了Windows 2000为每个进程提供了平坦的4GB地址空间:起始于0x00000000,终止于0xFFFFFFFF。示列4-13中最右边的标志符用来表示段的类型,该段的类型由它的描述符的Type成员给出。代码和数据段的Type属性可分别符号化为“cra”和“ewa”。省略号“-”意味着相应的属性没有设置。一个任务状态段(Task State Segment,TSS)仅能有“a”(可用)和“b”(忙)两种属性。表4-5给出了所有可用的属性。示列4-13展示了Windows 2000的CS段的不一致性,CS段允许执行和读取,而DS、ES、FS和SS段的属性则是可扩展和读/写访问。另一个不明显但十分重要的细节是CS、FS和SS段的DPL在用户模式和内核模式并不相同。DPL是描述符特权级别(Descriptor Privilege Level)。对于代码段(CS),仅当调用者位于其DPL指定的特权级时才能调用该段中的代码(参考Intel 1999c, pp. 4-8f)。在用户模式,CS段的DPL为3;在内核模式,其DPL为0。对于数据段(DS),其DPL是最低的特权级,在用户模式下,所有特权级都可访问它,而在内核模式下,仅允许特权0访问。
示列4-13. 显示CPU信息
IDT和GDT寄存器的内容显示了GDT的范围是:0x8003F000 --- 0x8003F3FF,紧随其后的就是IDT,其地址范围是:0x8003F400 --- 0x8003FBFF。由于每个描述符占用64位,故GDT和IDT分别包含128和256个项。注意,GDT可容纳8,192个项,但Windows 2000仅使用了其中的一小部分。
表4-5 代码和数据段的Type属性
|
段 |
属 性 |
描 述 |
|
CODE |
c |
使段一致(低特权的代码可能进入) |
|
CODE |
r |
允许读访问(和仅执行访问相斥) |
|
CODE |
a |
段可以访问 |
|
DATA |
e |
向下扩展段(堆栈段的典型属性) |
|
DATA |
w |
允许写访问(和仅读取访问相斥) |
|
DATA |
a |
段可以访问 |
|
TSS32 |
a |
任务状态段可用 |
|
TSS32 |
b |
任务状态段繁忙 |
W2k_mem.exe还提供了两个很有特色的选项----+g和+i,这两个选项可显示GDT和IDT的更多细节。示列4-14示范了+g选项的输出。它很类似于示列4-13中的“kernel-model segment:”一节,但列出了在内核模式下所有可用的段选择子(selector),而不仅仅是存储在段寄存器中的那些。W2k_mem.exe通过遍历整个GDT来获取所有的段选择子,可通过IOCTL函数SPY_IO_SEGMENT来指示Spy设备查询段信息。仅显示有效的选择子。比较示列4-13和4-14中的GDT选择子将十分有趣,GDT的选择子定义于ntddk.h中,汇总在表4-6。显然,它们与w2k_mem.exe的输出是一致的。
示列4-14. 显示GDT描述符
表4-6. 定义于ntddk.h中的GDT选择子(selector)
|
符 号 |
值 |
注 释 |
|
KGDT_NULL |
0x0000 |
空的段选择子(无效) |
|
KGDT_R0_CODE |
0x0008 |
内核模式的CS寄存器 |
|
KGDT_R0_DATA |
0x0010 |
内核模式的SS寄存器 |
|
KGDT_R3_CODE |
0x0018 |
用户模式的CS寄存器 |
|
KGDT_R3_DATA |
0x0020 |
用户模式的DS、ES和SS寄存器,内核模式的DS和ES寄存器 |
|
KGDT_TSS |
0x0028 |
位于用户和内核的任务状态段 |
|
KGDT_R0_PCR |
0x0030 |
内核模式的FS寄存器(处理器控制区域) |
|
KGDT_R3_TEB |
0x0038 |
用户模式的FS寄存器(线程环境块) |
|
KGDT_VDM_TILE |
0x0040 |
基地址0x00000400,限制0x0000FFFF(DOS虚拟机) |
|
KGDT_LDT |
0x0048 |
本地描述符表 |
|
KGDT_DF_TSS |
0x0050 |
Ntoskrnl.exe 变量 KiDoubleFaultTSS |
|
KGDT_NMI_TSS |
0x0058 |
Ntoskrnl.exe 变量 KiNMITSS |
示列4-14中的选择子(selector)没有在表4-6中列出,其中的某些选择子可以通过查找熟悉的基地址或其内存内容来确认它们。使用内核调试器可查找其中某些选择子的基地址对应的符号。表4-7给出了我已经确认的选择子。
W2k_mem.exe的+i选项可转储IDT中的门描述符(Gate Descriptor)。示列4-15给出了IDT的门描述符的部分内容,Intel仅定义了IDT中的前20个门描述符(Intel 1999c, pp. 5-6)。IDT中的中断0x14到0x1F由Intel保留;剩余的0x20到0xFF由操作系统使用。
在表4-8中,我给出了所有可确认的特殊的中断、陷阱和任务门。大多数用户自定义的中断都指向哑元例程---KiUnexpectedinterruptnNNN(),在前面我们已经解释过它。对于某些中断处理例程的地址,内核调试器也无法解析其地址对应的符号。
表4-7. 更多的GDT选择子(selector)
|
值 |
基地址 |
描 述 |
|
0x0078 |
0x80400000 |
Ntoskrnl.exe的代码段 |
|
0x0080 |
0x80400000 |
Ntoskrnl.exe的数据段 |
|
0x00A0 |
0x814985A8 |
TSS(EIP成员指向HalpMcaExceptionHandlerWrapper) |
|
0x00E0 |
0xF0430000 |
ROM BIOS代码段 |
|
0x00F0 |
0x8042DCE8 |
Ntoskrnl.exe函数KiI386CallAbios |
|
0x0100 |
0xF0440000 |
ROM BIOS数据段 |
|
0x0108 |
0xF0440000 |
ROM BIOS数据段 |
|
0x0110 |
0xF0440000 |
ROM BIOS数据段 |
示列4-15. 显示IDT门描述符
表4-8. Windows 2000 中断、陷阱和任务门
|
INT |
Intel定义的描述符 |
拥有者 |
处理例程/TSS |
|
0x00 |
整除错误(DE) |
ntoskrnl.exe |
KiTrap00 |
|
0x01 |
调试(DB) |
ntoskrnl.exe |
KiTrap01 |
|
0x02 |
NMI中断 |
ntoskrnl.exe |
KiNMITSS |
|
0x03 |
断点(BP) |
ntoskrnl.exe |
KiTrap03 |
|
0x04 |
溢出(OF) |
ntoskrnl.exe |
KiTrap04 |
|
0x05 |
越界(BR) |
ntoskrnl.exe |
KiTrap05 |
|
0x06 |
未定义的操作码(UD) |
ntoskrnl.exe |
KiTrap06 |
|
0x07 |
没有数学协处理器(NM) |
ntoskrnl.exe |
KiTrap07 |
|
0x08 |
Double Fault(DF) |
ntoskrnl.exe |
KiDouble |
|
0x09 |
协处理器段溢出 |
ntoskrnl.exe |
KiTrap09 |
|
0x0A |
无效的TSS(TS) |
ntoskrnl.exe |
KiTrap0A |
|
0x0B |
段不存在(NP) |
ntoskrnl.exe |
KiTrap0B |
|
0x0C |
堆栈段故障(SS) |
ntoskrnl.exe |
KiTrap0C |
|
0x0D |
常规保护(GP) |
ntoskrnl.exe |
KiTrap0D |
|
0x0E |
页故障(PF) |
ntoskrnl.exe |
KiTrap0E |
|
0x0F |
