将虚拟地址转换为物理地址
大多数调试器命令使用虚拟地址(而不是物理地址)作为其输入和输出。 但是,有时拥有物理地址可能很有用。
可通过两种方式将虚拟地址转换为物理地址:使用 !vtop 扩展和 !pte 扩展。
有关 Windows 中虚拟地址的概述,请参阅 虚拟地址空间。
使用 !vtop 进行地址转换
假设要调试运行 MyApp.exe 进程的目标计算机,并且想要调查虚拟地址0x0012F980。 下面是用于 !vtop 扩展以确定相应物理地址的过程。
使用 !vtop 将虚拟地址转换为物理地址
请确保以十六进制格式工作。 如有必要,请使用 N 16 命令设置当前基数。
确定地址的 字节索引 。 此数字等于虚拟地址的最低 12 位。 因此,虚拟地址0x0012F980的字节索引为0x980。
使用 !process 扩展确定地址的目录基:
kd> !process 0 0 **** NT ACTIVE PROCESS DUMP **** .... PROCESS ff779190 SessionId: 0 Cid: 04fc Peb: 7ffdf000 ParentCid: 0394 DirBase: 098fd000 ObjectTable: e1646b30 TableSize: 8. Image: MyApp.exe
确定目录基 页的页码 。 这只是没有三个尾随十六进制零的目录基。 在此示例中,目录基为0x098FD000,因此页帧编号0x098FD。
使用 !vtop 扩展。 此扩展的第一个参数应该是页帧编号。 !vtop 的第二个参数应该是有问题的虚拟地址:
kd> !vtop 98fd 12f980 Pdi 0 Pti 12f 0012f980 09de9000 pfn(09de9)
最后一行中显示的第二个数字是物理页开头的物理地址。
将字节索引添加到页面开头的地址:0x09DE9000 + 0x980 = 0x09DE9980。 这是所需的物理地址。
可以通过在每个地址显示内存来验证此计算是否正确完成。 !d\* 扩展在指定的物理地址处显示内存:
kd> !dc 9de9980
# 9de9980 6d206e49 726f6d65 00120079 0012f9f4 In memory.......
# 9de9990 0012f9f8 77e57119 77e8e618 ffffffff .....q.w...w....
# 9de99a0 77e727e0 77f6f13e 77f747e0 ffffffff .'.w>..w.G.w....
# 9de99b0 .....
d* (Display Memory) 命令使用虚拟地址作为其参数:
kd> dc 12f980
0012f980 6d206e49 726f6d65 00120079 0012f9f4 In memory.......
0012f990 0012f9f8 77e57119 77e8e618 ffffffff .....q.w...w....
0012f9a0 77e727e0 77f6f13e 77f747e0 ffffffff .'.w>..w.G.w....
0012f9b0 .....
由于结果相同,这表示物理地址0x09DE9980确实对应于虚拟地址0x0012F980。
使用 !pte 进行地址转换
同样,假设你正在调查属于 MyApp.exe 进程的虚拟地址0x0012F980。 下面是用于 !pte 扩展以确定相应物理地址的过程:
使用 !pte 将虚拟地址转换为物理地址
请确保以十六进制格式工作。 如有必要,请使用 N 16 命令设置当前基数。
确定地址的 字节索引 。 此数字等于虚拟地址的最低 12 位。 因此,虚拟地址0x0012F980的字节索引为0x980。
将 进程上下文 设置为所需的进程:
kd> !process 0 0 **** NT ACTIVE PROCESS DUMP **** .... PROCESS ff779190 SessionId: 0 Cid: 04fc Peb: 7ffdf000 ParentCid: 0394 DirBase: 098fd000 ObjectTable: e1646b30 TableSize: 8. Image: MyApp.exe kd> .process /p ff779190 Implicit process is now ff779190 .cache forcedecodeuser done
将 !pte 扩展与虚拟地址一起使用作为其参数。 这在两列中显示信息。 左列描述此地址 (PDE) 的页面目录条目;右列描述其页表条目 (PTE) :
kd> !pte 12f980 VA 0012f980 PDE at C0300000 PTE at C00004BC contains 0BA58067 contains 09DE9067 pfn ba58 ---DA--UWV pfn 9de9 ---DA--UWV
查看右列的最后一行。 此时会显示表示法“pfn 9de9”。 0x9DE9编号是此 PTE) (PFN 的页码 。 将页帧编号乘以0x1000 (例如,将页码左移 12 位) 。 结果(0x09DE9000)是页面开头的物理地址。
将字节索引添加到页面开头的地址:0x09DE9000 + 0x980 = 0x09DE9980。 这是所需的物理地址。
这与之前方法获取的结果相同。
手动转换地址
尽管 !ptov 和 pte 扩展提供了将虚拟地址转换为物理地址的最快方法,但也可以手动进行此转换。 此过程的说明将阐明虚拟内存体系结构的一些细节。
内存结构的大小因处理器和硬件配置而异。 此示例取自未启用物理地址扩展 (PAE) 的 x86 系统。
再次使用 0x0012F980 作为虚拟地址,首先需要手动或使用 .formats (显示数字格式) 命令将其转换为二进制地址:
kd> .formats 12f980
Evaluate expression:
Hex: 0012f980
Decimal: 1243520
Octal: 00004574600
Binary: 00000000 00010010 11111001 10000000
Chars: ....
Time: Thu Jan 15 01:25:20 1970
Float: low 1.74254e-039 high 0
Double: 6.14381e-318
此虚拟地址是三个字段的组合。 位 0 到 11 是字节索引。 位 12 到 21 是页表索引。 位 22 到 31 是页面目录索引。 分隔字段后,你有:
0x0012F980 = 0y 00000000 00 010010 1111 1001 10000000
这会公开虚拟地址的三个部分:
页目录索引 = 0y00000000000 = 0x0
页表索引 = 0y0100101111 = 0x12F
字节索引 = 0y100110000000 = 0x980
然后,系统需要另外三条信息。
每个 PTE 的大小。 这是非 PAE x86 系统上的 4 个字节。
页面的大小。 这是0x1000个字节。
PTE_BASE虚拟地址。 在非 PAE 系统上,这是0xC0000000。
使用此数据,可以计算 PTE 本身的地址:
PTE address = PTE_BASE
+ (page directory index) * PAGE_SIZE
+ (page table index) * sizeof(MMPTE)
= 0xc0000000
+ 0x0 * 0x1000
+ 0x12F * 4
= 0xC00004BC
这是 PTE 的地址。 PTE 是 32 位 DWORD。 检查其内容:
kd> dd 0xc00004bc L1
c00004bc 09de9067
此 PTE 的值0x09DE9067。 它由两个字段组成:
PTE 的低 12 位是 状态标志。 在这种情况下,这些标志等于 0x067 -- 或二进制 0y0000011100111。 有关状态标志的说明,请参阅 !pte 参考页。
PTE 的高 20 位等于 PTE (PFN) 页帧数 。 在这种情况下,PFN 是0x09DE9。
物理页上的第一个物理地址是 PFN 乘以0x1000 (移左 12 位) 。 字节索引是此页上的偏移量。 因此,要查找的物理地址为 0x09DE9000 + 0x980 = 0x09DE9980。 这与之前的方法获取的结果相同。