How to manually translate virtual addresses into physical ones
In previous posts we talked about virtual address space and how virtual memory is managed. I've never posted anything about virtual address translation though, and for the ones interested on the details behing this operation I recommend reading the chapter 7 - Memory Management (specifically the Address Translation section) of the Windows Internals 4th Edition book. So I won't go too deep on the details of how the system does the translation itself, my goal here is just how to show you how easily translate from one address to another once you have a memory dump or a live debugging session.
Once you choose the virtual address you want to do the translation the first step is to run the command !pte and find out some additional information for the corresponding PTE. For this example I'm going to use the virtual address used as a base address to load the tcpip driver itself on a 32-bit Windows 2003 SP2 system:
0: kd> lmvm tcpip
start end module name
f7625000 f7686000 tcpip (deferred)
Image path: \SystemRoot\system32\DRIVERS\tcpip.sys
Image name: tcpip.sys
Timestamp: Thu Mar 24 19:40:31 2005 (42435DFF)
Above highlighted in yellow we have the base address where the tcpip driver has been loaded (virtual address). Now we run the command !pte passing this address as an argument:
0: kd> !pte f7625000
PDE at C0300F74 PTE at C03DD894
contains 00ACC963 contains 1C055963
pfn acc -G-DA--KWEV pfn 1c055 -G-DA—KWEV
The highlighted information above is about the PFN correlated to the physical address associated with the virtual address we passed as an input to !pte. However you noticed that 1c055 uses up to 20 bits (which are used to locate the PDE and PTE itself which will point to the physical page) and we know the completed addresses are 32 bits wide. The last 12 bits on the translation process are used as a byte index to the physical page where the content is actually stored, so they come straight from the virtual address.
The final physical address will then be 1c055 followed by the last 12 bits of the virtual address which in this case is 000. So putting them together we have that 1c055000 is the physical address correlated to the virtual address f7652000. Let’s verify that by dumping both address and comparing their content:
Dumping the virtual address first:
0: kd> dc f7625000
f7625000 00905a4d 00000003 00000004 0000ffff MZ..............
f7625010 000000b8 00000000 00000040 00000000 ........@.......
f7625020 00000000 00000000 00000000 00000000 ................
f7625030 00000000 00000000 00000000 000000e0 ................
f7625040 0eba1f0e cd09b400 4c01b821 685421cd ........!..L.!Th
f7625050 70207369 72676f72 63206d61 6f6e6e61 is program canno
f7625060 65622074 6e757220 206e6920 20534f44 t be run in DOS
f7625070 65646f6d 0a0d0d2e 00000024 00000000 mode....$.......
Now let’s dump the physical address and compare the results:
0: kd> dc /p 1c055000
1c055000 00905a4d 00000003 00000004 0000ffff MZ..............
1c055010 000000b8 00000000 00000040 00000000 ........@.......
1c055020 00000000 00000000 00000000 00000000 ................
1c055030 00000000 00000000 00000000 000000e0 ................
1c055040 0eba1f0e cd09b400 4c01b821 685421cd ........!..L.!Th
1c055050 70207369 72676f72 63206d61 6f6e6e61 is program canno
1c055060 65622074 6e757220 206e6920 20534f44 t be run in DOS
1c055070 65646f6d 0a0d0d2e 00000024 00000000 mode....$.......
The content is identical as expected J.