📄 jiurl玩玩win2k内存篇 内存共享(二) copyonwrite.htm
字号:
NOTEPAD<BR>0328F000 84130860 01F8 NOTEPAD<BR>018EF000 8323F180 0100
CopyOnWrite-Er<BR>06AB8000 83091020 02BC CopyOnWrite-Er<BR>*00030000
8046BB60 0000 Idle<BR><BR>看到了2个 CopyOnWrite-Er ,进程ID 分别是 0100 和
02BC<BR><BR>这2个 CopyOnWrite-Er
的输出都是<BR>_________________________________________<BR><BR>JiurlSegData
Address: 0x0040a000<BR>JiurlSegData Pte Address:
0xc0001028<BR>JiurlSegData: aaaaaaaaaaaaaaaaaaaaaaaa<BR><BR>Input New
String to
JiurlSegData<BR>_________________________________________<BR><BR>CopyOnWrite-Er
100<BR><BR>使用 addr 命令转换到 CopyOnWrite-Er 100 的地址空间<BR>:addr 100<BR><BR>再看一下
CopyOnWrite-Er 100 的段的情况,看到了 ".jiurl" 段,<BR>地址范围在 0040A000 大小为
00000020<BR>和程序的输出 JiurlSegData Address: 0x0040a000 相符<BR>:map32
-u<BR>Owner Obj Name Obj# Address Size Type<BR>CopyOnWrit.text 0001
001B:00401000 00004D73 CODE RO<BR>CopyOnWrit.rdata 0002 0023:00406000
00000890 IDATA RO<BR>CopyOnWrit.data 0003 0023:00407000 000021C8 IDATA
RW<BR>CopyOnWrit.jiurl 0004 0023:0040A000 00000020 IDATA RW<BR>kernel32
.text 0001 001B:77E61000 0005D1AE CODE RO<BR>kernel32 .data 0002
0023:77EBF000 00001A30 IDATA RW<BR>kernel32 .rsrc 0003 0023:77EC1000
00070000 IDATA RO<BR>kernel32 .reloc 0004 0023:77F31000 0000359C IDATA
RO<BR>ntdll .text 0001 001B:77F81000 00042492 CODE RO<BR>ntdll ECODE 0002
001B:77FC4000 00004371 CODE RO<BR>ntdll PAGE 0003 001B:77FC9000 00003983
CODE RO<BR>ntdll .data 0004 0023:77FCD000 00002350 IDATA RW<BR>ntdll .rsrc
0005 0023:77FD0000 00026D08 IDATA RO<BR>ntdll .reloc 0006 0023:77FF7000
00001DA8 IDATA RO<BR><BR>根据我们计算出的 JiurlSegData Pte Address: 0xc0001028
,显示该页表项<BR>:dd c0001028 l 10<BR>0010:C0001028 06AC7225 00000000 00000000
00000000 %r..............<BR><BR>可以看到<BR>CopyOnWrite-Er 100 的 JiurlSegData
地址范围的 页表项值为 06AC7225<BR>注意物理页为 06AC7 ,标志为 225(hex)= 0010 0010 0101
(bin) <BR>bits1-1 Write 标志为0,表示只读。bits9-9 CopyOnWrite 标志为1,表示使用 Copy
On Write 。<BR><BR>CopyOnWrite-Er 2bc<BR><BR>转换到 CopyOnWrite-Er 2bc
的地址空间<BR>:addr 2bc<BR><BR>根据我们计算出的 JiurlSegData Pte Address: 0xc0001028
,显示该页表项<BR>:dd c0001028 l 10<BR>0010:C0001028 06AC7225 00000000 00000000
00000000 %r..............<BR>可以看到<BR>CopyOnWrite-Er 2bc 的 JiurlSegData
地址范围的 页表项值也为 06AC7225<BR>首先就是它和 CopyOnWrite-Er 100 映射了同样的物理页 06AC7
,页中的内容当然是一样的。他们共享了这个数据页。标志位同样也说明了使用 Copy On Write。<BR><BR>2. 向
CopyOnWrite-Er 2bc 的全局变量写入<BR><BR>向 CopyOnWrite-Er 2bc 的全局变量写入
bbbbbbbbb<BR>程序输出如下<BR><BR>_________________________________________<BR><BR>Input
New String to JiurlSegData<BR>:bbbbbbbbb<BR>JiurlSegData:
bbbbbbbbb<BR>_________________________________________<BR><BR>可以看到正确读出了输入的
bbbbbbbbb<BR><BR><BR>转换到 CopyOnWrite-Er 2bc 的地址空间<BR>:addr 2bc<BR><BR>显示
CopyOnWrite-Er 2bc 的 0xc0001028 处的页表项<BR>:dd c0001028 l
10<BR>0010:C0001028 04427067 00000000 00000000 00000000
gpB.............<BR>该页表项值为 04427067 ,可以看到映射的物理页变成了 04427
,这说明分配并使用了新的物理页。<BR>标志为 067(hex)= 0000 0110 0111 (bin) 可以看到<BR>bits1-1
Write 标志为1,表示可写了。bits9-9 CopyOnWrite 标志为0。<BR>对于 CopyOnWrite-Er 2bc ,Copy
On Write 机制得到了验证<BR><BR><BR>对于 CopyOnWrite-Er 100 的情况<BR><BR>转换到
CopyOnWrite-Er 100 的地址空间<BR>:addr 100<BR><BR>显示 CopyOnWrite-Er 100 的
0xc0001028 处的页表项<BR>:dd c0001028 l 10<BR>0010:C0001028 06AC7225 00000000
00000000 00000000 %r..............<BR>可以看到该页表项的值仍然是 06AC7225
,说明<BR>CopyOnWrite-Er 100 继续使用原来的物理页<BR>这也符合前面描述的 Copy On Write
机制<BR><BR><BR>3. 向 CopyOnWrite-Er 100 的全局变量写入<BR><BR>向 CopyOnWrite-Er 2bc
的全局变量写入
cccccccccc<BR>程序输出如下<BR>_________________________________________<BR><BR>Input
New String to JiurlSegData<BR>:cccccccccc<BR>JiurlSegData:
cccccccccc<BR>_________________________________________<BR><BR>可以看到正确读出了输入的
cccccccccc <BR><BR>转换到 CopyOnWrite-Er 100 的地址空间<BR>:addr
100<BR><BR>显示 CopyOnWrite-Er 100 的 0xc0001028 处的页表项<BR>:dd c0001028 l
10<BR>0010:C0001028 00D07067 00000000 00000000 00000000
gp..............<BR>可以看到,写入导致了使用新的物理页,并且改变了标志<BR><BR><BR>转换到
CopyOnWrite-Er 2bc 的地址空间 <BR>:addr 2bc<BR><BR>显示 CopyOnWrite-Er 2bc 的
0xc0001028 处的页表项<BR>:dd c0001028 l 10<BR>0010:C0001028 04427067 00000000
00000000 00000000 gpB.............<BR>还是刚才的<BR><BR>通过这个例子,我们看到了 Win2k 使用
Copy On Write 的效果<BR><BR>4. PfnDataBaseEntry 和 Copy On
Write<BR><BR>我们再来观察一下被共享的物理页的 PfnDataBaseEntry 中的情况。<BR><BR>运行一个
CopyOnWrite-Er 我们观察它的物理页的页帧号<BR>:addr CopyOnWrite-Er<BR>:dd c0001028 l
10<BR>0010:C0001028 06AC7225 00000000 00000000 00000000
%r..............<BR>页帧号为 06AC7<BR><BR>计算 06AC7 的页帧号数据库项的地址,页帧号数据库的首地址在全局变量
MmPfnDatabase 中,<BR>在我机子上它的值为 81456000 ,每项大小 0x18 个字节<BR><BR>我们可以看到 物理页
6ac7<BR>/*08*/ uint32 blink / share count = 00000001<BR>/*0D*/ byte page
state = 06<BR>:dd 81456000+18*6ac7 l 18<BR>0010:814F62A8 0000009B E301F2A8
00000001 00010608 ................<BR>0010:814F62B8 907B64B8 00004727
00000000 C03B37D8 .d{.'G.......7;.<BR>说明该物理页处在 Active
状态,并且共享数为1<BR><BR>我们又运行了两个 CopyOnWrite-Er <BR>我们可以看到 物理页
6ac7<BR>/*08*/ uint32 blink / share count = 00000003<BR>/*0D*/ byte page
state = 06<BR>:dd 81456000+18*6ac7 l 18<BR>0010:814F62A8 0000009B E301F2A8
00000003 00010608 ................<BR>0010:814F62B8 907B64B8 00004727
00000000 C03B37D8 .d{.'G.......7;.<BR>说明该物理页处在 Active
状态,并且共享数增为3<BR><BR>对一个 CopyOnWrite-Er 写入数据<BR>我们可以看到 物理页 6ac7<BR>/*08*/
uint32 blink / share count = 00000002<BR>/*0D*/ byte page state =
06<BR>:dd 81456000+18*6ac7 l 18<BR>0010:814F62A8 0000009B E301F2A8
00000002 00010608 ................<BR>0010:814F62B8 907B64B8 00004727
00000000 C03B37D8 .d{.'G.......7;.<BR>并且共享减为2,写数据的进程由于 Copy On Write
而不再使用本物理页<BR><BR>对剩下所有的 CopyOnWrite-Er 写入数据<BR>我们可以看到 物理页 6ac7<BR>/*08*/
uint32 blink / share count = 000068B9<BR>/*0D*/ byte page state =
02<BR>:dd 81456000+18*6ac7 l 18<BR>0010:814F62A8 FFFFFFFF E301F2A8
000068B9 00000208 .........h......<BR>0010:814F62B8 907B64B8 00004727
00000000 C03B37D8 .d{.'G.......7;.<BR>没有进程再使用这个物理页,所以状态变为了 Standby ,而且
/*08*/ 由于状态的变化含义也变成了 blink<BR><BR><B>广泛存在的 Copy On Write</B></FONT>
<P><FONT face=宋体> 观察一个进程的页表,你会发现除了我们设计的例子之外,很难找到其他的设置了
CopyOnWrite 标志的页表项(还是可以找到的,我用一个小工具就在记事本进程中找到了几项,标志位为 205),难道 Copy On Write
只有如此少量的应用?实际上 Win2k 中大量使用了 Copy On Write 我们使用 SoftICE 就可以观察到。 </FONT>
<P><FONT face=宋体> 当向 Copy On Write 页写时,会引发 Page-Fault
异常,CPU 会执行异常处理程序 ntoskrnl!KiTrap0E ntoskrnl!KiTrap0E 会调用
ntoskrnl!MmAccessFault ,分析 ntoskrnl!MmAccessFault 的汇编代码,可以知道 CopyOnWrite
引起的异常将由 ntoskrnl!MiCopyOnWrite 处理。分析汇编代码我们还可以看出 MiCopyOnWrite
函数有两个传入参数,第一个参数是引发异常的指令访问的虚拟地址,我们叫做 BadAddress 。第二个参数是该虚拟地址的页表项地址,我们叫做
PteAddress 。MiCopyOnWrite 使用 fastcall 调用协议,第一个参数 BadAddress 通过 ecx
寄存器传入,第二个参数 PteAddress 通过 edx 寄存器传入。只要向 CopyOnWrite 页写入数据,我们就应该可以断到
MiCopyOnWrite ,并且从 ecx 和 edx 当中看到 引发异常的指令访问的虚拟地址 和 该地址的 PTE 地址。<BR><BR>我们在
SoftICE 中下这样的断点<BR>BPX 8044BE22 DO "db ((ffdff124->0)+44)->0+1fc l
10 ; dd ecx l 4 ; dd edx l 4" <BR>其中,8044BE22 是 MiCopyOnWrite 的入口地址(
Win2k Build 2195 ),通过使用 kd , u MiCopyOnWrite 获得。断到之后,让 SoftICE 执行 db
((ffdff124->0)+44)->0+1fc l 10 ,这将显示当前运行的进程名。然后再显示出 ecx 和 edx
中的内容。<BR><BR>当我们运行一个程序时,这里我运行了一记事本程序,SoftICE 窗口立刻弹出,说明有 Copy On Write
发生。<BR><BR>// 这就是下的断点。<BR>:bl<BR>00) * BPX #0008:8044BE22 DO "db
((ffdff124->0)+44)->0+1fc l 10 ; dd ecx l 4 ; d<BR>:be 0<BR>// 退出
SoftICE 窗口<BR><BR>// 打开一个文本文件,运行了记事本程序,SoftICE 窗口立刻弹出<BR>NTICE: Load32
START=1000000 SIZE=10000 KPEB=80D5AD40 MOD=NOTEPAD<BR>NTICE: Load32
START=77F80000 SIZE=79000 KPEB=80D5AD40 MOD=ntdll<BR>Break due to BPX
#0008:8044BE22 DO "db ((ffdff124->0)+44)->0+1fc l 10 ; dd ecx<BR>l 4
; dd edx l 4" (ET=4.09 seconds)<BR>// 可以看到当前进程是
NOTEPAD.EXE<BR>0023:80D5AF3C 4E 4F 54 45 50 41 44 2E-45 58 45 00 00 00 00
00 NOTEPAD.EXE.....<BR>// ecx 也就是 BadAddress 为
77FCD34C <BR>0023:77FCD34C FFFFFFFF 00000000 00000000 00000000
................<BR>// edx 也就是 PteAddress 为 C01DFF34 ,可以看到页表项中标志位的
CopyOnWrite位 被设置,Write位 没// 有被设置<BR>0023:C01DFF34 006AA225 00000000
00000000 00000000 %.j.............<BR><BR>// 只有 NOTEPAD 和 ntdll
,BadAddress= 77FCD34C 在 ntdll .data 范围内。<BR>// 只有 NOTEPAD 和 ntdll 说明了其他的
dll 还没有被载入,也就是说在 ntdll 被载入,执行 DllMain 中// 对 ntdll 初始化时,就向使用了 CopyOnWrite
的数据页中写入数据,这将导致数据页的页表项中的物理页// 为新分配的物理页,并且标志将从 ReadOnly CopyOnWrite 变成
ReadWrite。<BR>:map32 -u<BR>Owner Obj Name Obj# Address Size
Type<BR>NOTEPAD .text 0001 001B:01001000 000065CA CODE RO<BR>NOTEPAD .data
0002 0023:01008000 00001944 IDATA RW<BR>NOTEPAD .rsrc 0003 0023:0100A000
00005238 IDATA RO<BR>ntdll .text 0001 001B:77F81000 00042492 CODE
RO<BR>ntdll ECODE 0002 001B:77FC4000 00004371 CODE RO<BR>ntdll PAGE 0003
001B:77FC9000 00003983 CODE RO<BR>ntdll .data 0004 0023:77FCD000 00002350
IDATA RW<BR>ntdll .rsrc 0005 0023:77FD0000 00026D08 IDATA RO<BR>ntdll
.reloc 0006 0023:77FF7000 00001DA8 IDATA RO<BR><BR>// Ctrl+D , SoftICE
立刻(322ms后)又被中断,ET=322.23 microseconds<BR>Break due to BPX #0008:8044BE22
DO "db ((ffdff124->0)+44)->0+1fc l 10 ; dd ecx<BR>l 4 ; dd edx l 4"
(ET=322.23 microseconds)<BR>0023:80D5AF3C 4E 4F 54 45 50 41 44 2E-45 58 45
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -