内存管理篇——私有内存和映射内存
写在前面
此系列是本人一个字一个字码出来的,包括示例和实验截图。由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新。 如有好的建议,欢迎反馈。码字不易,如果本篇文章有帮助你的,如有闲钱,可以打赏支持我的创作。如想转载,请把我的转载信息附在文章后面,并声明我的个人信息和本人博客地址即可,但必须事先通知我。
你如果是从中间插过来看的,请仔细阅读 羽夏看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
就是通过内存映射的方式实现的,不过它给的属性比较特别,是执行写拷贝,这个就是为了防止进程使用它操作影响别的进程,如果写入这样属性的内存操作系统就会另给一个物理页,然后写进去重新映射实现。
下一篇
内存管理篇——物理内存的管理
本文来自博客园,作者:寂静的羽夏 ,一个热爱计算机技术的菜鸟
转载请注明原文链接:https://www.cnblogs.com/wingsummer/p/15922379.html