Win7 x64 内存管理(一)

一、x64 地址空间布局

1.1 64 位虚拟地址真的有 16EB 吗?

理论上:

cpp 复制代码
64 位地址 → 2^64 = 16 EB

但这是"理论值"。

实际上,AMD 和 Intel 的 x64 处理器:

只实现了 48 位有效虚拟地址

也就是说:

cpp 复制代码
2^48 = 256 TB

这 256TB 叫做:

Canonical Virtual Address Space

1.2 Canonical Address 规则

处理器要求:

cpp 复制代码
第 48--63 位必须等于第 47 位

也就是:

  • 若 bit47 = 0 → 高 16 位全 0

  • 若 bit47 = 1 → 高 16 位全 1

因此 256TB 被划分成两个 128TB 区域:

cpp 复制代码
低半区:
0000 0000 0000 0000
        ↓
0000 7FFF FFFF FFFF   (128TB)

高半区:
FFFF 8000 0000 0000
        ↓
FFFF FFFF FFFF FFFF   (128TB)

中间是非法空洞。

1.3 Windows 7 x64 实际使用多少?

地址空间限制为 16TB

其中 8TB 给用户,8TB 给内核

也就是说:

cpp 复制代码
Windows 实际只使用 44 位
2^44 = 16 TB

二、VAD ------ Windows 进程虚拟地址空间的真正管理者

在 win7 x64 下,每个进程拥有:

cpp 复制代码
8TB 用户空间

问题来了:

  • Windows 如何知道哪些区域已分配?

  • 哪些是 Private?

  • 哪些是 Section 映射?

  • 哪些是只读?

  • 哪些支持写拷贝?

  • 哪些已提交(Commit)?

答案是:

VAD 树。

2.1 什么是 VAD?

VAD 全称:

cpp 复制代码
Virtual Address Descriptor

本质:

描述一段连续虚拟地址区间的内核数据结构

换句话说:

VAD 用来描述进程地址空间中一段连续的虚拟地址区间,以及该区间的类型、保护、提交和映射信息。

2.2 VAD 存在哪里?

我们可以使用windbg,!vad命令查看特定进程的VAD。首先使用!process命令找到VAD树的根地址,随后将改地址提供给!vad命令。

cpp 复制代码
kd> !process 0 0 
**** NT ACTIVE PROCESS DUMP ****
...
PROCESS fffffa8003d51060
    SessionId: 1  Cid: 0378    Peb: 7fffffd4000  ParentCid: 0b04
    DirBase: 31801000  ObjectTable: fffff8a0079601e0  HandleCount:  12.
    Image: Project1.exe
...


kd> dt _eprocess fffffa8003d51060
nt!_EPROCESS
...
   +0x448 VadRoot          : _MM_AVL_TABLE
...
cpp 复制代码
kd> !vad fffffa8003d51060+0x448
VAD             Level         Start             End              Commit
fffffa8003d514a8  0               0               0            5381 Mapped       NO_ACCESS          
fffffa8002bdfd60  5              10              1f               0 Mapped       READWRITE          Pagefile section, shared commit 0x10
fffffa8003dac7e0  4              20              2f               0 Mapped       READWRITE          Pagefile section, shared commit 0x10
fffffa8003c5ff80  5              30              33               0 Mapped       READONLY           Pagefile section, shared commit 0x4
fffffa8003192920  3              40              40               0 Mapped       READONLY           Pagefile section, shared commit 0x1
fffffa8003e00160  4              50              50               1 Private      READWRITE          
fffffa8002dee160  2              b0             1af               6 Private      READWRITE          
fffffa8002bfc110  5             1b0             216               0 Mapped       READONLY           \Windows\System32\locale.nls
fffffa8003c9a2c0  4             3a0             49f              32 Private      READWRITE          
fffffa8001c54aa0  5           779a0           77abe               4 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\kernel32.dll
fffffa8002009190  3           77bc0           77d5e              15 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\ntdll.dll
fffffa8001b8bbe0  5           7efe0           7f0df               0 Mapped       READONLY           Pagefile section, shared commit 0x5
fffffa8003cf1dd0  4           7f0e0           7ffdf               0 Private      READONLY           
fffffa8001be81b0  1           7ffe0           7ffef 2251799813685247 Private      READONLY           
fffffa8003eb1510  5          13f6d0          13f94f             176 Mapped  Exe  EXECUTE_WRITECOPY  \Users\abc\Desktop\Project1.exe
fffffa8003e12010  4        7fef9f40        7fef9f42               0 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\api-ms-win-core-synch-l1-2-0.dll
fffffa8001a915e0  5        7fefd980        7fefd9d6               5 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\apphelp.dll
fffffa8003c29e40  3        7fefde50        7fefdeb9               3 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\KernelBase.dll
fffffa8003b325f0  4        7feffec0        7feffec0               0 Mapped  Exe  EXECUTE_WRITECOPY  \Windows\System32\apisetschema.dll
fffffa8002d26be0  2        7fffffa0        7fffffd2               0 Mapped       READONLY           Pagefile section, shared commit 0x33
fffffa8003526e70  3        7fffffd4        7fffffd4               1 Private      READWRITE          
fffffa8003b5af70  4        7fffffde        7fffffdf               2 Private      READWRITE          

Total VADs: 22, average level: 4, maximum depth: 5
Total private commit: 0x80000000015f9 pages (9007199254763492 KB)
Total shared commit:  0x5d pages (372 KB)

2.3 MM_AVL_TABLE

在 Win7 x64 中,EPROCESS.VadRoot 并不是一个 _MMVAD* 指针,而是一个:

cpp 复制代码
_MM_AVL_TABLE

定义如下:

cpp 复制代码
//0x40 bytes (sizeof)
struct _MM_AVL_TABLE
{
    struct _MMADDRESS_NODE BalancedRoot;                                    //0x0
    ULONGLONG DepthOfTree:5;                                                //0x28
    ULONGLONG Unused:3;                                                     //0x28
    ULONGLONG NumberGenericTableElements:56;                                //0x28
    VOID* NodeHint;                                                         //0x30
    VOID* NodeFreeHint;                                                     //0x38
}; 

1️⃣BalancedRoot

cpp 复制代码
struct _MMADDRESS_NODE BalancedRoot
cpp 复制代码
//0x28 bytes (sizeof)
struct _MMADDRESS_NODE
{
    union
    {
        LONGLONG Balance:2;                                                 //0x0
        struct _MMADDRESS_NODE* Parent;                                     //0x0
    } u1;                                                                   //0x0
    struct _MMADDRESS_NODE* LeftChild;                                      //0x8
    struct _MMADDRESS_NODE* RightChild;                                     //0x10
    ULONGLONG StartingVpn;                                                  //0x18
    ULONGLONG EndingVpn;                                                    //0x20
}; 
cpp 复制代码
_MMVAD_SHORT / _MMVAD / _MMVAD_LONG 的前半部分布局
与 _MMADDRESS_NODE 完全兼容

也就是说:

cpp 复制代码
struct _MMVAD
{
    // 前 0x28 字节与 _MMADDRESS_NODE 一致
}

所以 !vad eprocess+0x448 仍然可以工作

2️⃣ DepthOfTree : 3

树的深度(0~31)。AVL 树高度一般很小,所以 5 bit 足够。

3️⃣ NumberGenericTableElements : 56

树中节点数量(这里就是 VAD 数量,或者说 address node 数量)。

4️⃣ NodeHint / NodeFreeHint

这两个是 查找/插入的缓存提示指针:

  • NodeHint:上次查找/插入附近的节点,用于加速下一次查找(比如按地址顺序分配时,很多操作都发生在相邻区域)

  • NodeFreeHint:与"空闲区搜索/分配"相关的提示(不同版本用途略有差异)

这属于性能优化字段,不影响树的逻辑正确性。

2.4 _MMVAD、_MMVAD_SHORT、_MMVAD_LONG 结构解析

在 Windows 7 x64中,VAD 节点并非只有一种结构。

内核根据"该虚拟地址区间是否需要附加元数据",使用两种不同大小的结构:

  • _MMVAD_SHORT

  • _MMVAD_LONG

而 _MMVAD 是两者的公共头部。

cpp 复制代码
//0x78 bytes (sizeof)
struct _MMVAD
{
    union
    {
        LONGLONG Balance:2;                                                 //0x0
        struct _MMVAD* Parent;                                              //0x0
    } u1;                                                                   //0x0
    struct _MMVAD* LeftChild;                                               //0x8
    struct _MMVAD* RightChild;                                              //0x10
    ULONGLONG StartingVpn;                                                  //0x18
    ULONGLONG EndingVpn;                                                    //0x20
    union
    {
        ULONGLONG LongFlags;                                                //0x28
        struct _MMVAD_FLAGS VadFlags;                                       //0x28
    } u;                                                                    //0x28
    struct _EX_PUSH_LOCK PushLock;                                          //0x30
    union
    {
        ULONGLONG LongFlags3;                                               //0x38
        struct _MMVAD_FLAGS3 VadFlags3;                                     //0x38
    } u5;                                                                   //0x38
    union
    {
        ULONG LongFlags2;                                                   //0x40
        struct _MMVAD_FLAGS2 VadFlags2;                                     //0x40
    } u2;                                                                   //0x40
    union
    {
        struct _SUBSECTION* Subsection;                                     //0x48
        struct _MSUBSECTION* MappedSubsection;                              //0x48
    };
    struct _MMPTE* FirstPrototypePte;                                       //0x50
    struct _MMPTE* LastContiguousPte;                                       //0x58
    struct _LIST_ENTRY ViewLinks;                                           //0x60
    struct _EPROCESS* VadsProcess;                                          //0x70
}; 



//0x90 bytes (sizeof)
struct _MMVAD_LONG
{
    union
    {
        LONGLONG Balance:2;                                                 //0x0
        struct _MMVAD* Parent;                                              //0x0
    } u1;                                                                   //0x0
    struct _MMVAD* LeftChild;                                               //0x8
    struct _MMVAD* RightChild;                                              //0x10
    ULONGLONG StartingVpn;                                                  //0x18
    ULONGLONG EndingVpn;                                                    //0x20
    union
    {
        ULONGLONG LongFlags;                                                //0x28
        struct _MMVAD_FLAGS VadFlags;                                       //0x28
    } u;                                                                    //0x28
    struct _EX_PUSH_LOCK PushLock;                                          //0x30
    union
    {
        ULONGLONG LongFlags3;                                               //0x38
        struct _MMVAD_FLAGS3 VadFlags3;                                     //0x38
    } u5;                                                                   //0x38
    union
    {
        ULONG LongFlags2;                                                   //0x40
        struct _MMVAD_FLAGS2 VadFlags2;                                     //0x40
    } u2;                                                                   //0x40
    struct _SUBSECTION* Subsection;                                         //0x48
    struct _MMPTE* FirstPrototypePte;                                       //0x50
    struct _MMPTE* LastContiguousPte;                                       //0x58
    struct _LIST_ENTRY ViewLinks;                                           //0x60
    struct _EPROCESS* VadsProcess;                                          //0x70
    union
    {
        struct _LIST_ENTRY List;                                            //0x78
        struct _MMADDRESS_LIST Secured;                                     //0x78
    } u3;                                                                   //0x78
    union
    {
        struct _MMBANKED_SECTION* Banked;                                   //0x88
        struct _MMEXTEND_INFO* ExtendedInfo;                                //0x88
    } u4;                                                                   //0x88
}; 



//0x40 bytes (sizeof)
struct _MMVAD_SHORT
{
    union
    {
        LONGLONG Balance:2;                                                 //0x0
        struct _MMVAD* Parent;                                              //0x0
    } u1;                                                                   //0x0
    struct _MMVAD* LeftChild;                                               //0x8
    struct _MMVAD* RightChild;                                              //0x10
    ULONGLONG StartingVpn;                                                  //0x18
    ULONGLONG EndingVpn;                                                    //0x20
    union
    {
        ULONGLONG LongFlags;                                                //0x28
        struct _MMVAD_FLAGS VadFlags;                                       //0x28
    } u;                                                                    //0x28
    struct _EX_PUSH_LOCK PushLock;                                          //0x30
    union
    {
        ULONGLONG LongFlags3;                                               //0x38
        struct _MMVAD_FLAGS3 VadFlags3;                                     //0x38
    } u5;                                                                   //0x38
}; 

1️⃣ 关键标志:如何判断一个 VAD 是 LONG还是SHORT?

看 VadFlags2的LongVad标志位:

cpp 复制代码
ULONG LongVad:1;

如果 VadFlags2.LongVad = 1

该节点实际结构应解释为 _MMVAD_LONG

2️⃣ StartingVpn / EndingVpn 如何计算真实地址?

它们表示:

这条 VAD 覆盖的虚拟页号区间(Virtual Page Number)

假设:

cpp 复制代码
StartingVpn = 0x2770
EndingVpn   = 0x27EF

真实虚拟地址区间为:

cpp 复制代码
StartVA = 0x2770 << 12 = 0x2770000
EndVA   = (0x27EF << 12) + 0xFFF

2.5 _MMVAD_FLAGS 结构解析

在 Win7 x64 中,VAD 节点的核心属性字段位于:

cpp 复制代码
union
{
    ULONGLONG LongFlags;        // 0x28
    _MMVAD_FLAGS VadFlags;
} u;

其定义如下:

cpp 复制代码
// 8 bytes
struct _MMVAD_FLAGS
{
    ULONGLONG CommitCharge:51;
    ULONGLONG NoChange:1;
    ULONGLONG VadType:3;
    ULONGLONG MemCommit:1;
    ULONGLONG Protection:5;
    ULONGLONG Spare:2;
    ULONGLONG PrivateMemory:1;
};

总共:

cpp 复制代码
51 + 1 + 3 + 1 + 5 + 2 + 1 = 64 bits
刚好8字节

1️⃣ CommitCharge(51 位)

含义:

该 VAD 区间占用的"已提交页数"

单位:

cpp 复制代码
页(4KB)

例如:

cpp 复制代码
CommitCharge = 32

表示:

cpp 复制代码
32 页 = 128KB 已提交内存

2️⃣ NoChange(1 位)

表示:

该区域的保护属性是否允许修改

如果:
NoChange = 1

VirtualProtect 无法更改保护属性

3️⃣ VadType(3 位)

这 3 位字段用于标识当前 VAD 节点的"内存类型"。

在 Win7 x64 中,其枚举定义为:

cpp 复制代码
typedef enum _MI_VAD_TYPE {
    VadNone,                   // 0
    VadDevicePhysicalMemory,   // 1
    VadImageMap,               // 2
    VadAwe,                    // 3
    VadWriteWatch,             // 4
    VadLargePages,             // 5
    VadRotatePhysical,         // 6
    VadLargePageSection        // 7
} MI_VAD_TYPE, *PMI_VAD_TYPE;

4️⃣ MemCommit(1 位)

表示:

该 VAD 是否已经提交(commit)

区别:

  • CommitCharge 表示页数

  • MemCommit 表示状态标志

5️⃣ Protection(5 位)

_MMVAD_FLAGS.Protection 不是用户态 API 里看到的 PAGE_READWRITE / PAGE_EXECUTE... 那些常量值本身。

  • 它是 内核内部的保护码(常叫 MM protection code),用 5 bit(0~31)编码,表示这段 VAD 的"基础页保护属性"。

    VAD 记录的是"这段区间应该是什么保护"

  • 真正落实到 PTE 里时,PTE 的 NX、RW、U/S 等位会根据这个 Protection 进一步展开(并可能叠加别的机制:CopyOnWrite、NoChange、SecNoChange、Image/Section 等)

6️⃣Spare(2 位)

保留字段。

7️⃣PrivateMemory(1 位)

表示:

是否为 Private VAD

  • 1 = 私有内存(VirtualAlloc)

  • 0 = Section 映射(文件 / pagefile / image)

这和 !vad 输出的:

cpp 复制代码
Private / Mapped

直接对应

2.6 _MMVAD_FLAGS2 结构解析

_MMVAD_FLAGS2 位于 _MMVAD/_MMVAD_LONG 的 u2 中:

其定义如下:

cpp 复制代码
//0x4 bytes (sizeof)
struct _MMVAD_FLAGS2
{
    ULONG FileOffset:24;                                                    //0x0
    ULONG SecNoChange:1;                                                    //0x0
    ULONG OneSecured:1;                                                     //0x0
    ULONG MultipleSecured:1;                                                //0x0
    ULONG Spare:1;                                                          //0x0
    ULONG LongVad:1;                                                        //0x0
    ULONG ExtendableFile:1;                                                 //0x0
    ULONG Inherit:1;                                                        //0x0
    ULONG CopyOnWrite:1;                                                    //0x0
}; 

1️⃣FileOffset(24 位)

含义:

FileOffset 主要用于 "文件映射 / section 映射" 相关的 VAD,用来表达"该 VAD 对应的映射视图在文件中的偏移"。

2️⃣ SecNoChange(1 位)

含义:

SecNoChange 表示该 VAD 对应的 Section 映射保护属性不允许被更改。

和 _MMVAD_FLAGS.NoChange 的区别:

  • NoChange:更广义,偏"VAD 保护是否可改"

  • SecNoChange:更偏"由 Section 派生出来的保护不可改"

LongVad(1 位)------最关键的分支开关

含义

LongVad 用于决定 该节点的真实结构体类型:

  • LongVad = 0 → 该节点按 _MMVAD_SHORT 解释(只有公共头)

  • LongVad = 1 → 该节点按 _MMVAD_LONG 解释(有额外扩展字段 u3/u4 等)

CopyOnWrite(1 位)

含义:

CopyOnWrite = 1 表示该 VAD 区间具备 写时复制(COW) 语义:

  • 读时共享

  • 一旦写入触发私有化(产生 private copy)

  • !vad 常显示为 WRITECOPY / EXECUTE_WRITECOPY

典型场景

  • 映像映射(EXE/DLL ImageMap)几乎是它的"主场"

  • 共享 section 映射且设置了写时复制属性

相关推荐
程序员陆通4 小时前
零基础零成本把Clawdbot(OpenClaw)在Windows电脑上部署起来
windows
盘古工具5 小时前
告别重复数据:Excel禁止重复输入的两种方法
windows·excel
x***r1516 小时前
windows7安装步骤详解(附U盘启动盘制作与常见问题解决)
windows
xyzhan6 小时前
RAD Studio Amalthea Delphi 13.1 Beta深度解析:Delphi重磅更新,原生支持Windows ARM64EC
windows·delphi·rad studio·c++ builder·delphi 13
YoungHong19929 小时前
Windows Terminal:AI时代的现代化命令行方案
windows·terminal·powershell7
love530love10 小时前
【OpenClaw 本地实战 Ep.2】零代码对接:使用交互式向导快速连接本地 LM Studio 用 CUDA GPU 推理
人工智能·windows·gpu·cuda·ollama·lm studio·openclaw
编码者卢布10 小时前
【Azure App Service】为什么启用 Health Check 后应用服务实例持续显示 Unhealthy?
windows·azure
云小逸19 小时前
【nmap源码分析】Target 类——目标主机信息管理的核心引擎
服务器·windows·nmap
x***r15120 小时前
VMware17安装步骤详解(附虚拟机创建与常见问题解决)
windows