WinDbg演示IA-32 CPU下的Windows 分页机制下的地址转换过程

 今天在学习《软件调试》的时候,练习虚拟地址转物理地址的时候遇到了一个问题。用windbg本地调试内核功能时,!dd无法使用

用windbg命令dd的时候出现了以下错误:

kd> !dd 0b3c03c0
Physical memory read at b3c03c0 failed
If you know the caching attributes used for the memory,
try specifying [c], [uc] or [wc], as in !dd [c] .
WARNING: Incorrect use of these flags will cause unpredictable
processor corruption. This may immediately (or at any time in
the future until reboot) result in a system hang, incorrect data
being displayed or other strange crashes and corruption.

在网上查询以后初步估计是360的问题,但是不想卸载,所以在VM里边尝试,dd没问题了,但是新的问题出来了,在索引页表地址的时候,得到的页表地址始终为0,后来想想是不是虚拟机的原因,估计什么地方和真是的计算机环境不一样。没办法还是在XP系统中进行,删除360杀毒,DD命令还是出错,再删除360安全卫士,windbg正常了,可以正常使用DD命令了,计算机系统博大精深,什么地方造成的冲突不得而知,希望不久的将来能够知晓。以下是本次试验的过程,写得很详细所以直接转载了。

===========以下为转载=================

我们知道,386开始,IA-32 CPU开始支持Paging。在启用Paging之后,OS将线性地址空间划分为固定大小的Page(通常为4KB或4MB)

本文演示了如何通过WinDbg展示windows paging中的virtual address 向physical address转换过程。 

在现代OS中,涉及到Paging的几个概念如下(以32-bit IA CPU&Microsoft Windows OS为例): 

Page Directory (页目录)

Page Directory 是用来存放Page-Directory EntryPDE)(页目录表项)的线性表。每个页目录占一个4kb的内存页(page)PD中的每个PDE长度为32Bit(其中高20bit为PDE所指向的页表的起始地址,低12bit为该PDE的属性)。因此每个PD中最多包含1024PDE

对于内存页大小为4KB的页目录地址格式:

      其实际上只有高20Bit有用,其低12bit固定为0 

对于内存页大小为4MB的页目录地址格式:

      其实际上只有高10Bit有用,其低12bit固定为0 

其高20(10)为该页目录的起始地址。

Page Table (页表)

Page Table是用来存在页表项(Page Table Entry, PTE)的线性表。同样,每个PT占用一个内存页,每个PTE的长度为32bit(其中高20bit为PT所指向的物理页的起始地址,低12bit为该物理页的属性),故每个PT中最多可以包含1024PTE 

Virtual Address(虚地址)

对于一个32bitvirtual address,其格式如下所示:

 

其中高10bitPage Directory中的index项,中间10BitPage Tableindex项,最低12bit为页内偏移地址。  

virtual addressphysical address转换的过程如下:

Step1:

通过CR3寄存器定位到页目录的起始位置(DirBase)CR3 Regesiter又称为页目录基地址寄存器

Step2:

virtual address的高10bit作为index,在PD中查找相应的PDE.

Step3:

根据PDE中的页表基地址(即PDE的高20bit)定位到Page Table(页表)

Step4:

virtual address中第10-21bit作为索引,选取页表中的一个PTE(页表项)

Step5:

PTE中的内存页表基地址(高20bit+ virtual address中的低12offset,即可以得到实际的physical address 了。

图示如下:

WinDbg演示IA-32 CPU下的Windows 分页机制下的地址转换过程_第1张图片

 下面用windbg展示地址转换过程

以windows计算器(calc.exe)为例

1.打开calc.exe,输入数字,如123456, 然后在user mode下开始调试calc.exe

0:002> x calc!g*
01014f08 calc!ghwndTimeOutDlg = <no type information>
01014d9c calc!g_fHighContrast = <no type information>
0100514d calc!GetKeyColor = <no type information>
01014ef8 calc!gfExiting = <no type information>
0100518d calc!GetHelpID = <no type information>
01014c70 calc!ghnoPrecNum = <no type information>
01014c08 calc!ghnoParNum = <no type information>
01014038 calc!gszSep = <no type information>
01014eec calc!ghcurOld = <no type information>
01014d38 calc!g_ahnoChopNumbers = <no type information>
01014f00 calc!ghCalcDone = <no type information>
01014db0 calc!gpszNum = <no type information>
01014f0c calc!gnPendingError = <no type information>
01014000 calc!gnDecGrouping = <no type information>
01014dc0 calc!gcio = <no type information>
01014d98 calc!ghnoLastNum = <no type information>
01014f04 calc!ghDogThread = <no type information>
01014d80 calc!g_hDecMenu = <no type information>
01014f48 calc!gbinexact = <no type information>
01014d7c calc!g_hHexMenu = <no type information>
01014efc calc!ghCalcStart = <no type information>
01014da0 calc!g_fLayoutRTL = <no type information>
01014db8 calc!gbRecord = <no type information>
010149d8 calc!gcIntDigits = <no type information>
01014d6c calc!g_hwndDlg = <no type information>
01014d4c calc!gbUseSep = <no type information>
01014d94 calc!ghnoMem = <no type information>
010044b4 calc!GroupDigits = <no type information>
01014f4c calc!gllfact = <no type information>
01014d90 calc!ghnoNum = <no type information>
01014064 calc!gldPrevious = <no type information>

0:002> dd 01014db0
01014db0  000b2ee0 00000000 00000001 00000000
01014dc0  00000000 ffffffff 00000000 00000000
01014dd0  00000006 00320031 00340033 00360035
01014de0  00000000 00000000 00000000 00000000
01014df0  00000000 00000000 00000000 00000000
01014e00  00000000 00000000 00000000 00000000
01014e10  00000000 00000000 00000000 00000000
01014e20  00000000 00000000 00000000 00000000

我们可以看看000b2ee0附近 连续内存空间里面的内容:

0:002> dd 000b2ee0
000b2ee0  00320031 00340033 00360035 0000002e
000b2ef0  00030025 0008013d 000b3060 000b2f14
000b2f00  00000000 00000000 00000000 00000000
000b2f10  00000000 5443534d 614d2e46 61687372
000b2f20  746e496c 61667265 462e6563 4d656c69
000b2f30  412e7061 462e4c4d 4545482e 00464945
000b2f40  00000000 00000000 00000000 00000000
000b2f50  00000000 00000000 00000000 00000000

输入字符串变量的地址就在000b2ee0内存里面
0:002> du 000b2ee0
000b2ee0  "123456."

该地址000b2ee0 里面存放的正是我们所输入的数字:123456。而000b2ee0  为 virtual address。那么其physical address 到底是什么呢?根据前面的介绍,一个32bit的virtual address由3部分组成,我们可以具体看看每一部分的值

0:002> .formats 000b2ee0
Evaluate expression:
  Hex:     000b2ee0
  Decimal: 732896
  Octal:   00002627340
  Binary:  00000000 00001011 00101110 11100000
  Chars:   ....
  Time:    Fri Jan 09 03:34:56 1970
  Float:   low 1.02701e-039 high 0
  Double:  3.62099e-318

由以上可以看出,其PDE index为高10 :0, PTE index(中间10bit): B2, 页内偏移地址:EE0

下面找出该虚地址的absolute address

再启动一个kernal model debug -> local

lkd> !process 0 0

......

PROCESS 85185288  SessionId: 0  Cid: 0b8c    Peb: 7ffde000  ParentCid: 021c
    DirBase: 1d386000  ObjectTable: e1ff17e0  HandleCount: 187.
    Image: dllhost.exe

PROCESS 84f10da0  SessionId: 0  Cid: 049c    Peb: 7ffde000  ParentCid: 0508
    DirBase: 093ee000  ObjectTable: e2ace720  HandleCount:  49.
    Image: calc.exe

PROCESS 847e6220  SessionId: 0  Cid: 0840    Peb: 7ffde000  ParentCid: 02c4
    DirBase: 15297000  ObjectTable: e2b1fe30  HandleCount: 161.
    Image: msmsgs.exe

其中DirBase所指向的地址高20位 即为该进程calc.exe的页目录基地址:093ee000(低12固定为0)

下面来看PD中具体的PDE

lkd> !dd 093ee000  (显示指定地址的页目录表项内容)
# 93ee000 093fb067 0c765067 1803b067 00000000
# 93ee010 14240067 00000000 00000000 00000000
# 93ee020 00000000 00000000 00000000 00000000
# 93ee030 00000000 00000000 00000000 00000000
# 93ee040 00000000 00000000 00000000 00000000
# 93ee050 00000000 00000000 00000000 00000000
# 93ee060 00000000 00000000 00000000 00000000
# 93ee070 00000000 00000000 00000000 00000000
根据virtual address中的 PDE index:0, 故其在PD中查找PDT的index为0, 即为第1个PDE :093fb067 。

在该093fb067 地址里面, 其高20bit (即093fb000)为其page table(页表)起始地址,低12bit为页表属性,至于每个Bit代表什么属性,在此不作赘述。

页表起始地址 + 页表项在页表内的索引,即可以得到该PTE的物理地址。从以上我们已经知道,该virtual address在PT中的索引值为B2,故PTE的地址为:

093fb000 + B2*4 (因为每个表项占4个byte)

lkd> !dd 093fb000 + B2 * 4
# 93fb2c8 105eb067 148ec886 18aed886 0dcee886
# 93fb2d8 00000080 00000000 00000000 00000000
# 93fb2e8 00000000 00000000 00000000 00000000
# 93fb2f8 00000000 00000000 00000000 00000000
# 93fb308 00000000 00000000 00000000 00000000
# 93fb318 00000000 00000000 00000000 00000000
# 93fb328 00000000 00000000 00000000 00000000
# 93fb338 00000000 00000000 00000000 00000000

可以得到该地址内的PTE地址为105eb067 ,而该地址的高20bit( 105eb000 )为所在物理内存页的起始地址,低12bit为内存页属性。

得到该物理页的起始地址后 + virtual address的offset,即可得到其物理地址

即105eb000 + EE0 =105ebee0

下面我们看看该地址内的内容是什么:

lkd> !dd 105ebee0
#105ebee0 00320031 00340033 00360035 0000002e
#105ebef0 00030025 0008013d 000b3060 000b2f14
#105ebf00 00000000 00000000 00000000 00000000
#105ebf10 00000000 5443534d 614d2e46 61687372
#105ebf20 746e496c 61667265 462e6563 4d656c69
#105ebf30 412e7061 462e4c4d 4545482e 00464945
#105ebf40 00000000 00000000 00000000 00000000
#105ebf50 00000000 00000000 00000000 00000000

lkd> !du 105ebee0
#105ebee0 "123456."

比对user mode下的dd 000b2ee0 ,我们可以看到, 其内容是完全相同的。

也就是说我们找到了virtual address : 000b2ee0 的准确物理地址:105ebee0。

只不过我们在user mode下使用dd时,这个转换过程自动完成了。


你可能感兴趣的:(c,windows,image,table,attributes,caching)