直接的原因是向MiDeleteValidAddress传递了一个无效的地址——00400221。如这个函数的函数名所说的,这个函数是用来删除一个有效的地址,也就是位于物理内存中的地址。每个有效地址在PFN数据库中有一个MMPFN结构与之对应。删除这个地址时需要找到这个结构然后对其进行更新。
但因为00400221是个无效的地址,所以用它计算出的PFN索引来寻找MMPFN结构的字段时非法访问了。
仔细观察出问题的那一刻:
eax=00000002 ebx=03ffffff ecx=00000020 edx=02800000 esi=c7000000 edi=c0002000 eip=8051174d esp=a5f73bf8 ebp=a5f73c14 iopl=0 nv up ei pl nz na po cy cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010203 nt!MiDeleteValidAddress+0x51: 8051174d 8b460c mov eax,dword ptr [esi+0Ch] ds:0023:c700000c=????????
其中edx的值应该是要删除地址(00400221)的pfn,在PFN数据库中每个表项的长度是0x1C:
1: kd> ?? sizeof(_MMPFN)unsigned int 0x1c
所以要把这个值*1C得到在PFN数据库中的偏移:
8051173f 8bf2 mov esi,edx 80511741 6bf61c imul esi,esi,1Ch
然后加上PFN数据库的起始地址:
80511744 0335c8105680 add esi,dword ptr [nt!MmPfnDatabase (805610c8)]
我们手工算一下:
1: kd> ? 02800000*1c+poi(MmPfnDatabase)Evaluate expression: -956301312 = c7000000
也就是计算出的要删除地址的MMPFN结构位于c7000000。
然后取偏移0xc处的_MMPFNENTRY:
8051174d 8b460c mov eax,dword ptr [esi+0Ch]
而这个地方根本不是一个有效地MMPFNENTRY,所以就异常了。
那么为什么要把一个无效的地址交给MiDeleteValidAddress去删除呢?
这要问上一级调用函数了,即MiDeleteAddressesInWorkingSet。
MiDeleteAddressesInWorkingSet负责删除指定进程的工作集。使用!wsle命令可以观察工作集的信息:
1: kd> !wsle
Working Set @ c0883000 FirstFree 103 FirstDynamic 11 LastEntry 108d NextSlot 7 LastInitialized 10c0 NonDirect 56c HashTable 0 HashTableSize a00
如果要列出当前进程工作集中的所有表项(内存页),那么可以执行!wsle 7命令:
1: kd> !wsle 7
Reading the WSLE data .....................................
Virtual Address Age Locked ReferenceCount c0600203 0 1 1 c0601203 0 1 1 c0602203 0 1 1 c0603203 0 1 1 c0604203 0 1 1 c0882203 0 1 1 c0883203 0 1 1
...
所以现在看来应该是这个进程的工作集数据有错误,这恰好与驱动程序曾经修改PTE导致问题相符...