内存管理篇——私有内存和映射内存

写在前面

  此系列是本人一个字一个字码出来的,包括示例和实验截图。由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新。 如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我

你如果是从中间插过来看的,请仔细阅读 羽夏看Win系统内核——简述 ,方便学习本教程。

  看此教程之前,问几个问题,基础知识储备好了吗?保护模式篇学会了吗?练习做完了吗?没有的话就不要继续了。


🔒 华丽的分割线 🔒


练习及参考

本次答案均为参考,可以与我的答案不一致,但必须成功通过。

1️⃣ 写代码实现需输入进程的PID来获取打印其VAD树。

🔒 点击查看答案 🔒


  该项目的麻烦的地方就是搞偏移,话不多说给个效果图:


🔒 点击查看驱动代码 🔒
#include <ntifs.h>
#include <ntddk.h>

typedef struct _CONTROL_AREA
{
    char Reserve[0x24];
    ULONG FileObject;
}CONTROL_AREA, * PCONTROL_AREA;

typedef struct _MMVAD_FLAGS {
    ULONG CommitCharge : 19;
    ULONG PhysicalMapping : 1;
    ULONG ImageMap : 1;
    ULONG UserPhysicalPages : 1;
    ULONG NoChange : 1;
    ULONG WriteWatch : 1;
    ULONG Protection : 5;
    ULONG LargePages : 1;
    ULONG MemCommit : 1;
    ULONG PrivateMemory : 1;
} MMVAD_FLAGS;

typedef struct _MMVAD {
    ULONG StartingVpn;
    ULONG EndingVpn;
    struct _MMVAD* Parent;
    struct _MMVAD* LeftChild;
    struct _MMVAD* RightChild;
    union {
        ULONG LongFlags;
        MMVAD_FLAGS VadFlags;
    } u;
    PCONTROL_AREA ControlArea;
    ULONG FirstPrototypePte;
    ULONG LastContiguousPte;
    union {
        ULONG LongFlags2;
        ULONG VadFlags2;
    } u2;
}MMVAD, * PMMVAD;


#define DEVICE_NAME L"\\Device\\VADLookup"
#define SYMBOL_LINK L"\\??\\VADLookup"

//操作码:0x0-0x7FF 被保留,0x800-0xFFF 可用
#define Lookup CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)

NTSTATUS DEVICE_CONTROL_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp);
NTSTATUS Common_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp);
void LookUpVAD(int pid);
void _fastcall ParseVad(PMMVAD vad);

UNICODE_STRING Devicename;
UNICODE_STRING SymbolLink;

NTSTATUS UnloadDriver(PDRIVER_OBJECT DriverObject)
{
    IoDeleteSymbolicLink(&SymbolLink);
    IoDeleteDevice(DriverObject->DeviceObject);
    DbgPrint("卸载成功!!!\n");
    return STATUS_SUCCESS;
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{

    DriverObject->DriverUnload = UnloadDriver;

    RtlInitUnicodeString(&Devicename, DEVICE_NAME);
    DEVICE_OBJECT dobj;
    if (IoCreateDevice(DriverObject, 0, &Devicename, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &dobj))
    {
        DbgPrint("创建对象失败!!!");
    }

    RtlInitUnicodeString(&SymbolLink, SYMBOL_LINK);

    NTSTATUS s = IoCreateSymbolicLink(&SymbolLink, &Devicename);

    if (s)
    {
        DbgPrint("创建符号失败!!!%x", s);
    }

    DriverObject->Flags |= DO_BUFFERED_IO;

    DriverObject->MajorFunction[IRP_MJ_CREATE] = Common_Dispatch;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DEVICE_CONTROL_Dispatch;
    DriverObject->MajorFunction[IRP_MJ_CLOSE] = Common_Dispatch;

    DbgPrint("驱动加载完毕!!!\n");
    return STATUS_SUCCESS;
}

NTSTATUS DEVICE_CONTROL_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{

    NTSTATUS status = STATUS_SUCCESS;
    PIO_STACK_LOCATION pIrqlStack;
    ULONG uIoControlCode;
    PVOID pIoBuffer;
    ULONG uInLength;
    ULONG uOutLength;
    ULONG uRead;
    ULONG uWrite;

    pIrqlStack = IoGetCurrentIrpStackLocation(pIrp);
    uIoControlCode = pIrqlStack->Parameters.DeviceIoControl.IoControlCode;
    pIoBuffer = pIrp->AssociatedIrp.SystemBuffer;
    uInLength = pIrqlStack->Parameters.DeviceIoControl.InputBufferLength;
    uOutLength = pIrqlStack->Parameters.DeviceIoControl.OutputBufferLength;

    switch (uIoControlCode)
    {
        case Lookup:
            if (uInLength > 4)
            {
                status = STATUS_BAD_DATA;
            }
            RtlCopyMemory(&uRead, pIoBuffer, uInLength);
            LookUpVAD(uRead);
            break;
        default:
            status = STATUS_INVALID_DEVICE_REQUEST;
            break;
    }

    pIrp->IoStatus.Status = status;
    pIrp->IoStatus.Information = 4;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
}

NTSTATUS Common_Dispatch(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
    pIrp->IoStatus.Status = STATUS_SUCCESS;
    pIrp->IoStatus.Information = 0;
    IoCompleteRequest(pIrp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;
}

void LookUpVAD(int pid)
{
    PEPROCESS process;
    if (PsLookupProcessByProcessId(pid, &process) == STATUS_SUCCESS)
    {
        PMMVAD vad = *(DWORD32*)((DWORD32)process + 0x11C);
        DbgPrint("Attribute\\tEnd\\tProtect\n");
        ParseVad(vad);
    }
}

void _fastcall ParseVad(PMMVAD vad)
{
    if (!vad)
        return;

    __try
    {
        ParseVad(vad->LeftChild);

        MMVAD_FLAGS vadflag = vad->u.VadFlags;

        if (vadflag.PrivateMemory)
        {
            DbgPrint("Attr : Private\tStart : %x\tEnd : %x\tCommit : %d\tProtection : %d\n", vad->StartingVpn, vad->EndingVpn, vadflag.CommitCharge, vadflag.Protection);
        }
        else
        {
            DbgPrint("Attr : Mapped\tStart : %x\tEnd : %x\tCommit : %d\tProtection : %d", vad->StartingVpn, vad->EndingVpn, vadflag.CommitCharge, vadflag.Protection);

            if (vadflag.ImageMap)
            {
                DbgPrint("\tEXE");
            }
            else
            {
                DbgPrint("\t\t");
            }

            ULONG fobj = vad->ControlArea->FileObject;

            if (fobj)
            {
                DbgPrint("\t%wZ", fobj + 0x30);
            }

            DbgPrint("\n");
        }

        ParseVad(vad->RightChild);

    }
    __except (1)
    {
        DbgPrint("打印失败!\n");
    }
}
🔒 点击查看应用代码代码 🔒
#include "stdafx.h"
#include <windows.h>
#include <winioctl.h>
#include <stdlib.h>

//操作码:0x0-0x7FF 被保留,0x800-0xFFF 可用
#define Lookup CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define SYMBOL_LINK_NAME L"\\\\.\\VADLookup"

HANDLE g_Device;

int main(int argc, char* argv[])
{
   //获取驱动链接对象句柄
   g_Device=CreateFileW(SYMBOL_LINK_NAME,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
   if (g_Device==INVALID_HANDLE_VALUE)
   {
      puts("访问驱动符号链接失败!");
      goto endproc;
   }

   DWORD pid;
   DWORD outBuffer;
   DWORD re;
   
   puts("请输入需要遍历VAD的程序 pid :");
   scanf("%d",&pid);
   
   if (pid)
   {
       if (DeviceIoControl(g_Device,Lookup,&pid,sizeof(DWORD), &outBuffer,sizeof(DWORD),&re,NULL))
       {
           puts("命令发送成功……");
       }  
   }
   else
   {
       puts("pid 非法!");
   }
   
endproc:
   
   CloseHandle(g_Device);
   system("pause");
   return 0;
}

概述

  前面我们详细的介绍了线性地址的管理,知道了线性地址具有私有内存Private和映射内存Mapped两种类型,下面我们来详细介绍一下它们是如何申请使用的。
  在Windows中,实现申请内存的函数是VirtualAlloc(Ex)CreateFileMapping。前者申请私有内存,后者申请映射内存。私有内存好理解,什么是映射内存呢?我们来看一张图:

  如果使用映射内存,我可以在不同进程使用,所以这种内存又被称之为共享内存。而这块内存不会重复申请物理页,只要创建一次,就可以在任意进程使用,从而降低物理页的浪费。

私有内存

详解私有内存

  对于私有内存的实验代码如下:

#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>


int main(int argc, char* argv[])
{
    LPVOID p = VirtualAlloc(NULL,4096,MEM_COMMIT,PAGE_READWRITE);
    printf("Addr : 0x%X\n",p);
    *(int*)p=5;
    system("pause");
    VirtualFree(p,0,MEM_FREE);
    return 0;
}

  单步执行,到VirtualAlloc还未执行的时候,我们打开Windbg来看看情况:

kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
……
Failed to get VadRoot
PROCESS 89b58790  SessionId: 0  Cid: 0554    Peb: 7ffd5000  ParentCid: 07f4
    DirBase: 139c01a0  ObjectTable: e1296f58  HandleCount:  13.
    Image: mytest.exe

kd> dt _EPROCESS 89b58790
ntdll!_EPROCESS
   ……
   +0x11c VadRoot          : 0x89b374c8 Void
   +0x120 VadHint          : 0x89b374c8 Void
   ……
kd> !vad 0x89b374c8
VAD   Level     Start       End Commit
89ac2628  1        10        10      1 Private      READWRITE          
89aded18  2        20        20      1 Private      READWRITE          
89cae1a8  3        30       12f      3 Private      READWRITE          
89ac9510  4       130       132      0 Mapped       READONLY           Pagefile section, shared commit 0x3
89b02128  5       140       23f      3 Private      READWRITE          
8984b8a8  6       240       24f      6 Private      READWRITE          
89a77700  7       250       25f      0 Mapped       READWRITE          Pagefile section, shared commit 0x3
89a92160  8       260       275      0 Mapped       READONLY           \WINDOWS\system32\unicode.nls
89bdf5d8  9       280       2c0      0 Mapped       READONLY           \WINDOWS\system32\locale.nls
89b48e18 10       2d0       310      0 Mapped       READONLY           \WINDOWS\system32\sortkey.nls
89bb9360 11       320       325      0 Mapped       READONLY           \WINDOWS\system32\sorttbls.nls
89b55438 12       330       370      0 Mapped       READONLY           Pagefile section, shared commit 0x41
89b563c8 13       380       38f      3 Private      READWRITE          
89867100 14       390       392      0 Mapped       READONLY           \WINDOWS\system32\ctype.nls
89b374c8  0       400       42d      8 Mapped  Exe  EXECUTE_WRITECOPY  \Program Files\Microsoft Visual Studio\MyProjects\mytest\Debug\mytest.exe
89b48270  2     7c800     7c91d      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\kernel32.dll
89bb7c98  1     7c920     7c9b2      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\ntdll.dll
89b3e480  3     7f6f0     7f7ef      0 Mapped       EXECUTE_READ       Pagefile section, shared commit 0x7
8986e660  2     7ffa0     7ffd2      0 Mapped       READONLY           Pagefile section, shared commit 0x33
89ac97b0  3     7ffd5     7ffd5      1 Private      READWRITE          
89c82bc8  4     7ffdf     7ffdf      1 Private      READWRITE          

Total VADs: 21, average level: 6, maximum depth: 14
Total private commit: 0x25 pages (148 KB)
Total shared commit:  0x81 pages (516 KB)

  然后放回继续单步,直至打印申请的地址:

Addr : 0x3A0000

  然后重新到Windbg,再查阅VAD的情况:

kd> !vad 0x89b374c8
VAD   Level     Start       End Commit
89ac9510  2       130       132      0 Mapped       READONLY           Pagefile section, shared commit 0x3
89b02128  1       140       23f      3 Private      READWRITE          
8984b8a8  3       240       24f      6 Private      READWRITE          
89a77700  2       250       25f      0 Mapped       READWRITE          Pagefile section, shared commit 0x3
89a92160  4       260       275      0 Mapped       READONLY           \WINDOWS\system32\unicode.nls
89bdf5d8  3       280       2c0      0 Mapped       READONLY           \WINDOWS\system32\locale.nls
89b48e18  5       2d0       310      0 Mapped       READONLY           \WINDOWS\system32\sortkey.nls
89bb9360  4       320       325      0 Mapped       READONLY           \WINDOWS\system32\sorttbls.nls
89b55438  6       330       370      0 Mapped       READONLY           Pagefile section, shared commit 0x41
89b563c8  5       380       38f      5 Private      READWRITE          
89867100  6       390       392      0 Mapped       READONLY           \WINDOWS\system32\ctype.nls
89aafb48  7       3a0       3a0      1 Private      READWRITE          
89b374c8  0       400       42d      9 Mapped  Exe  EXECUTE_WRITECOPY  \Program Files\Microsoft Visual Studio\MyProjects\mytest\Debug\mytest.exe
89b48270  2     7c800     7c91d      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\kernel32.dll
89bb7c98  1     7c920     7c9b2      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\ntdll.dll
89b3e480  3     7f6f0     7f7ef      0 Mapped       EXECUTE_READ       Pagefile section, shared commit 0x7
8986e660  2     7ffa0     7ffd2      0 Mapped       READONLY           Pagefile section, shared commit 0x33
89ac97b0  3     7ffd5     7ffd5      1 Private      READWRITE          
89c82bc8  4     7ffdf     7ffdf      1 Private      READWRITE          

Total VADs: 19, average level: 4, maximum depth: 7
Total private commit: 0x24 pages (144 KB)
Total shared commit:  0x81 pages (516 KB)

  此时,我们就看到就多了一条申请的记录了,下面我们继续深入探讨一个问题:

Commit 与 Reserve

  我们使用VirtualAlloc这个函数的时候,第三个参数要传分配的类型,常见的一个是MEM_COMMIT,另一个是MEM_RESERVE。很多开发者认为前者申请后就会提供一个物理页,而后者会有记录但不会给你物理页,实际上不是这样,我们来继续上面的实验,查一下我们上面申请的物理页有没有地址:

kd> !vtop 139c01a0 0x3A0000
X86VtoP: Virt 00000000003a0000, pagedir 00000000139c01a0
X86VtoP: PAE PDPE 00000000139c01a0 - 000000004603f001
X86VtoP: PAE PDE 000000004603f008 - 0000000045e8a067
X86VtoP: PAE PTE 0000000045e8ad00 - 0000000000000000
X86VtoP: PAE zero PTE
Virtual address 3a0000 translation fails, error 0xD0000147.

  不幸的是,它没有PTE,也就是说,没有挂物理页。我们放开这个程序后,再查一下它的属性:

kd> !vtop 139c01a0 0x3A0000
X86VtoP: Virt 00000000003a0000, pagedir 00000000139c01a0
X86VtoP: PAE PDPE 00000000139c01a0 - 000000004603f001
X86VtoP: PAE PDE 000000004603f008 - 0000000045e8a067
X86VtoP: PAE PTE 0000000045e8ad00 - 00000000463e2067
X86VtoP: PAE Mapped phys 00000000463e2000
Virtual address 3a0000 translates to physical address 463e2000.

  可以看出,这次成功挂上了PTE了。也就是说,就算你使用MEM_COMMIT参数,如果不用的话,物理页也不会给你的。

malloc 分析

  在3环我们经常用malloc来“申请内存”。但它不是真正申请内存,它的内部实现是HeapAlloc,也就是申请堆内存,而这堆内存是进程创建好后操作系统就分配好的了,我们来做个实验验证一下:

#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>


int main(int argc, char* argv[])
{
    LPVOID p = malloc(4);
    printf("Addr : 0x%X\n",p);
    system("pause");
    free(p);
    return 0;
}

  当malloc之前,我们得看看VAD的情况:

kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
……
Failed to get VadRoot
PROCESS 89a797b8  SessionId: 0  Cid: 062c    Peb: 7ffda000  ParentCid: 07f4
    DirBase: 139c0240  ObjectTable: e18600d8  HandleCount:  21.
    Image: mytest.exe


kd> dt _EPROCESS 89a797b8
ntdll!_EPROCESS
   ……
   +0x118 HardwareTrigger  : 0
   +0x11c VadRoot          : 0x89ad68b8 Void
   +0x120 VadHint          : 0x89ad68b8 Void
   ……
kd> !vad 0x89ad68b8
VAD   Level     Start       End Commit
89b70870  1        10        10      1 Private      READWRITE          
89ac2628  2        20        20      1 Private      READWRITE          
89ad68b8  0        30       12f      4 Private      READWRITE          
89aa7ea8  3       130       132      0 Mapped       READONLY           Pagefile section, shared commit 0x3
89b67bd0  2       140       23f      8 Private      READWRITE          
89c36038  4       240       24f      6 Private      READWRITE          
89aafea8  3       250       25f      0 Mapped       READWRITE          Pagefile section, shared commit 0x3
89a7d4e0  5       260       275      0 Mapped       READONLY           \WINDOWS\system32\unicode.nls
89a65be8  4       280       2c0      0 Mapped       READONLY           \WINDOWS\system32\locale.nls
89c582a8  6       2d0       310      0 Mapped       READONLY           \WINDOWS\system32\sortkey.nls
89a804b0  5       320       325      0 Mapped       READONLY           \WINDOWS\system32\sorttbls.nls
8984a8e8  7       330       370      0 Mapped       READONLY           Pagefile section, shared commit 0x41
89aa7ce0  6       380       38f      5 Private      READWRITE          
89a77700  7       390       392      0 Mapped       READONLY           \WINDOWS\system32\ctype.nls
89bb9360  8       3a0       3ad      0 Mapped       READWRITE          Pagefile section, shared commit 0xe
89bb91f0  9       3b0       3b0      1 Private      READWRITE          
89a71930  1       400       42d      8 Mapped  Exe  EXECUTE_WRITECOPY  \Program Files\Microsoft Visual Studio\MyProjects\mytest\Debug\mytest.exe
89b72328  4     76d70     76d91      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\apphelp.dll
8986e660  5     77bd0     77bd7      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\version.dll
89ad52a8  6     77da0     77e48      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\advapi32.dll
89b4c008  7     77e50     77ee1      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\rpcrt4.dll
89a6ef40  8     77fc0     77fd0      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\secur32.dll
89d789a0  3     7c800     7c91d      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\kernel32.dll
89b77380  2     7c920     7c9b2      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\ntdll.dll
89a979a8  4     7f6f0     7f7ef      0 Mapped       EXECUTE_READ       Pagefile section, shared commit 0x7
89b5a9a0  3     7ffa0     7ffd2      0 Mapped       READONLY           Pagefile section, shared commit 0x33
89b69c20  4     7ffda     7ffda      1 Private      READWRITE          
89c00950  5     7ffdf     7ffdf      1 Private      READWRITE          

Total VADs: 28, average level: 5, maximum depth: 9
Total private commit: 0x37 pages (220 KB)
Total shared commit:  0x8f pages (572 KB)

  然后放开程序:

Addr : 0x382A50
请按任意键继续. . .

  继续查询VAD,你就会发现,它就是记录上已经有的内存了。我们还听说过栈内存和全局变量,它是不是也和堆内存是一样的呢?我们继续做实验:

#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>

int v=0;

int main(int argc, char* argv[])
{
    int stack;
    LPVOID p = malloc(4);
    printf("v : 0x%X\np : 0x%X\nstack : 0x%X\n",&v,p,&stack);
    system("pause");
    free(p);
    return 0;
}

  同理在真正运行前查一下进程VAD情况:

kd> !process 0 0
……
Failed to get VadRoot
PROCESS 89cb6410  SessionId: 0  Cid: 053c    Peb: 7ffdc000  ParentCid: 07f4
    DirBase: 139c0240  ObjectTable: e18600d8  HandleCount:  21.
    Image: mytest.exe
kd> dt _EPROCESS 89cb6410
ntdll!_EPROCESS
   ……
   +0x118 HardwareTrigger  : 0
   +0x11c VadRoot          : 0x89b414b8 Void
   +0x120 VadHint          : 0x89b414b8 Void
   ……
kd> !vad 0x89b414b8
VAD   Level     Start       End Commit
89aa0138  1        10        10      1 Private      READWRITE          
89ca54b0  2        20        20      1 Private      READWRITE          
89b414b8  0        30       12f      4 Private      READWRITE          
89bb91a0  3       130       132      0 Mapped       READONLY           Pagefile section, shared commit 0x3
89cc9458  2       140       23f      8 Private      READWRITE          
89b5cfe8  4       240       24f      6 Private      READWRITE          
89bfa7c8  3       250       25f      0 Mapped       READWRITE          Pagefile section, shared commit 0x3
89ab5c40  5       260       275      0 Mapped       READONLY           \WINDOWS\system32\unicode.nls
89abda00  4       280       2c0      0 Mapped       READONLY           \WINDOWS\system32\locale.nls
89854a80  6       2d0       310      0 Mapped       READONLY           \WINDOWS\system32\sortkey.nls
89b5ccb8  5       320       325      0 Mapped       READONLY           \WINDOWS\system32\sorttbls.nls
89872988  7       330       370      0 Mapped       READONLY           Pagefile section, shared commit 0x41
89dc63d8  6       380       38f      5 Private      READWRITE          
89bb9360  7       390       392      0 Mapped       READONLY           \WINDOWS\system32\ctype.nls
89ad52a8  8       3a0       3ad      0 Mapped       READWRITE          Pagefile section, shared commit 0xe
89b67bd0  9       3b0       3b0      1 Private      READWRITE          
89869828  1       400       42d      8 Mapped  Exe  EXECUTE_WRITECOPY  \Program Files\Microsoft Visual Studio\MyProjects\mytest\Debug\mytest.exe
89a804b0  4     76d70     76d91      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\apphelp.dll
89cd9a50  5     77bd0     77bd7      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\version.dll
89bc8e80  6     77da0     77e48      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\advapi32.dll
89871a10  7     77e50     77ee1      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\rpcrt4.dll
89bcc868  8     77fc0     77fd0      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\secur32.dll
89ae1ab0  3     7c800     7c91d      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\kernel32.dll
89b55438  2     7c920     7c9b2      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\ntdll.dll
89a77700  4     7f6f0     7f7ef      0 Mapped       EXECUTE_READ       Pagefile section, shared commit 0x7
89b31c68  3     7ffa0     7ffd2      0 Mapped       READONLY           Pagefile section, shared commit 0x33
89b69c20  4     7ffdc     7ffdc      1 Private      READWRITE          
89b48e08  5     7ffdf     7ffdf      1 Private      READWRITE          

Total VADs: 28, average level: 5, maximum depth: 9
Total private commit: 0x37 pages (220 KB)
Total shared commit:  0x8f pages (572 KB)

  然后放开程序跑起来:

v : 0x429E68
p : 0x382A50
stack : 0x12FF7C
请按任意键继续. . .

  再次查询后,你就会发现这些地址都说已经申请好的而已咱们用,并没有真正申请内存。

映射内存

初识映射内存

  介绍完了私有内存,我们来做一个实验来初步认识映射内存:

#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{
    HANDLE handle = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,BUFSIZ,"SharedMem");
    LPVOID p = MapViewOfFile(handle,FILE_MAP_ALL_ACCESS,0,0,BUFSIZ);
    printf("p : 0x%X\n",p);
    *(int*)p = 5;
    system("pause");
    UnmapViewOfFile(p);
    CloseHandle(handle);
    return 0;
}

  我们单步执行到CreateFileMapping运行之前,查一下它的VAD

kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
……
Failed to get VadRoot
PROCESS 89ab1da0  SessionId: 0  Cid: 06d0    Peb: 7ffda000  ParentCid: 07f4
    DirBase: 139c0240  ObjectTable: e11e84a8  HandleCount:  13.
    Image: mytest.exe

kd> dt _EPROCESS 89ab1da0
ntdll!_EPROCESS
   ……
   +0x114 ForkInProgress   : (null) 
   +0x118 HardwareTrigger  : 0
   +0x11c VadRoot          : 0x89c16668 Void
   ……
kd> !vad 0x89c16668
VAD   Level     Start       End Commit
89cae1a8  1        10        10      1 Private      READWRITE          
89aa0138  2        20        20      1 Private      READWRITE          
89dbf128  3        30       12f      3 Private      READWRITE          
89a804b0  4       130       132      0 Mapped       READONLY           Pagefile section, shared commit 0x3
89b70870  5       140       23f      3 Private      READWRITE          
89c36038  6       240       24f      6 Private      READWRITE          
898693d0  7       250       25f      0 Mapped       READWRITE          Pagefile section, shared commit 0x3
89ab5c40  8       260       275      0 Mapped       READONLY           \WINDOWS\system32\unicode.nls
89ae1ab0  9       280       2c0      0 Mapped       READONLY           \WINDOWS\system32\locale.nls
89abda00 10       2d0       310      0 Mapped       READONLY           \WINDOWS\system32\sortkey.nls
89854a80 11       320       325      0 Mapped       READONLY           \WINDOWS\system32\sorttbls.nls
89bcc210 12       330       370      0 Mapped       READONLY           Pagefile section, shared commit 0x41
89b67bd0 13       380       38f      3 Private      READWRITE          
89bb9520 14       390       392      0 Mapped       READONLY           \WINDOWS\system32\ctype.nls
89c16668  0       400       42d     10 Mapped  Exe  EXECUTE_WRITECOPY  \Program Files\Microsoft Visual Studio\MyProjects\mytest\Debug\mytest.exe
89b554e0  2     7c800     7c91d      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\kernel32.dll
89a7d2a0  1     7c920     7c9b2      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\ntdll.dll
89ad85a8  3     7f6f0     7f7ef      0 Mapped       EXECUTE_READ       Pagefile section, shared commit 0x7
89869828  2     7ffa0     7ffd2      0 Mapped       READONLY           Pagefile section, shared commit 0x33
89aceb18  3     7ffda     7ffda      1 Private      READWRITE          
8985ce38  4     7ffdf     7ffdf      1 Private      READWRITE          

Total VADs: 21, average level: 6, maximum depth: 14
Total private commit: 0x27 pages (156 KB)
Total shared commit:  0x81 pages (516 KB)

  继续单步走一行代码,它的VAD如下:

kd> !vad 0x89c16668
VAD   Level     Start       End Commit
89cae1a8  1        10        10      1 Private      READWRITE          
89aa0138  2        20        20      1 Private      READWRITE          
89dbf128  3        30       12f      3 Private      READWRITE          
89a804b0  4       130       132      0 Mapped       READONLY           Pagefile section, shared commit 0x3
89b70870  5       140       23f      3 Private      READWRITE          
89c36038  6       240       24f      6 Private      READWRITE          
898693d0  7       250       25f      0 Mapped       READWRITE          Pagefile section, shared commit 0x3
89ab5c40  8       260       275      0 Mapped       READONLY           \WINDOWS\system32\unicode.nls
89ae1ab0  9       280       2c0      0 Mapped       READONLY           \WINDOWS\system32\locale.nls
89abda00 10       2d0       310      0 Mapped       READONLY           \WINDOWS\system32\sortkey.nls
89854a80 11       320       325      0 Mapped       READONLY           \WINDOWS\system32\sorttbls.nls
89bcc210 12       330       370      0 Mapped       READONLY           Pagefile section, shared commit 0x41
89b67bd0 13       380       38f      3 Private      READWRITE          
89bb9520 14       390       392      0 Mapped       READONLY           \WINDOWS\system32\ctype.nls
89c16668  0       400       42d     10 Mapped  Exe  EXECUTE_WRITECOPY  \Program Files\Microsoft Visual Studio\MyProjects\mytest\Debug\mytest.exe
89b554e0  2     7c800     7c91d      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\kernel32.dll
89a7d2a0  1     7c920     7c9b2      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\ntdll.dll
89ad85a8  3     7f6f0     7f7ef      0 Mapped       EXECUTE_READ       Pagefile section, shared commit 0x7
89869828  2     7ffa0     7ffd2      0 Mapped       READONLY           Pagefile section, shared commit 0x33
89aceb18  3     7ffda     7ffda      1 Private      READWRITE          
8985ce38  4     7ffdf     7ffdf      1 Private      READWRITE          

Total VADs: 21, average level: 6, maximum depth: 14
Total private commit: 0x27 pages (156 KB)
Total shared commit:  0x81 pages (516 KB)

  执行完MapViewOfFile之后并打印地址后,我们再查看VAD

kd> !vad 0x89c16668
VAD   Level     Start       End Commit
89a804b0  2       130       132      0 Mapped       READONLY           Pagefile section, shared commit 0x3
89b70870  1       140       23f      3 Private      READWRITE          
89c36038  3       240       24f      6 Private      READWRITE          
898693d0  2       250       25f      0 Mapped       READWRITE          Pagefile section, shared commit 0x3
89ab5c40  4       260       275      0 Mapped       READONLY           \WINDOWS\system32\unicode.nls
89ae1ab0  3       280       2c0      0 Mapped       READONLY           \WINDOWS\system32\locale.nls
89abda00  5       2d0       310      0 Mapped       READONLY           \WINDOWS\system32\sortkey.nls
89854a80  4       320       325      0 Mapped       READONLY           \WINDOWS\system32\sorttbls.nls
89bcc210  6       330       370      0 Mapped       READONLY           Pagefile section, shared commit 0x41
89b67bd0  5       380       38f      5 Private      READWRITE          
89bb9520  6       390       392      0 Mapped       READONLY           \WINDOWS\system32\ctype.nls
8984a8e8  7       3a0       3a0      0 Mapped       READWRITE          Pagefile section, shared commit 0x1
89c16668  0       400       42d     10 Mapped  Exe  EXECUTE_WRITECOPY  \Program Files\Microsoft Visual Studio\MyProjects\mytest\Debug\mytest.exe
89b554e0  2     7c800     7c91d      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\kernel32.dll
89a7d2a0  1     7c920     7c9b2      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\ntdll.dll
89ad85a8  3     7f6f0     7f7ef      0 Mapped       EXECUTE_READ       Pagefile section, shared commit 0x7
89869828  2     7ffa0     7ffd2      0 Mapped       READONLY           Pagefile section, shared commit 0x33
89aceb18  3     7ffda     7ffda      1 Private      READWRITE          
8985ce38  4     7ffdf     7ffdf      1 Private      READWRITE          

Total VADs: 19, average level: 4, maximum depth: 7
Total private commit: 0x24 pages (144 KB)
Total shared commit:  0x82 pages (520 KB)

  你就会发现上面多了一条记录,并且和私有内存并不一样,它的映射的。我们测试一下我们申请后操作系统有没有立刻给我们物理页:

kd> !vtop 139c0240 0x3A0000
X86VtoP: Virt 00000000003a0000, pagedir 00000000139c0240
X86VtoP: PAE PDPE 00000000139c0240 - 00000000764c0001
X86VtoP: PAE PDE 00000000764c0008 - 000000007618d067
X86VtoP: PAE PTE 000000007618dd00 - 0000000000000000
X86VtoP: PAE zero PTE
Virtual address 3a0000 translation fails, error 0xD0000147.

  然后放开程序跑,继续查看:

kd> !vtop 139c0240 0x3A0000
X86VtoP: Virt 00000000003a0000, pagedir 00000000139c0240
X86VtoP: PAE PDPE 00000000139c0240 - 00000000764c0001
X86VtoP: PAE PDE 00000000764c0008 - 000000007618d067
X86VtoP: PAE PTE 000000007618dd00 - 00000000764fe067
X86VtoP: PAE Mapped phys 00000000764fe000
Virtual address 3a0000 translates to physical address 764fe000.

  这个和私有内存是一样的,就不赘述了。

文件映射内存

  你可以看到VAD的一些映射内存记录后面是有文件路径的,这是为什么呢?因为它创建的是文件映射,我们用下面的代码进行讲解:

#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{
    HANDLE hfile =CreateFile("C:\\WINDOWS\\NOTEPAD.EXE",GENERIC_WRITE|GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
    HANDLE handle = CreateFileMapping(hfile,NULL,PAGE_READWRITE,0,BUFSIZ,NULL);
    LPVOID p = MapViewOfFile(handle,FILE_MAP_ALL_ACCESS,0,0,BUFSIZ);
    printf("p : 0x%X\n",p);
    system("pause");
    UnmapViewOfFile(p);
    CloseHandle(handle);
    CloseHandle(hfile);
    return 0;
}

  直接跑起来,看到地址:

p : 0x3A0000
请按任意键继续. . .

  查询VAD

kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
……
Failed to get VadRoot
PROCESS 89840598  SessionId: 0  Cid: 01a4    Peb: 7ffda000  ParentCid: 07f4
    DirBase: 139c0240  ObjectTable: e1114520  HandleCount:  23.
    Image: mytest.exe

kd> dt _EPROCESS 89840598
ntdll!_EPROCESS
   ……
   +0x118 HardwareTrigger  : 0
   +0x11c VadRoot          : 0x89a750e8 Void
   +0x120 VadHint          : 0x89a750e8 Void
   ……
kd> !vad 0x89a750e8
VAD   Level     Start       End Commit
89a9f1d8  1        10        10      1 Private      READWRITE          
89cdaed8  2        20        20      1 Private      READWRITE          
89a750e8  0        30       12f      4 Private      READWRITE          
89ccc468  3       130       132      0 Mapped       READONLY           Pagefile section, shared commit 0x3
89d18de0  2       140       23f      8 Private      READWRITE          
89cae1a8  4       240       24f      6 Private      READWRITE          
89ac4b60  3       250       25f      0 Mapped       READWRITE          Pagefile section, shared commit 0x3
89cc8870  5       260       275      0 Mapped       READONLY           \WINDOWS\system32\unicode.nls
89ab2a58  4       280       2c0      0 Mapped       READONLY           \WINDOWS\system32\locale.nls
89859980  6       2d0       310      0 Mapped       READONLY           \WINDOWS\system32\sortkey.nls
89aa8bb8  5       320       325      0 Mapped       READONLY           \WINDOWS\system32\sorttbls.nls
89856468  7       330       370      0 Mapped       READONLY           Pagefile section, shared commit 0x41
89c84f40  6       380       38f      5 Private      READWRITE          
8984c1c8  7       390       392      0 Mapped       READONLY           \WINDOWS\system32\ctype.nls
89ae2a08  8       3a0       3a0      0 Mapped       READWRITE          \WINDOWS\NOTEPAD.EXE
89d78940  9       3b0       3bd      0 Mapped       READWRITE          Pagefile section, shared commit 0xe
89b02128 10       3c0       3c0      1 Private      READWRITE          
8985ca38  1       400       42d      9 Mapped  Exe  EXECUTE_WRITECOPY  \Program Files\Microsoft Visual Studio\MyProjects\mytest\Debug\mytest.exe
89b06098  4     76d70     76d91      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\apphelp.dll
898e2078  5     77bd0     77bd7      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\version.dll
89b1a138  6     77da0     77e48      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\advapi32.dll
89cd9a50  7     77e50     77ee1      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\rpcrt4.dll
898e2510  8     77fc0     77fd0      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\secur32.dll
89a92c10  3     7c800     7c91d      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\kernel32.dll
89cc6fd8  2     7c920     7c9b2      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\ntdll.dll
89a7d798  4     7f6f0     7f7ef      0 Mapped       EXECUTE_READ       Pagefile section, shared commit 0x7
89cd9dd8  3     7ffa0     7ffd2      0 Mapped       READONLY           Pagefile section, shared commit 0x33
89ab60e8  4     7ffda     7ffda      1 Private      READWRITE          
89868d28  5     7ffdf     7ffdf      1 Private      READWRITE          

Total VADs: 29, average level: 5, maximum depth: 10
Total private commit: 0x38 pages (224 KB)
Total shared commit:  0x8f pages (572 KB)

  可以看到我们申请的记录上后面就有我的文件名了,但是还有一个特别的,就是还有Exe标记,这个是咋弄的?我们来看看下面的代码:

#include "stdafx.h"
#include <windows.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{
    HMODULE lib = LoadLibrary("C:\\WINDOWS\\NOTEPAD.EXE");
    printf("Addr : 0x%X\n",lib);
    system("pause");
    return 0;
}

  跑起来查看地址:

Addr : 0x1000000
请按任意键继续. . .

  然后查询VAD

kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
……
Failed to get VadRoot
PROCESS 89ab1da0  SessionId: 0  Cid: 04b0    Peb: 7ffd7000  ParentCid: 07f4
    DirBase: 139c0300  ObjectTable: e1860870  HandleCount:  21.
    Image: mytest.exe

kd> dt _EPROCESS 89ab1da0
ntdll!_EPROCESS
   ……
   +0x118 HardwareTrigger  : 0
   +0x11c VadRoot          : 0x89b560b0 Void
   +0x120 VadHint          : 0x89b560b0 Void
   ……
kd> !vad 0x89b560b0
VAD   Level     Start       End Commit
89cb58b8  1        10        10      1 Private      READWRITE          
89dbf128  2        20        20      1 Private      READWRITE          
89b560b0  0        30       12f      4 Private      READWRITE          
89a71930  3       130       132      0 Mapped       READONLY           Pagefile section, shared commit 0x3
89b81690  2       140       23f      8 Private      READWRITE          
89dc6230  4       240       24f      6 Private      READWRITE          
89a908f0  3       250       25f      0 Mapped       READWRITE          Pagefile section, shared commit 0x3
89cc8870  5       260       275      0 Mapped       READONLY           \WINDOWS\system32\unicode.nls
89b78808  4       280       2c0      0 Mapped       READONLY           \WINDOWS\system32\locale.nls
89c0ecf0  6       2d0       310      0 Mapped       READONLY           \WINDOWS\system32\sortkey.nls
89bdab80  5       320       325      0 Mapped       READONLY           \WINDOWS\system32\sorttbls.nls
89cc6fd8  7       330       370      0 Mapped       READONLY           Pagefile section, shared commit 0x41
89cdb3a8  6       380       38f      5 Private      READWRITE          
89856468  7       390       392      0 Mapped       READONLY           \WINDOWS\system32\ctype.nls
89cd9dd8  8       3a0       3ad      0 Mapped       READWRITE          Pagefile section, shared commit 0xe
89aceaf8  9       3b0       3b0      1 Private      READWRITE          
8985ca38  1       400       42d      8 Mapped  Exe  EXECUTE_WRITECOPY  \Program Files\Microsoft Visual Studio\MyProjects\mytest\Debug\mytest.exe
89a7d798  4      1000      1012      2 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\NOTEPAD.EXE
89ad6280  5     76d70     76d91      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\apphelp.dll
89844168  6     77bd0     77bd7      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\version.dll
89bbe270  7     77da0     77e48      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\advapi32.dll
89aa8bb8  8     77e50     77ee1      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\rpcrt4.dll
89ab2a58  9     77fc0     77fd0      1 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\secur32.dll
898719a8  3     7c800     7c91d      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\kernel32.dll
89855958  2     7c920     7c9b2      5 Mapped  Exe  EXECUTE_WRITECOPY  \WINDOWS\system32\ntdll.dll
89868d38  4     7f6f0     7f7ef      0 Mapped       EXECUTE_READ       Pagefile section, shared commit 0x7
89a85dd0  3     7ffa0     7ffd2      0 Mapped       READONLY           Pagefile section, shared commit 0x33
89bb5328  4     7ffd7     7ffd7      1 Private      READWRITE          
89b77068  5     7ffdf     7ffdf      1 Private      READWRITE          

Total VADs: 29, average level: 5, maximum depth: 9
Total private commit: 0x39 pages (228 KB)
Total shared commit:  0x8f pages (572 KB)

  可以看到,我们的记录上就有一个Exe标记了。可以得出LoadLibrary就是通过内存映射的方式实现的,不过它给的属性比较特别,是执行写拷贝,这个就是为了防止进程使用它操作影响别的进程,如果写入这样属性的内存操作系统就会另给一个物理页,然后写进去重新映射实现。

下一篇

  内存管理篇——物理内存的管理

posted @ 2022-02-22 11:30  寂静的羽夏  阅读(751)  评论(0编辑  收藏  举报