JIURL玩玩Win2k内存篇 VAD

来源:互联网 发布:python系统运维常用库 编辑:程序博客网 时间:2024/05/29 14:09

JIURL玩玩Win2k内存篇 VAD

作者: JIURL

                主页: http://jiurl.yeah.net

    日期: 2003-7-30


    在程序中我们可以使用 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地址空间的哪些部分映射了物理页。需要注意的是,一个物理页可以被以映射到虚拟地址空间的多个页中。

欢迎交流,欢迎交朋友,
欢迎访问 http://jiurl.yeah.net http://jiurl.cosoft.org.cn/forum


下载 JiurlVadSee 可执行文件及源程序
下载 JiurlValidPageSee 可执行文件及源程序

 

原创粉丝点击