今天在學習《軟體調試》的時候,練習虛擬位址轉物理地址的時候遇到了一個問題。用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 Entry(PDE)(頁目錄表項)的線性表。每個頁目錄佔一個4kb的記憶體頁(page),PD中的每個PDE長度為32Bit(其中高20bit為PDE所指向的頁表的起始地址,低12bit為該PDE的屬性)。因此每個PD中最多包含1024個PDE。
對於記憶體頁大小為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中最多可以包含1024個PTE
Virtual Address(虛地址)
對於一個32bit的virtual address,其格式如下所示:
其中高10bit為Page Directory中的index項,中間10Bit為Page Table的index項,最低12bit為頁內位移地址。
從virtual address向physical 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中的低12位offset,即可以得到實際的physical
address 了。
圖示如下:
下面用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時,這個轉換過程自動完成了。