5

Windows 虚拟地址 到底是如何映射到 物理地址 的?

 11 months ago
source link: https://www.cnblogs.com/huangxincheng/p/17656346.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

1. 讲故事

我发现有很多的 .NET程序员 写了很多年的代码都没弄清楚什么是 虚拟地址,更不用谈什么是 物理地址 以及Windows是如何实现地址映射的了?这一篇我们就来聊一聊这两者之间的联系。

二:地址映射研究

1. 找虚拟地址

怎么去找 虚拟地址 呢?相信很多朋友都知道应用程序用的是虚拟地址,所以从应用程序中取一个就好了,这里就拿 notepad 举例子吧。

开启一个装有 win10 的虚拟机,然后打开 notepad.exe,使用 windbg 进行它的内核态调式,参考代码如下:


0: kd> !process 0 0 notepad.exe
PROCESS ffffe0011f9c9840
    SessionId: 1  Cid: 11a8    Peb: 7ff63d8ff000  ParentCid: 0bf4
    DirBase: 23c6d000  ObjectTable: ffffc00088bdcbc0  HandleCount: <Data Not Accessible>
    Image: notepad.exe

0: kd> .process /i /p ffffe0011f9c9840
You need to continue execution (press 'g' <enter>) for the context
to be switched. When the debugger breaks in again, you will be in
the new process context.

0: kd> g
Break instruction exception - code 80000003 (first chance)
nt!DbgBreakPointWithStatus:
fffff801`bed59c50 cc              int     3

1: kd> .reload /user
Loading User Symbols
....................................

Press ctrl-c (cdb, kd, ntsd) or ctrl-break (windbg) to abort symbol loads that take too long.
Run !sym noisy before .reload to track down problems loading symbols.

......

1: kd> lm
start             end                 module name
00007ff6`3e1e0000 00007ff6`3e21a000   notepad    (deferred)             
00007ff9`83e60000 00007ff9`83fac000   UIAutomationCore   (deferred)             
...

1: kd> dB 00007ff6`3e1e0000+0x50 L30
00007ff6`3e1e0050  69 73 20 70 72 6f 67 72-61 6d 20 63 61 6e 6e 6f  is program canno
00007ff6`3e1e0060  74 20 62 65 20 72 75 6e-20 69 6e 20 44 4f 53 20  t be run in DOS 
00007ff6`3e1e0070  6d 6f 64 65 2e 0d 0d 0a-24 00 00 00 00 00 00 00  mode....$.......

从卦中可以看到 00007ff63e1e0050 处是一段字符串,接下来我们就以它为例吧。

2. 如何用 Windbg 推算

到底是如何映射的呢?如果你了解 Windows 的源码可能你就很清楚,不了解也没关系,我们可以用 WinDbg 帮我们计算,在 windbg 中有一个 !vtop 命令可以一键查找,输出如下:


1: kd> !vtop 0 00007ff63e1e0050
Amd64VtoP: Virt 00007ff63e1e0050, pagedir 0000000023c6d000
Amd64VtoP: PML4E 0000000023c6d7f8
Amd64VtoP: PDPE 000000002360aec0
Amd64VtoP: PDE 000000000b910f80
Amd64VtoP: PTE 000000001fa51f00
Amd64VtoP: Mapped phys 000000000ad38050
Virtual address 7ff63e1e0050 translates to physical address ad38050.

1: kd> !dB ad38050 L30
# ad38050 69 73 20 70 72 6f 67 72-61 6d 20 63 61 6e 6e 6f is program canno
# ad38060 74 20 62 65 20 72 75 6e-20 69 6e 20 44 4f 53 20 t be run in DOS 
# ad38070 6d 6f 64 65 2e 0d 0d 0a-24 00 00 00 00 00 00 00 mode....$.......

从卦中可以清晰的看到,虚拟地址 00007ff63e1e0050 所对应的物理地址为 ad38050,然后用 !dB 去观察物理地址也确实如此。

这里要提醒一下,如果你还想知道这个物理地址所属的 PDE (页目录项)PTE (页表项) ,可以用 !pte 命令帮我们一键显示,输出如下:


1: kd> !pte 00007ff63e1e0050
                                           VA 00007ff63e1e0050
PXE at FFFFF6FB7DBED7F8    PPE at FFFFF6FB7DAFFEC0    PDE at FFFFF6FB5FFD8F80    PTE at FFFFF6BFFB1F0F00
contains 009000002360A867  contains 00E000000B910867  contains 00F000001FA51867  contains 810000000AD38025
pfn 2360a     ---DA--UWEV  pfn b910      ---DA--UWEV  pfn 1fa51     ---DA--UWEV  pfn ad38      ----A--UR-V

从卦中可以看到,x64的地址有四级结构,不仅有 PDE,PTE,还有 PXE, PPE,并且从 pfn ad38 可以清楚的看到它的物理页号是 ad38,加上虚拟地址后的 12bit(050) 偏移,最后的物理地址也就是 ad38050

用 WinDbg 推算虽然简单,但不利于我们了解原理,为了加深理解,我们需要手工的去推算。

3. 如何手工推算

要明白手工推算,在脑子中一定要有一张架构图,有了这张架构图就方便行事了。

214741-20230825104934859-1259234832.png

卦图中有几点要解释。

  1. 二进制怎么出来的?

可以用 windbg 的 .formats 命令。


1: kd> .formats 00007ff63e1e0050
Evaluate expression:
  Hex:     00007ff6`3e1e0050
  Decimal: 140695580835920
  Binary:  00000000 00000000 01111111 11110110 00111110 00011110 00000000 01010000

  1. CR3 是什么?

CR3 是Windows的控制寄存器,它记录着这个进程所属的虚拟地址首地址,专业点就是 BaseDir (基目录) 地址,参考如下输出:


1: kd> !process 0 0 notepad.exe
PROCESS ffffe0011f9c9840
    SessionId: 1  Cid: 11a8    Peb: 7ff63d8ff000  ParentCid: 0bf4
    DirBase: 23c6d000  ObjectTable: ffffc00088bdcbc0  HandleCount: <Data Not Accessible>
    Image: notepad.exe

  1. 各级页表占用多少bit位数?
  • PXE 占用 9bit(39-47)
  • PPE 占用 9bit(30-38)
  • PDE 占用 9bit(21-29)
  • PTE 占用 9bit(12-20)

有了这些信息之后,最后就是手工推算了,这里要提醒一下,每个表的首地址都把后 12bit 抹为0,因为他们是表的meta信息,详细输出如下:


1: kd> !process 0 0 notepad.exe
PROCESS ffffe0011f9c9840
    SessionId: 1  Cid: 11a8    Peb: 7ff63d8ff000  ParentCid: 0bf4
    DirBase: 23c6d000  ObjectTable: ffffc00088bdcbc0  HandleCount: <Data Not Accessible>
    Image: notepad.exe

1: kd> r cr3
cr3=0000000023c6d000

1: kd> !dp 23c6d000 + (0y011111111*8) L1
#23c6d7f8 00900000`2360a867

1: kd> !dp 2360a000+(0y111011000*8) L1
#2360aec0 00e00000`0b910867

1: kd> !dp 0b910000 + (0y111110000*8) L1

# b910f80 00f00000`1fa51867

1: kd> !dp 1fa51000+(0y111100000*8) L1
#1fa51f00 81000000`0ad38025

从卦中可以看到最后推算出来的是 810000000ad38025 ,抹掉 高32bit 和 末 12bit 之后就变成了 ad38,这个就是我们的 pfn (页帧号) ,如果你想核算一下 !dp 出来的值对不对,可以看下 !pte 命令中的 contains xxx 是不是这个值? 输出如下:


1: kd> !pte 00007ff63e1e0050
                                           VA 00007ff63e1e0050
PXE at FFFFF6FB7DBED7F8    PPE at FFFFF6FB7DAFFEC0    PDE at FFFFF6FB5FFD8F80    PTE at FFFFF6BFFB1F0F00
contains 009000002360A867  contains 00E000000B910867  contains 00F000001FA51867  contains 810000000AD38025
pfn 2360a     ---DA--UWEV  pfn b910      ---DA--UWEV  pfn 1fa51     ---DA--UWEV  pfn ad38      ----A--UR-V

从卦中可以看到,四个地址和pfn都是对的,最后 pfn+页内偏移 = ad38050 ,也就是我们苦苦寻找的 物理地址,再次输出一下结果。


1: kd> !dB ad38050 L30
# ad38050 69 73 20 70 72 6f 67 72-61 6d 20 63 61 6e 6e 6f is program canno
# ad38060 74 20 62 65 20 72 75 6e-20 69 6e 20 44 4f 53 20 t be run in DOS 
# ad38070 6d 6f 64 65 2e 0d 0d 0a-24 00 00 00 00 00 00 00 mode....$.......

手工推算是不是非常的有意思,可以让我们更加的理解Windows底层玩法,WinDbg在手,天下我有!

图片名称

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK