JIURL玩玩Win2k内存篇 VAD

在程序中我们可以使用 VirtualAlloc 在用户地址空间(4G地址空间中的低2G)中申请(保留或者提交)指定地址和大小的一段地址空间。那么系统如何知道指定的这段地址空间是不是已经被分配(保留或者提交)。对于指定地址空间是否已经被提交了物理内存,可以通过页目录和页表来判断,不过这样做很麻烦。而对于指定地址空间是否已经被保留,通过页目录和页表没有办法判断。Win2k 中使用 VAD 来解决这个问题。

    VAD 是 virtual address descriptor 的缩写,即 虚拟地址描述符。一个VAD保存一段被分配地址空间的信息,所有VAD组成一个二叉排序树来描述用户地址空间的情况。每个进程有自己的用户地址空间,所以每个进程也有自己的VAD二叉排序树。VAD二叉排序树的根的地址保存在进程结构 EPROCESS 中。对于 Win2k build 2195 来说,VadRoot 在 EPROCESS 偏移 +194 处。

    二叉排序树是一棵二叉树,每个节点最多有左右两个子树。左子树上的所有节点的值均小于它的根节点的值,右子树上所有节点的值均大于它的根节点的值。使用二叉排序树是为了保证查找速度。

VAD 结构定义如下

typedef struct _VAD_HEADER { /*00*/ PVOID StartVPN; /*04*/ PVOID EndVPN; /*08*/ _VAD_HEADER* ParentLink; /*0C*/ _VAD_HEADER* LeftLink; /*10*/ _VAD_HEADER* RightLink; /*14*/ ULONG CommitCharge : 20; /*14*/ ULONG Flags : 12; /*18*/ PVOID ControlArea; /*1C*/ PVOID FirstProtoPte; /*20*/ PVOID LastPTE; /*24*/ ULONG Unknown; /*28*/ LIST_ENTRY Secured; /*30*/ } VAD_HEADER, *PVAD_HEADER; StartVPN 这个VAD所描述的那段地址空间的开始虚拟页号 EndVPN 这个VAD所描述的那段地址空间的结束虚拟页号 CommitCharge 提交的物理页数

VAD 结构的一些其他字段,会在其他用到的地方做更多的介绍。

下面我们使用 kd 看一个实际的例子 // 系统进程的PID为8 kd> !process 8 0 !process 8 0 Searching for Process with Cid == 8 PROCESS 8141e020 SessionId: 0 Cid: 0008 Peb: 00000000 ParentCid: 0000 DirBase: 00030000 ObjectTable: 81452a68 TableSize: 106. Image: System // 系统进程的 EPROCESS 地址为 8141e020 // 对于 Win2k build 2195 来说 VadRoot 在 EPROCESS 偏移 +194 处 kd> !strct eprocess 8141e020 ... +194 void *VadRoot = 8141BB48 ... // VadRoot 地址为 8141BB48 kd> !vad 8141BB48 !vad 8141BB48 VAD level start end commit 8141bb48 ( 0) 10 42 0 Mapped READWRITE 810f8208 ( 2) 60 60 1 Private READWRITE 810ba508 ( 3) 70 70 0 Mapped READWRITE 813e6ca8 ( 1) 77f80 77ff8 3 Mapped Exe EXECUTE_WRITECOPY Total VADs: 4 average level: 2 maximum depth: 3 // 我们来看一下根节点的VAD中的具体内容,根节点在 8141bb48 处 kd> dd 8141bb48 l 30/4 dd 8141bb48 l 30/4 8141bb48 00000010 00000042 00000000 00000000 8141bb58 813e6ca8 04000000 8141bae8 e10089e0 8141bb68 e1008aa8 40000000 00000000 00000000 需要指出的是,只有系统进程的用户地址空间会这么简单,一般的程序的VAD二叉树都会有20多个节点。 下面我们再来看一个例子,这是我写的一个程序 JiurlVadSee 的输出,它可以列出指定进程的VAD二叉树。系 统中运行进程的ID可以通过 Windows任务管理器 获得。 ProcessId(Decimal): 556 0 - Min Information 1 - Max Information Type: 1 Vad Level StartVPN EndVPN Commit Flags 0x82b05928 [ 1] 0x00010 0x00010 1 c40 StartVirtualAddress: 0x00010000 EndVirtualAddress: 0x00010fff /*00*/ PVOID StartVPN; = 0x00010 /*04*/ PVOID EndVPN; = 0x00010 /*08*/ _VAD_HEADER* ParentLink; = 0x810482a8 /*0C*/ _VAD_HEADER* LeftLink; = 0x00000000 /*10*/ _VAD_HEADER* RightLink; = 0x82b05da8 /*14*/ ULONG CommitCharge:20; = 0x00001 /*14*/ ULONG Flags :12; = 0xc40 /*18*/ PVOID ControlArea; = 0x02018001 /*1C*/ PVOID FirstProtoPte; = 0x20646156 /*20*/ PVOID LastPTE; = 0x00005240 /*24*/ ULONG Unknown; = 0x0000533f /*28*/ LIST_ENTRY Secured; = 0x8106d2e8 0x00000000 0x82b05da8 [ 2] 0x00020 0x00020 1 c40 StartVirtualAddress: 0x00020000 EndVirtualAddress: 0x00020fff /*00*/ PVOID StartVPN; = 0x00020 /*04*/ PVOID EndVPN; = 0x00020 /*08*/ _VAD_HEADER* ParentLink; = 0x82b05928 /*0C*/ _VAD_HEADER* LeftLink; = 0x00000000 /*10*/ _VAD_HEADER* RightLink; = 0x8269a808 /*14*/ ULONG CommitCharge:20; = 0x00001 /*14*/ ULONG Flags :12; = 0xc40 /*18*/ PVOID ControlArea; = 0x02018001 /*1C*/ PVOID FirstProtoPte; = 0x20646156 /*20*/ PVOID LastPTE; = 0x0007ffd8 /*24*/ ULONG Unknown; = 0x0007ffd8 /*28*/ LIST_ENTRY Secured; = 0x84875a28 0x00000000 0x8269a808 [ 3] 0x00030 0x0012f 3 840 StartVirtualAddress: 0x00030000 EndVirtualAddress: 0x0012ffff /*00*/ PVOID StartVPN; = 0x00030 /*04*/ PVOID EndVPN; = 0x0012f /*08*/ _VAD_HEADER* ParentLink; = 0x82b05da8 /*0C*/ _VAD_HEADER* LeftLink; = 0x00000000 /*10*/ _VAD_HEADER* RightLink; = 0x826f9ba8 /*14*/ ULONG CommitCharge:20; = 0x00003 /*14*/ ULONG Flags :12; = 0x840 /*18*/ PVOID ControlArea; = 0x02018001 /*1C*/ PVOID FirstProtoPte; = 0x20646156 /*20*/ PVOID LastPTE; = 0x000777f0 /*24*/ ULONG Unknown; = 0x000777fd /*28*/ LIST_ENTRY Secured; = 0x82a65c28 0x82f11e88 0x826f9ba8 [ 4] 0x00130 0x0022f 4 840 StartVirtualAddress: 0x00130000 EndVirtualAddress: 0x0022ffff /*00*/ PVOID StartVPN; = 0x00130 /*04*/ PVOID EndVPN; = 0x0022f /*08*/ _VAD_HEADER* ParentLink; = 0x8269a808 /*0C*/ _VAD_HEADER* LeftLink; = 0x00000000 /*10*/ _VAD_HEADER* RightLink; = 0x810b7fc8 /*14*/ ULONG CommitCharge:20; = 0x00004 /*14*/ ULONG Flags :12; = 0x840 /*18*/ PVOID ControlArea; = 0x02018001 /*1C*/ PVOID FirstProtoPte; = 0x45746146 /*20*/ PVOID LastPTE; = 0x834e40e8 /*24*/ ULONG Unknown; = 0x8351dde8 /*28*/ LIST_ENTRY Secured; = 0x00000000 0x00000000 0x810b7fc8 [ 5] 0x00230 0x0023f 0 040 StartVirtualAddress: 0x00230000 EndVirtualAddress: 0x0023ffff /*00*/ PVOID StartVPN; = 0x00230 /*04*/ PVOID EndVPN; = 0x0023f /*08*/ _VAD_HEADER* ParentLink; = 0x826f9ba8 /*0C*/ _VAD_HEADER* LeftLink; = 0x00000000 /*10*/ _VAD_HEADER* RightLink; = 0x8109d6c8 /*14*/ ULONG CommitCharge:20; = 0x00000 /*14*/ ULONG Flags :12; = 0x040 /*18*/ PVOID ControlArea; = 0x86346148 /*1C*/ PVOID FirstProtoPte; = 0xe33eda40 /*20*/ PVOID LastPTE; = 0xe33eda7c /*24*/ ULONG Unknown; = 0x00000000 /*28*/ LIST_ENTRY Secured; = 0x00000000 0x00000000 0x8109d6c8 [ 6] 0x00240 0x00255 0 010 StartVirtualAddress: 0x00240000 EndVirtualAddress: 0x00255fff /*00*/ PVOID StartVPN; = 0x00240 /*04*/ PVOID EndVPN; = 0x00255 /*08*/ _VAD_HEADER* ParentLink; = 0x810b7fc8 /*0C*/ _VAD_HEADER* LeftLink; = 0x00000000 /*10*/ _VAD_HEADER* RightLink; = 0x82b057a8 /*14*/ ULONG CommitCharge:20; = 0x00000 /*14*/ ULONG Flags :12; = 0x010 /*18*/ PVOID ControlArea; = 0x810e83c8 /*1C*/ PVOID FirstProtoPte; = 0xe2beb380 /*20*/ PVOID LastPTE; = 0xe2beb3d4 /*24*/ ULONG Unknown; = 0x80000000 /*28*/ LIST_ENTRY Secured; = 0x00000000 0x00000000 0x82b057a8 [ 7] 0x00260 0x0028e 0 010 StartVirtualAddress: 0x00260000 EndVirtualAddress: 0x0028efff /*00*/ PVOID StartVPN; = 0x00260 /*04*/ PVOID EndVPN; = 0x0028e /*08*/ _VAD_HEADER* ParentLink; = 0x8109d6c8 /*0C*/ _VAD_HEADER* LeftLink; = 0x00000000 /*10*/ _VAD_HEADER* RightLink; = 0x82b05768 /*14*/ ULONG CommitCharge:20; = 0x00000 /*14*/ ULONG Flags :12; = 0x010 /*18*/ PVOID ControlArea; = 0x810e7be8 /*1C*/ PVOID FirstProtoPte; = 0xe2becd20 /*20*/ PVOID LastPTE; = 0xe2becdd8 /*24*/ ULONG Unknown; = 0x80000000 /*28*/ LIST_ENTRY Secured; = 0x00000000 0x00000000 0x82b05768 [ 8] 0x00290 0x002d0 0 010 StartVirtualAddress: 0x00290000 EndVirtualAddress: 0x002d0fff /*00*/ PVOID StartVPN; = 0x00290 /*04*/ PVOID EndVPN; = 0x002d0 /*08*/ _VAD_HEADER* ParentLink; = 0x82b057a8 /*0C*/ _VAD_HEADER* LeftLink; = 0x00000000 /*10*/ _VAD_HEADER* RightLink; = 0x82b05728 /*14*/ ULONG CommitCharge:20; = 0x00000 /*14*/ ULONG Flags :12; = 0x010 /*18*/ PVOID ControlArea; = 0x810e7828 /*1C*/ PVOID FirstProtoPte; = 0xe2bed600 /*20*/ PVOID LastPTE; = 0xe2bed700 /*24*/ ULONG Unknown; = 0x80000000 /*28*/ LIST_ENTRY Secured; = 0x00000000 0x00000000 0x82b05728 [ 9] 0x002e0 0x002e3 0 010 StartVirtualAddress: 0x002e0000 EndVirtualAddress: 0x002e3fff /*00*/ PVOID StartVPN; = 0x002e0 /*04*/ PVOID EndVPN; = 0x002e3 /*08*/ _VAD_HEADER* ParentLink; = 0x82b05768 /*0C*/ _VAD_HEADER* LeftLink; = 0x00000000 /*10*/ _VAD_HEADER* RightLink; = 0x82b056e8 /*14*/ ULONG CommitCharge:20; = 0x00000 /*14*/ ULONG Flags :12; = 0x010 /*18*/ PVOID ControlArea; = 0x810e7648 /*1C*/ PVOID FirstProtoPte; = 0xe139cfa0 /*20*/ PVOID LastPTE; = 0xe139cfac /*24*/ ULONG Unknown; = 0x80000000 /*28*/ LIST_ENTRY Secured; = 0x00000000 0x00000000 0x82b056e8 [10] 0x002f0 0x00330 0 010 StartVirtualAddress: 0x002f0000 EndVirtualAddress: 0x00330fff /*00*/ PVOID StartVPN; = 0x002f0 /*04*/ PVOID EndVPN; = 0x00330 /*08*/ _VAD_HEADER* ParentLink; = 0x82b05728 /*0C*/ _VAD_HEADER* LeftLink; = 0x00000000 /*10*/ _VAD_HEADER* RightLink; = 0x81070188 /*14*/ ULONG CommitCharge:20; = 0x00000 /*14*/ ULONG Flags :12; = 0x010 /*18*/ PVOID ControlArea; = 0x810e7228 /*1C*/ PVOID FirstProtoPte; = 0xe2beebe0 /*20*/ PVOID LastPTE; = 0xe2beece0 /*24*/ ULONG Unknown; = 0x00000000 /*28*/ LIST_ENTRY Secured; = 0x00000000 0x00000000 0x81070188 [11] 0x00380 0x0038f 6 840 StartVirtualAddress: 0x00380000 EndVirtualAddress: 0x0038ffff /*00*/ PVOID StartVPN; = 0x00380 /*04*/ PVOID EndVPN; = 0x0038f /*08*/ _VAD_HEADER* ParentLink; = 0x82b056e8 /*0C*/ _VAD_HEADER* LeftLink; = 0x00000000 /*10*/ _VAD_HEADER* RightLink; = 0x00000000 /*14*/ ULONG CommitCharge:20; = 0x00006 /*14*/ ULONG Flags :12; = 0x840 /*18*/ PVOID ControlArea; = 0x02018001 /*1C*/ PVOID FirstProtoPte; = 0x6d665346 /*20*/ PVOID LastPTE; = 0x00000001 /*24*/ ULONG Unknown; = 0x00000000 /*28*/ LIST_ENTRY Secured; = 0x00000000 0x00040001 0x810482a8 [ 0] 0x00400 0x00405 2 071 StartVirtualAddress: 0x00400000 EndVirtualAddress: 0x00405fff /*00*/ PVOID StartVPN; = 0x00400 /*04*/ PVOID EndVPN; = 0x00405 /*08*/ _VAD_HEADER* ParentLink; = 0x00000000 /*0C*/ _VAD_HEADER* LeftLink; = 0x82b05928 /*10*/ _VAD_HEADER* RightLink; = 0x83040348 /*14*/ ULONG CommitCharge:20; = 0x00002 /*14*/ ULONG Flags :12; = 0x071 /*18*/ PVOID ControlArea; = 0x8401f428 /*1C*/ PVOID FirstProtoPte; = 0xe2beb260 /*20*/ PVOID LastPTE; = 0xfffffffc /*24*/ ULONG Unknown; = 0x40000000 /*28*/ LIST_ENTRY Secured; = 0x00000000 0x00000000 0x86348b68 [ 3] 0x00410 0x0050f 8 840 StartVirtualAddress: 0x00410000 EndVirtualAddress: 0x0050ffff /*00*/ PVOID StartVPN; = 0x00410 /*04*/ PVOID EndVPN; = 0x0050f /*08*/ _VAD_HEADER* ParentLink; = 0x810bba08 /*0C*/ _VAD_HEADER* LeftLink; = 0x00000000 /*10*/ _VAD_HEADER* RightLink; = 0x8109de08 /*14*/ ULONG CommitCharge:20; = 0x00008 /*14*/ ULONG Flags :12; = 0x840 /*18*/ PVOID ControlArea; = 0x01018001 /*1C*/ PVOID FirstProtoPte; = 0x53646156 /*20*/ PVOID LastPTE; = 0x00000010 /*24*/ ULONG Unknown; = 0x00000010 /*28*/ LIST_ENTRY Secured; = 0x810745e8 0x00000000 0x8109de08 [ 4] 0x00510 0x00511 0 010 StartVirtualAddress: 0x00510000 EndVirtualAddress: 0x00511fff /*00*/ PVOID StartVPN; = 0x00510 /*04*/ PVOID EndVPN; = 0x00511 /*08*/ _VAD_HEADER* ParentLink; = 0x86348b68 /*0C*/ _VAD_HEADER* LeftLink; = 0x00000000 /*10*/ _VAD_HEADER* RightLink; = 0x00000000 /*14*/ ULONG CommitCharge:20; = 0x00000 /*14*/ ULONG Flags :12; = 0x010 /*18*/ PVOID ControlArea; = 0x810e7a08 /*1C*/ PVOID FirstProtoPte; = 0xe139cf40 /*20*/ PVOID LastPTE; = 0xe139cf44 /*24*/ ULONG Unknown; = 0x80000000 /*28*/ LIST_ENTRY Secured; = 0x00000000 0x00000000 0x810bba08 [ 2] 0x77e60 0x77f34 2 071 StartVirtualAddress: 0x77e60000 EndVirtualAddress: 0x77f34fff /*00*/ PVOID StartVPN; = 0x77e60 /*04*/ PVOID EndVPN; = 0x77f34 /*08*/ _VAD_HEADER* ParentLink; = 0x83040348 /*0C*/ _VAD_HEADER* LeftLink; = 0x86348b68 /*10*/ _VAD_HEADER* RightLink; = 0x00000000 /*14*/ ULONG CommitCharge:20; = 0x00002 /*14*/ ULONG Flags :12; = 0x071 /*18*/ PVOID ControlArea; = 0x810f4f28 /*1C*/ PVOID FirstProtoPte; = 0xe17bfc40 /*20*/ PVOID LastPTE; = 0xfffffffc /*24*/ ULONG Unknown; = 0x40000000 /*28*/ LIST_ENTRY Secured; = 0x00000000 0x00000000 0x83040348 [ 1] 0x77f80 0x77ff8 3 071 StartVirtualAddress: 0x77f80000 EndVirtualAddress: 0x77ff8fff /*00*/ PVOID StartVPN; = 0x77f80 /*04*/ PVOID EndVPN; = 0x77ff8 /*08*/ _VAD_HEADER* ParentLink; = 0x810482a8 /*0C*/ _VAD_HEADER* LeftLink; = 0x810bba08 /*10*/ _VAD_HEADER* RightLink; = 0x8106a248 /*14*/ ULONG CommitCharge:20; = 0x00003 /*14*/ ULONG Flags :12; = 0x071 /*18*/ PVOID ControlArea; = 0x813e6a08 /*1C*/ PVOID FirstProtoPte; = 0xe13c9520 /*20*/ PVOID LastPTE; = 0xfffffffc /*24*/ ULONG Unknown; = 0x40000000 /*28*/ LIST_ENTRY Secured; = 0x00000000 0x00000000 0x810b7e48 [ 3] 0x7f6f0 0x7f7ef 0 034 StartVirtualAddress: 0x7f6f0000 EndVirtualAddress: 0x7f7effff /*00*/ PVOID StartVPN; = 0x7f6f0 /*04*/ PVOID EndVPN; = 0x7f7ef /*08*/ _VAD_HEADER* ParentLink; = 0x8106a248 /*0C*/ _VAD_HEADER* LeftLink; = 0x00000000 /*10*/ _VAD_HEADER* RightLink; = 0x00000000 /*14*/ ULONG CommitCharge:20; = 0x00000 /*14*/ ULONG Flags :12; = 0x034 /*18*/ PVOID ControlArea; = 0x810e8ca8 /*1C*/ PVOID FirstProtoPte; = 0xe2be6be0 /*20*/ PVOID LastPTE; = 0xe2be6fdc /*24*/ ULONG Unknown; = 0x01000000 /*28*/ LIST_ENTRY Secured; = 0x00000000 0x00000000 0x8106a248 [ 2] 0x7ffa0 0x7ffd2 0 014 StartVirtualAddress: 0x7ffa0000 EndVirtualAddress: 0x7ffd2fff /*00*/ PVOID StartVPN; = 0x7ffa0 /*04*/ PVOID EndVPN; = 0x7ffd2 /*08*/ _VAD_HEADER* ParentLink; = 0x83040348 /*0C*/ _VAD_HEADER* LeftLink; = 0x810b7e48 /*10*/ _VAD_HEADER* RightLink; = 0x81fd5708 /*14*/ ULONG CommitCharge:20; = 0x00000 /*14*/ ULONG Flags :12; = 0x014 /*18*/ PVOID ControlArea; = 0x8141bae8 /*1C*/ PVOID FirstProtoPte; = 0xe10089e0 /*20*/ PVOID LastPTE; = 0xe1008aa8 /*24*/ ULONG Unknown; = 0x41000000 /*28*/ LIST_ENTRY Secured; = 0x00000000 0x00000000 0x82b052a8 [ 4] 0x7ffde 0x7ffde 1 c64 StartVirtualAddress: 0x7ffde000 EndVirtualAddress: 0x7ffdefff /*00*/ PVOID StartVPN; = 0x7ffde /*04*/ PVOID EndVPN; = 0x7ffde /*08*/ _VAD_HEADER* ParentLink; = 0x81fd5708 /*0C*/ _VAD_HEADER* LeftLink; = 0x00000000 /*10*/ _VAD_HEADER* RightLink; = 0x00000000 /*14*/ ULONG CommitCharge:20; = 0x00001 /*14*/ ULONG Flags :12; = 0xc64 /*18*/ PVOID ControlArea; = 0x00000000 /*1C*/ PVOID FirstProtoPte; = 0x00000002 /*20*/ PVOID LastPTE; = 0xe13d08a0 /*24*/ ULONG Unknown; = 0x12000000 /*28*/ LIST_ENTRY Secured; = 0x7ffde000 0x7ffdefff 0x81fd5708 [ 3] 0x7ffdf 0x7ffdf 1 c64 StartVirtualAddress: 0x7ffdf000 EndVirtualAddress: 0x7ffdffff /*00*/ PVOID StartVPN; = 0x7ffdf /*04*/ PVOID EndVPN; = 0x7ffdf /*08*/ _VAD_HEADER* ParentLink; = 0x8106a248 /*0C*/ _VAD_HEADER* LeftLink; = 0x82b052a8 /*10*/ _VAD_HEADER* RightLink; = 0x00000000 /*14*/ ULONG CommitCharge:20; = 0x00001 /*14*/ ULONG Flags :12; = 0xc64 /*18*/ PVOID ControlArea; = 0x00000000 /*1C*/ PVOID FirstProtoPte; = 0x00000002 /*20*/ PVOID LastPTE; = 0xffffffff /*24*/ ULONG Unknown; = 0x12000000 /*28*/ LIST_ENTRY Secured; = 0x7ffdf000 0x7ffdffff 遍历VAD二叉排序树的一种方法

为了使函数简单清楚一些,没有用 程序JiurlVadSee 中的函数做例子。下面的函数需要放在驱动程序(VAD二叉树在系统地址空间中,ring0程序才能访问)中,DbgPrint()的输出,可以使用 www.sysinternals.com 的工具 Dbgview 查看。 void JiurlDumpVad(PVAD_HEADER pVad, ULONG level) {   if(pVad->LeftLink!=NULL)  {     level++;     JiurlDumpVad(pVad->LeftLink,level);     level--;   }   DbgPrint("0x%08x [%2d] 0x%08x 0x%08x %7d %03x/n",             pVad,level,pVad->StartVPN,pVad->EndVPN,             pVad->CommitCharge,pVad->Flags);   if(pVad->RightLink!=NULL)   {     level++;     JiurlDumpVad(pVad->RightLink,level);     level--;   } } 通过递归,先左子树,再根节点,再右子树。这样会按VAD代表地址段的先后顺序输出。 程序申请内存,使用内存,释放内存 VAD 二叉树的变化 运行一个小程序分别申请,使用,释放内存,每步之间使用 getch() 停住,观察该进程 VAD 二叉树 和 该进程的页目录和页表的变化。我们在 地址空间中的 0x60000000 处进行试验。 1 getch(); 0x60000000 没有相应的VAD,观察页目录,对应PDE无效 c0300600: 00000000 2 VirtualAlloc((void*)0x60000000,0x3000,MEM_RESERVE,PAGE_READONLY); 从0x60000000 开始处,保留3页。  Vad Level StartVPN EndVPN Commit Flags 0x830ac008 [ 5] 0x60000 0x60002 0 810 出现了相应的VAD,可以看到保留了3页,但是并没有提交页 观察页目录,对应PDE无效 c0300600: 00000000 尝试 char buf[100]; memcpy(buf,(void*)0x60000000,100); 将会导致 Access violation 3 VirtualAlloc((void*)0x60000000,0x2000,MEM_COMMIT,PAGE_READONLY); 从0x60000000 开始处,提交2页。(注意,使用MEM_COMMIT之前,必须MEM_RESERVE) Vad Level StartVPN EndVPN Commit Flags 0x830ac008 [ 5] 0x60000 0x60002 2 810 刚才出现的VAD中已经表明提交了2页 观察页目录,对应PDE有效了 c0300600: 03cbc067 对应PTE仍然无效 c0180000: 00000020 00000020 00000000 4 char buf[100]; memcpy(buf,(void*)0x60000000,100); 读取 0x60000000 开始处的100字节,读取成功。 Vad Level StartVPN EndVPN Commit Flags 0x849f3328 [ 5] 0x60000 0x60002 2 810 对应PDE c0300600: 03cbc067 对应PTE c0180000: 00559025 00000020 00000000 这时被访问的一页对应的PTE有效了 5 VirtualFree((void*)0x60000000,0,MEM_RELEASE); 0x60000000 相应的VAD消失,观察页目录,对应PDE又变成无效 c0300600: 00000000 我们可以看到。保留地址空间,只建立一个相应的VAD。提交页只是设置相应的页目录项和页表项(有可能需要给页表分配物理内存)。只有当访问该地址的时候才真正给该地址映射物理页。Win2k 使用的这种思想叫做lazy-evaluation algorithm ,可以提高内存的使用效率。 结构和标志位的定义 在 www.insidewindows.inf 的非官方的 ntifs.h 文件中,对 VAD_HEADER 定义如下 typedef struct _VAD_HEADER { PVOID StartVPN; PVOID EndVPN; PVAD_HEADER ParentLink; PVAD_HEADER LeftLink; PVAD_HEADER RightLink; ULONG Flags; // LSB = CommitCharge PVOID ControlArea; PVOID FirstProtoPte; PVOID LastPTE; ULONG Unknown; LIST_ENTRY Secured; } VAD_HEADER, *PVAD_HEADER; 在对VAD的分析过程中,发现其中 Flags 字段,定义的不是很清楚。于是写了一个驱动程序和一个应用程序,应用程序负责接收输入的一个DWORD值,然后和驱动程序通信,把这个值传给驱动程序。驱动程序负责申请一段在系统地址空间中的内存,大小和 VAD_HEADER 的大小一样,接收到应用程序传来的DWORD之后,放在 Flags 的位置上,其他位置的值是固定的一些设计好的值。并且用DbgPrint() 输出这段内存的首地址。得到了这段空间的首地址之后,对这个地址使用 kd 的 !vad 命令,看命令的结果。在应用程序中输入新的值。再用!vad命令看输出结果。通过几次比较之后发现,VAD中 0x14 处的这个DWORD 的低20位用来保存提交的页数(20bit最大可以表示提交1024*1024页,对应4GB大小),高12位作为标志。通过这种方法,得到了部分标志位的定义(由于 !vad 可能并不解释一些标志位)。 #define MEM_IMAGE 0x001 // 指明页映射的是一个 image section #define MEM_PRIVATE 0x800 // 指明页是私有的(不和别的进程共享) #define PAGE_READONLY 0x010 #define PAGE_READWRITE 0x040 #define PAGE_WRITECOPY 0x050 // copy-on-write #define PAGE_EXECUTE 0x020 #define PAGE_EXECUTE_READ 0x030 #define PAGE_EXECUTE_READWRITE 0x060 #define PAGE_EXECUTE_WRITECOPY 0x070 这些标志和 VirtualAlloc 中的同名的相对应。使用 VirtualAlloc 指定标志 PAGE_READONLY 申请了一段内存 ,使用 !vad 观察该进程的这段内存对应的VAD,发现 Flags:12 的值的确是 0x010 ,验证了上面定义是正确的。如果想得到所有的标志定义,可以使用这种方法。也可以分析相关函数的汇编代码。

进程地址空间中的物理页

    通过一个进程的页目录和页表,我们可以知道这个进程地址空间的哪些部分映射了物理页。我写了一个叫 JiurlValidPageSee 的程序,可以得到指定进程的4GB地址空间的哪些部分映射了物理页。需要注意的是,一个物理页可以被以映射到虚拟地址空间的多个页中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值