假设我们有进程A和进程B,都使用example.dll。
现在让我们假设这个dll被映射到进程A和进程B中的不同地址(比如说,这是由于ASLR或其他一些冲突)。
操作系统是否会将其映射两次,或者仍然能够使用为两个进程映射 dll 的同一物理页?我的意思是,这就是 DLL 的全部意义,对吗?共享内存,因此我们不必映射两次。
您已经从评论中得到了所有正确的答案,我只想通过内核调试器(Windbg)来演示它。
我正在运行 notepad.exe 和 explorer.exe。
EPROCESS
结构体(这是从内核角度描述一个进程的结构体,这是PROCESS
后面的64位数字):0: kd> !process 0 0 notepad.exe
PROCESS ffffe28aca250080
SessionId: 2 Cid: 0f58 Peb: 5903bde000 ParentCid: 1c60
DirBase: 16543002 ObjectTable: ffffab8f834dac40 HandleCount: 240.
Image: notepad.exe
注意 DirBase 给出的进程的 CR3,这里:
0x16543002
。
0: kd> .process /i ffffe28aca250080
3: kd> lmu
start end module name
; snip
00007ff9`c6120000 00007ff9`c61cc000 ADVAPI32 (deferred)
00007ff9`c6330000 00007ff9`c64d0000 USER32 (deferred)
00007ff9`c6510000 00007ff9`c6705000 ntdll (pdb symbols)
我们有模块 base、end 及其名称。在 Windows 上,ASLR 是“每次启动”(不是每个进程)。因此,在所有进程中,ntdll
将被映射到相同的地址,直到系统重新启动并选择新的随机地址。
ntdll
3: kd> db 00007ff9`c6510000
00007ff9`c6510000 4d 5a 90 00 03 00 00 00-04 00 00 00 ff ff 00 00 MZ..............
00007ff9`c6510010 b8 00 00 00 00 00 00 00-40 00 00 00 00 00 00 00 ........@.......
00007ff9`c6510020 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
00007ff9`c6510030 00 00 00 00 00 00 00 00-00 00 00 00 e8 00 00 00 ................
00007ff9`c6510040 0e 1f ba 0e 00 b4 09 cd-21 b8 01 4c cd 21 54 68 ........!..L.!Th
00007ff9`c6510050 69 73 20 70 72 6f 67 72-61 6d 20 63 61 6e 6e 6f is program canno
00007ff9`c6510060 74 20 62 65 20 72 75 6e-20 69 6e 20 44 4f 53 20 t be run in DOS
00007ff9`c6510070 6d 6f 64 65 2e 0d 0d 0a-24 00 00 00 00 00 00 00 mode....$.......
典型的 PE 模块头(
MZ
和
This program cannot ...
)。
将虚拟地址转换为物理地址。命令!vtop
0: kd> !vtop 16543000 00007ff9c6510000
Amd64VtoP: Virt 00007ff9c6510000, pagedir 0000000016543000
Amd64VtoP: PML4E 00000000165437f8
Amd64VtoP: PDPE 000000012ce4ff38
Amd64VtoP: PDE 000000012cd52190
Amd64VtoP: PTE 0000000012d53880
Amd64VtoP: Mapped phys 00000002c1b98000
Virtual address 7ff9c6510000 translates to physical address 2c1b98000.
0: kd> !db 2c1b98000
#2c1b98000 4d 5a 90 00 03 00 00 00-04 00 00 00 ff ff 00 00 MZ..............
#2c1b98010 b8 00 00 00 00 00 00 00-40 00 00 00 00 00 00 00 ........@.......
#2c1b98020 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................
#2c1b98030 00 00 00 00 00 00 00 00-00 00 00 00 e8 00 00 00 ................
#2c1b98040 0e 1f ba 0e 00 b4 09 cd-21 b8 01 4c cd 21 54 68 ........!..L.!Th
#2c1b98050 69 73 20 70 72 6f 67 72-61 6d 20 63 61 6e 6e 6f is program canno
#2c1b98060 74 20 62 65 20 72 75 6e-20 69 6e 20 44 4f 53 20 t be run in DOS
#2c1b98070 6d 6f 64 65 2e 0d 0d 0a-24 00 00 00 00 00 00 00 mode....$.......
现在让我们尝试一下explorer.exe:
2: kd> !process 0 0 explorer.exe
PROCESS ffffe28ac9c4f080
SessionId: 2 Cid: 1c60 Peb: 00568000 ParentCid: 1c3c
DirBase: 1546ad002 ObjectTable: ffffab8f83851cc0 HandleCount: 2638.
Image: explorer.exe
2: kd> .process /i ffffe28ac9c4f080
1: kd> db 00007ff9`c6510000 L10
00007ff9`c6510000 4d 5a 90 00 03 00 00 00-04 00 00 00 ff ff 00 00 MZ..............
1: kd> !vtop 1546ad000 00007ff9c6510000
Amd64VtoP: Virt 00007ff9c6510000, pagedir 00000001546ad000
Amd64VtoP: PML4E 00000001546ad7f8
Amd64VtoP: PDPE 0000000155db9f38
Amd64VtoP: PDE 0000000155dbf190
Amd64VtoP: PTE 0000000155dc0880
Amd64VtoP: Mapped phys 00000002c1b98000
Virtual address 7ff9c6510000 translates to physical address 2c1b98000.
如您所见,我们得到了相同的结果物理地址,但请注意,两个进程中的表条目(PML4E、PDPTE、PDE 和 PTE)并不相同,但结果物理页完全相同。
显然,Windows 有一种 CoW(写入时复制)机制,如果您写入其中一个页面,该机制会立即复制,并且只有发生写入的进程才能看到修改后的页面。