转自:http://user.qzone.qq.com/31731705/blog/1323426728
前文是理论分析,在此: PAE下的虚拟内存映射 分析 (http://user.qzone.qq.com/31731705/blog/1323414733 ),理论需要结合实际,现在来实际体验一下。
随便选个进程,就当前进程吧,
0: kd> !process
PROCESS 8da74d40 SessionId: 1 Cid: 1f6c Peb: 7ffdf000 ParentCid: 19e4
DirBase: deb6d0c0 ObjectTable: cce0d8b0 HandleCount: 216.
Image: windbg.exe
VadRoot b11b89c8 Vads 106 Clone 0 Private 3601. Modified 0. Locked 0.
......
是Windbg进程,根据 上篇 中的介绍,0xC0603000的地方,指向了进程的页目录,而DirBase是deb6d0c0,是物理地址,也是指向PDPE的地址。它们的内容应该相同。
0: kd> dd c0603000
c0603000 b21f0863 00000000 15ff1863 00000000
c0603010 55df2863 00000000 183f3863 00000000
......
开始处的4个值应该是4个PDE的物理地址,对应的VA分别是0xC0600000, 0xC0601000, 0xC0602000, 0xC0603000,让我们使用DirBase验证,
0: kd> !dd deb6d0c0
#deb6d0c0 b21f0801 00000000 15ff1801 00000000
#deb6d0d0 55df2801 00000000 183f3801 00000000
......
乍一看,里面的值不一样, ,可以推断0xC0603000并没被映射到物理地址deb6d0c0,而是2块独立的内存。但是,地址处的值其实是一样的(因为最低的12Bit是属性值)。
进一步看看,
0: kd> !vtop deb6d0c0 c0600000
X86VtoP: Virt c0600000, pagedir deb6d0c0
X86VtoP: PAE PDPE deb6d0d8 - 00000000183f3801
X86VtoP: PAE PDE 183f3018 - 00000000183f3863
X86VtoP: PAE PTE 183f3000 - 00000000b21f0863
X86VtoP: PAE Mapped phys b21f0000
Virtual address c0600000 translates to physical address b21f0000 .
0: kd> !vtop deb6d0c0 c0601000
X86VtoP: Virt c0601000, pagedir deb6d0c0
X86VtoP: PAE PDPE deb6d0d8 - 00000000183f3801
X86VtoP: PAE PDE 183f3018 - 00000000183f3863
X86VtoP: PAE PTE 183f3008 - 0000000015ff1863
X86VtoP: PAE Mapped phys 15ff1000
Virtual address c0601000 translates to physical address 15ff1000 .
0: kd> !vtop deb6d0c0 c0602000
X86VtoP: Virt c0602000, pagedir deb6d0c0
X86VtoP: PAE PDPE deb6d0d8 - 00000000183f3801
X86VtoP: PAE PDE 183f3018 - 00000000183f3863
X86VtoP: PAE PTE 183f3010 - 0000000055df2863
X86VtoP: PAE Mapped phys 55df2000
Virtual address c0602000 translates to physical address 55df2000 .
0: kd> !vtop deb6d0c0 c0603000
X86VtoP: Virt c0603000, pagedir deb6d0c0
X86VtoP: PAE PDPE deb6d0d8 - 00000000183f3801
X86VtoP: PAE PDE 183f3018 - 00000000183f3863
X86VtoP: PAE PTE 183f3018 - 00000000183f3863
X86VtoP: PAE Mapped phys 183f3000
Virtual address c0603000 translates to physical address 183f3000 .
确实验证了前面的分析结果,VA 0xC0603000或者物理地址deb6d0c0处的值,分别对应着4个VA(0xC0600000, 0xC0601000, 0xC0602000, 0xC0603000)的物理地址。如果你够细心,你会发现0xC0603000处的内容指向了它自己(结合 上一篇 中的图),这真的很神奇。这样,任何进程都可以通过线性地址访问页目录指针表,页目录,页表,还有普通的内存页。
回到开始,为什么DirBase的值,不是0xC0603000的物理地址183f3000,而是另外的一块deb6d0c0(对应的虚拟地址VA是0x8856d0c0)。
0: kd> !vtop deb6d0c0 0x8856d0c0
X86VtoP: Virt 8856d0c0, pagedir deb6d0c0
X86VtoP: PAE PDPE deb6d0d0 - 0000000055df2801
X86VtoP: PAE PDE 55df2210 - 00000000dea009e3
X86VtoP: PAE Large page mapped phys deb6d0c0
Virtual address 8856d0c0 translates to physical address deb6d0c0.
是一个Large Page,在PAE下这是1个2M的Page,先Hold住这个问题, 切换到另外一个进程看看,
0: kd> !process 0 0 qq.exe
PROCESS 887a4d40 SessionId: 1 Cid: 029c Peb: 7ffd6000 ParentCid: 17c0
DirBase: dc99dac0 ObjectTable: be517450 HandleCount: 2125.
Image: QQ.exe
0: kd> .process /p /r 887a4d40
Implicit process is now 887a4d40
Loading User Symbols
................................................................
看看QQ进程的情况,
0: kd> !dd dc99dac0
#dc99dac0 acec1801 00000000 a92c2801 00000000
#dc99dad0 9dcc3801 00000000 a95c4801 00000000
#dc99dae0 8a79df20 8495ec20 38eb2801 00000000
......
0: kd> dd c0603000
c0603000 acec1863 00000000 a92c2863 00000000
c0603010 9dcc3863 00000000 a95c4863 00000000
c0603020 b07c5863 00000000 00000000 00000000
......
同样,0xC0603000没有被映射到DirBase: dc99dac0上,但前4项的内容是一致的,是QQ进程的PDE的物理地址,
0: kd> !vtop dc99dac0 c0600000; !vtop dc99dac0 c0601000; !vtop dc99dac0 c0602000; !vtop dc99dac0 c0603000;
X86VtoP: Virt c0600000, pagedir dc99dac0
X86VtoP: PAE PDPE dc99dad8 - 00000000a95c4801
X86VtoP: PAE PDE a95c4018 - 00000000a95c4863
X86VtoP: PAE PTE a95c4000 - 00000000acec1863
X86VtoP: PAE Mapped phys acec1000
Virtual address c0600000 translates to physical address acec1000 .
X86VtoP: Virt c0601000, pagedir dc99dac0
X86VtoP: PAE PDPE dc99dad8 - 00000000a95c4801
X86VtoP: PAE PDE a95c4018 - 00000000a95c4863
X86VtoP: PAE PTE a95c4008 - 00000000a92c2863
X86VtoP: PAE Mapped phys a92c2000
Virtual address c0601000 translates to physical address a92c2000 .
X86VtoP: Virt c0602000, pagedir dc99dac0
X86VtoP: PAE PDPE dc99dad8 - 00000000a95c4801
X86VtoP: PAE PDE a95c4018 - 00000000a95c4863
X86VtoP: PAE PTE a95c4010 - 000000009dcc3863
X86VtoP: PAE Mapped phys 9dcc3000
Virtual address c0602000 translates to physical address 9dcc3000 .
X86VtoP: Virt c0603000, pagedir dc99dac0
X86VtoP: PAE PDPE dc99dad8 - 00000000a95c4801
X86VtoP: PAE PDE a95c4018 - 00000000a95c4863
X86VtoP: PAE PTE a95c4018 - 00000000a95c4863
X86VtoP: PAE Mapped phys a95c4000
Virtual address c0603000 translates to physical address a95c4000 .
转换过程和前面一样,4个PDE分别对应着QQ进程的虚拟地址VA 0xC0600000, 0xC0601000, 0xC0602000, 0xC0603000。
这个实验也证明了另一点,页表是进程相关的,即使0xC0X00000是位于内核态的地址,随着进程的切换也会被映射到不同的物理地址上。(这打破了我以前的一个假设,之前我并没想这么多,而是简单的认为内核只有一个,里面的对象都是共享的,进程无关的,现在看来至少页表不是。 )
再来看看开始的那个问题, 0xC0603000对应的并不是DirBase表示的物理地址,为什么呢?
我并没有确切的答案, 猜想是这样,象鸡和蛋的关系,要将虚拟地址转化成为物理地址,必须要有一个物理地址作为引子,也需要PDE的物理地址,这是必需且没有办法避免的。如何访问到那些PDE呢?通过0xC0603000是行不通的,因为那是虚拟地址,本身就需要转换,并且是代表着当前进程的页表,是进程相关的,所以需要一个进程无关的,系统全局的数据来储存这些数据,这些就是DirBase。内核可以通过这些数据来转化虚拟地址,切换进程,访问各个进程的地址空间。
那么,另一个问题是, DirBase为什么不使用各个进程?中0xC0603000对应的物理地址? 比如以Windbg和QQ为例,DirBase分别是183f3000和a95c4000。
我还是不知道答案, ,推测进程的页表会随着进程的切换而切换,而183f3000和a95c4000这2个物理页有可能被切换出去,这时它其中的值就没有意义了。而现在的值deb6d0c0和dc99dac0,这2个物理页应该是常驻物理内存的那种,这样内核可以在任何时候访问进程的PDE。
看看Windbg进程DirBase: deb6d0c0(对应的虚拟地址VA是0x8856d0c0),当前的进程已经切换成了QQ进程,
0: kd> !vtop dc99dac0 8856d0c0
X86VtoP: Virt 8856d0c0, pagedir dc99dac0
X86VtoP: PAE PDPE dc99dad0 - 000000009dcc3801
X86VtoP: PAE PDE 9dcc3210 - 00000000dea009e3
X86VtoP: PAE Large page mapped phys deb6d0c0
Virtual address 8856d0c0 translates to physical address deb6d0c0 .
可以清楚的看到,即使使用了QQ进程的DirBase,这个虚拟地址对应的内容还是deb6d0c0,没有变化。
再看看QQ进程的DirBase: dc99dac0(对应的虚拟地址是8a79dac0)。
0: kd> !vtop dc99dac0 8a79dac0
X86VtoP: Virt 8a79dac0, pagedir dc99dac0
X86VtoP: PAE PDPE dc99dad0 - 000000009dcc3801
X86VtoP: PAE PDE 9dcc3298 - 00000000dc8009e3
X86VtoP: PAE Large page mapped phys dc99dac0
Virtual address 8a79dac0 translates to physical address dc99dac0 .
使用Windbg进程的DirBase测试一下,
0: kd> !vtop deb6d0c0 8a79dac0
X86VtoP: Virt 8a79dac0, pagedir deb6d0c0
X86VtoP: PAE PDPE deb6d0d0 - 0000000055df2801
X86VtoP: PAE PDE 55df2298 - 00000000dc8009e3
X86VtoP: PAE Large page mapped phys dc99dac0
Virtual address 8a79dac0 translates to physical address dc99dac0 .
可见,这些DirBase的内存页是进程无关,系统共享的,只需要知道这些DirBase,就能够顺藤摸瓜,找到所有进程的PDE,进而确认所有进程的页表。