次の方法で共有


仮想アドレスから物理アドレスへの変換

ほとんどのデバッガー コマンドでは、入力と出力として、物理アドレスではなく仮想アドレスが使用されます。 ただし、物理アドレスを使用すると便利な場合があります。

仮想アドレスを物理アドレスに変換するには、!vtop 拡張機能を使用する方法と、!pte 拡張機能を使用する方法の 2 つの方法があります。

Windows の仮想アドレスの概要については、「仮想アドレス空間」を参照してください。

!vtop を使用したアドレス変換

MyApp.exe プロセスが実行されているターゲット コンピューターをデバッグしていて、仮想アドレスの 0x0012F980 を調査するとします。 対応する物理アドレスを決定するために、!vtop 拡張機能と共に使用する手順を次に示します。

!vtop を使用して仮想アドレスを物理アドレスに変換する

  1. 16 進数で作業していることを確認します。 必要に応じて、N 16 コマンドを使用して現在のベースを設定します。

  2. アドレスのバイト インデックスを決定します。 この数は、仮想アドレスの最小 12 ビットと等しくなります。 したがって、仮想アドレス 0x0012F980 には、0x980 のバイト インデックスがあります。

  3. !process 拡張機能を使用して、アドレスのディレクトリ ベースを決定します。

    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
    
  4. ディレクトリ ベースのページ フレーム番号を決定します。 これは、末尾に 3 つの 16 進ゼロがない単なるディレクトリ ベースです。 この例では、ディレクトリ ベースは 0x098FD000 であるため、ページ フレーム番号は 0x098FD です。

  5. !vtop 拡張機能を使用します。 この拡張機能の最初のパラメーターは、ページ フレーム番号である必要があります。 !vtop の 2 番目のパラメーターは、問題の仮想アドレスである必要があります。

    kd> !vtop 98fd 12f980
    Pdi 0 Pti 12f
    0012f980 09de9000 pfn(09de9)
    

    最後の行に表示される 2 番目の番号は、物理ページの先頭の物理アドレスです。

  6. ページの先頭のアドレスにバイト インデックスを追加します: 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* (Display Memory) コマンドは、引数として仮想アドレスを使用します。

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 を使用して仮想アドレスを物理アドレスに変換する

  1. 16 進数で作業していることを確認します。 必要に応じて、N 16 コマンドを使用して現在のベースを設定します。

  2. アドレスのバイト インデックスを決定します。 この数は、仮想アドレスの最小 12 ビットと等しくなります。 したがって、仮想アドレス 0x0012F980 には、0x980 のバイト インデックスがあります。

  3. プロセス コンテキストを目的のプロセスに設定します。

    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
    
  4. 仮想アドレスを引数として !pte 拡張子を使用します。 これにより、2 つの列に情報が表示されます。 左側の列には、このアドレスのページ ディレクトリ エントリ (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
    
  5. 右側の列の最後の行を見てください。 "pfn 9de9" という表記が表示されます。 0x9DE9番号は、この PTE の ページ フレーム番号 (PFN) です。 ページ フレーム番号に 0x1000 を乗算します (たとえば、左に 12 ビットシフトします)。 結果の 0x09DE9000 は、ページの先頭の物理アドレスです。

  6. ページの先頭のアドレスにバイト インデックスを追加します: 0x09DE9000 + 0x980 = 0x09DE9980。 これは目的の物理アドレスです。

これは、前の方法で取得した結果と同じです。

アドレスを手動で変換する

!ptov および pte 拡張機能は、仮想アドレスを物理アドレスに変換する最速の方法を提供しますが、この変換も手動で行うことができます。 このプロセスの説明は、仮想メモリ アーキテクチャの詳細の一部を明らかにします。

メモリ構造のサイズは、プロセッサとハードウェアの構成によって異なります。 この例は、物理アドレス拡張 (PAE) が有効になっていない x86 システムから取得されます。

0x0012F980を仮想アドレスとして再び使用する場合は、まず、手動または .formats (Show Number 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

この仮想アドレスは、3 つのフィールドの組み合わせです。 ビット 0 から 11 はバイト インデックスです。 ビット 12 から 21 は、ページ テーブルのインデックスです。 ビット 22 から 31 は、ページ ディレクトリのインデックスです。 フィールドを区切ると、次のようになります。

0x0012F980  =  0y  00000000 00   010010 1111   1001 10000000

これにより、仮想アドレスの 3 つの部分が公開されます。

  • ページ ディレクトリ インデックス = 0y000000000 = 0x0

  • ページ テーブルインデックス = 0y0100101111 = 0x12F

  • バイト インデックス = 0y100110000000 = 0x980

その後、システムに対して 3 つの追加情報が必要になります。

  • 各 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 があります。 これは、次の 2 つのフィールドで構成されます。

  • PTE の下位 12 ビットがステータス フラグです。 この場合、これらのフラグは 0x067 -- またはバイナリの 0y000001100111 と等しくなります。 ステータス フラグの説明については、!pte リファレンス ページを参照してください。

  • PTE の上位 20 ビットは、PTE の ページ フレーム番号 (PFN) と同じです。 この例では、PFN は 0x09DE9 です。

物理ページの最初の物理アドレスは、PFN に 0x1000 (左 12 ビットシフト) を掛けた値です。 バイト インデックスは、このページのオフセットです。 したがって、探している物理アドレスは、0x09DE9000 + 0x980 = 0x09DE9980 です。 これは、前の方法で取得した結果と同じです。