將虛擬地址轉換成物理地址

大多數調試器命令的輸入參數和輸出結果使用虛擬地址,而不使用物理地址。不過,有時候可能用得上物理地址。

有兩個方法將一個虛擬地址轉換成一個物理地址:使用 !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。這跟以上方法獲得的結果相同。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章