大多數調試器命令的輸入參數和輸出結果使用虛擬位址,而不使用物理地址。不過,有時候可能用得上物理地址。
有兩個方法將一個虛擬位址轉換成一個物理地址:使用 !vtop 擴充和使用 !pte 擴充。在Windows NT 4.0中還可以使用 !vpdd 擴充。
使用 !vtop 進行地址轉換
假設你正在調試一台正在運行MyApp.exe進程的目標電腦,而且你想要調查虛擬位址0x0012F980。使用 !vtop 擴充確定對應的物理地址,步驟如下。
使用 !vtop 將虛擬位址轉換成物理地址
確保你是工作在十六進位中。如若不然,用 N 16 命令設定當前基數。
確定地址的位元組索引byte index。這個數值等於虛擬位址的最低12位。因此,虛擬位址 0x0012F980 的位元組索引是0x980。
使用 !process 擴充來確定該地址的目錄基址directory base:
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
確定目錄基址的頁架構號page frame number。只須把目錄基址去掉尾部三個十六進位零就是了。本例中,目錄基址是 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* (顯示記憶體) 命令以一個虛擬位址作為它的參數:
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 命令設定當前基數。
確定地址的byte index。這個數值等於虛擬位址的最低12位。因此,虛擬位址0x0012F980的位元組索引是0x980。
把需要的進程設定為進程上下文環境process context:
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的page frame number (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
這展現該虛擬位址的三個部份:
頁目錄索引 = 0y0000000000 = 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位是狀態標記status flags。這裡,這些標記等於0x067 - 或者用二進位表示為0y000001100111。關於狀態標記的說明,請看 !pte 參考頁。
該PTE的高20位等於該PTE的page frame number (PFN)。這裡,該PFN是0x09DE9。
該物理頁上的第一個物理地址是該PFN乘以0x1000 (左移12位)。位元組索引是在該頁上的位移量。因此,你所求的物理地址是0x09DE9000 + 0x980 = 0x09DE9980。這跟以上方法獲得的結果相同。