windows原理04

windows的权限管理

UAC提升按钮

​ 在windows vista之后windows操作系统引入了UAC机制。即User Account Control,用户账户控制。

​ 它使得即使以管理员登录的账户在创建进程的时候也是分配一个低权限的令牌。这个令牌又叫做Filter Token,即过滤令牌。

​ 只有当程序以管理员身份运行,才会给予其高权限,但是以管理员身份运行的程序全部都会弹出一个选择对话框。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 1. 获得本进程的令牌
HANDLE hToken = NULL;
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
return false;
// 2. 获取提升类型
TOKEN_ELEVATION_TYPE ElevationType = TokenElevationTypeDefault;
BOOL bIsAdmin = false;
DWORD dwSize = 0;
if (GetTokenInformation(hToken, TokenElevationType, &ElevationType,
sizeof(TOKEN_ELEVATION_TYPE), &dwSize)) {
// 2.1 创建管理员组的对应SID
BYTE adminSID[SECURITY_MAX_SID_SIZE];
dwSize = sizeof(adminSID);
CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, &adminSID, &dwSize);
// 2.2 判断当前进程运行用户角色是否为管理员
if (ElevationType == TokenElevationTypeLimited) {
// a. 获取连接令牌的句柄
HANDLE hUnfilteredToken = NULL;
GetTokenInformation(hToken, TokenLinkedToken, (PVOID)&hUnfilteredToken,
sizeof(HANDLE), &dwSize);
// b. 检查这个原始的令牌是否包含管理员的SID
if (!CheckTokenMembership(hUnfilteredToken, &adminSID, &bIsAdmin))
return false;
CloseHandle(hUnfilteredToken);
}
else {
bIsAdmin = IsUserAnAdmin();
}
CloseHandle(hToken);
}
// 3. 判断具体的权限状况
BOOL bFullToken = false;
switch (ElevationType) {
case TokenElevationTypeDefault: /* 默认的用户或UAC被禁用 */
if (IsUserAnAdmin()) bFullToken = true; // 默认用户有管理员权限
else bFullToken = false;// 默认用户不是管理员组
break;
case TokenElevationTypeFull: /* 已经成功提高进程权限 */
if (IsUserAnAdmin()) bFullToken = true; //当前以管理员权限运行
else bFullToken = false;//当前未以管理员权限运行
break;
case TokenElevationTypeLimited: /* 进程在以有限的权限运行 */
if (bIsAdmin) bFullToken = false;//用户有管理员权限,但进程权限有限
else bFullToken = false;//用户不是管理员组,且进程权限有限
}
// 4. 根据权限的不同控制按钮的显示
if (!bFullToken)
Button_SetElevationRequiredState(GetDlgItem(hWnd,控件ID),
!bFullToken);
else
ShowWindow(GetDlgItem(hWnd, 控件ID), SW_SHOW);

image-20191115143854097

以管理员权限打开进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 1. 隐藏当前窗口
ShowWindow(SW_HIDE);
// 2. 获取当前程序路径
WCHAR szApplication[MAX_PATH] = { 0 };
DWORD cchLength = _countof(szApplication);
QueryFullProcessImageName(GetCurrentProcess(), 0,
szApplication, &cchLength);
// 3. 以管理员权限重新打开进程
SHELLEXECUTEINFO sei = { sizeof(SHELLEXECUTEINFO) };
sei.lpVerb = L"runas"; // 请求提升权限
sei.lpFile = szApplication; // 可执行文件路径
sei.lpParameters = NULL; // 不需要参数
sei.nShow = SW_SHOWNORMAL; // 正常显示窗口
if (ShellExecuteEx(&sei))
exit(0);
else
ShowWindow(hWnd,SW_SHOWNORMAL);

提升当前进程权限为调试权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
BOOL EnableDebugPrivilege(BOOL fEnable){  //提升为调试权限
BOOL fOk = FALSE;
HANDLE hToken;
//以修改权限的方式,打开进程的令牌
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES,
&hToken)) {
//令牌权限结构体
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
//获得LUID
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
tp.Privileges[0].Attributes == fEnable ? SE_PRIVILEGE_ENABLED : 0;
//修改权限
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
fOk = (GetLastError() == ERROR_SUCCESS);
CloseHandle(hToken);
}
return (fOk);
}

遍历权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void ShowPrivilege()
{
//打开访问令牌
HANDLE hToken;
OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken);
if (!hToken)
{
printf("令牌打开失败\n");
return;
}
//查询令牌中的权限
DWORD dwSize;
//第一次调用是为了获取数据大小
GetTokenInformation(hToken, TokenPrivileges, NULL, NULL, &dwSize);
char* pBuf = new char[dwSize]{};
//第二次调用就可以获取数据了
GetTokenInformation(hToken, TokenPrivileges, pBuf, dwSize, &dwSize);
TOKEN_PRIVILEGES* pTp = (TOKEN_PRIVILEGES*)pBuf;

//权限个数
DWORD dwCount = pTp->PrivilegeCount;
//pTp->Privileges:存储权限的数组
LUID_AND_ATTRIBUTES* pLaa = pTp->Privileges;

for (int i = 0; i < dwCount;i++,pLaa++)
{
char strName[100] = {};
DWORD dwLen = sizeof(strName);
LookupPrivilegeNameA(0, &pLaa->Luid, strName, &dwLen);
//pLaa->Attributes:0表示关闭,1表示默认开启,2:开启,3:默认开启
printf("权限:【%s】-状态:【%d】\n", strName, pLaa->Attributes);
}
//释放内存
delete pBuf;
}

windows的内存管理

windows引出了虚拟内存的概念

  • 无论物理内存实际有多大,每一个进程都有4GB的虚拟内存空间。
  • 每一个进程再虚拟内存空间的使用上都是相似的,低2GB是用户空间,高2GB是系统空间,低2GB的用户代码空间的代码无法访问高2GB系统空间。
  • 在进程中使用的全部都是虚拟内存,具体虚拟地址到物理地址的装换由操作系统内核完成,故而你无法再自己的进程中访问到其他进程的内存,虽然大家的地址长得如此类似。
  • 一个进程的虚拟空间只有使用一部分与物理内存有映射关系,并且windows尽量保证对于不同的进程的同一份数据,在物理内存中只有一份,分别映射到多个进程中。从而节约内存。

堆内存的管理

​ 堆在windows中也是作为一个对象来管理的,我们可以创建一个堆,之后再堆上分配内存,销毁内存等等,c/c++中的new和malloc最终也是使用windows中的堆对象来分配空间的。

​ 当windows系统创建一个进程后,会为此进程创建一个默认堆,这个默认堆是不能够销毁的。所以有这么一个应用场景,可能会在某一个时间段内,或者某一个任务内需要大量的内存,并且可能是一个个很小很小的分片。如果全部使用默认的堆,那么全用完之后,需要释放就显得相当麻烦,必须一个个的释放,而如果这个作业中途异常,可能导致一些内存分片的句柄丢失,则释放就更加麻烦,又不能枚举出来一个个释放,如此就很容易造成内存丢失,但是再这种情况下,就可以通过HeapCreate创建一个独立的内存堆,当应用完之后,直接HeapDestory将该内存堆所有的内存释放掉。

API 说明
HeapCreate 在进程中创建一个堆,返回一个堆句柄
GetProcessHeap 获取当前进程中的一个堆,返回一个句柄
GetProcessHeaps 获得进程中所有堆,包括堆的数量和各个堆的句柄
HeapAlloc 从指定的堆上分配块
HeapReAlloc 重新分配内存,改变已经分配好的堆内存块大小
GetSystemInfo 获取系统信息
HeapSize 获取制定堆的大小
HeapFree 释放HeapAlloc和HeapReAlloc申请的内存
HeapDestory 销毁由HeapCreate创建的堆
CreateToolhelp32Snapshot 可以分别创建进程,线程,进程模块,进程堆的快照
Heap32First 用来首次调用,获得第一个堆对象的信息
Heap32Next 以后的调用由它来完成,不断获取堆对象信息

创建一个堆

1
2
3
4
5
6
7
8
9
10
//创建一个可增长的堆
HANDLE hHeap = HeapCreate(0, 0, 0);
SYSTEM_INFO si; //系统信息
GetSystemInfo(&si); // 获取系统信息
//在堆上分配3个页面大小的内存
LPVOID lpMem = HeapAlloc(hHeap,
HEAP_ZERO_MEMORY,
si.dwPageSize * 3);
HeapFree(hHeap, 0, lpMem);
HeapDestroy(hHeap);

在已经存在的堆上申请空间

1
2
3
4
5
6
7
HANDLE  hHeap = GetProcessHeap(); // 获取默认堆
SYSTEM_INFO si; //系统信息
GetSystemInfo(&si); // 获取系统信息
//在堆上分配3个页面大小的内存
LPVOID lpMem = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, si.dwPageSize * 3);
HeapFree(hHeap, 0, lpMem);
HeapDestroy( hHeap ); //默认堆是不能被销毁的

虚拟内存管理

需要知道,虚拟内存是按照分页来管理,目前一个内存页是4KB,故而管理内存的时候,总是以4KB为单位进程管理的。管理虚拟内存需要用到下面的函数:

作用 函数名 说明
分配 VirtualAlloc 分配或者预定一块虚拟内存
VirtualAllocEx 可以在其他进程分配或者预定一块虚拟内存
释放 VirtualFree 将一块虚拟内存释放
VirtualFreeEx 可以释放其他进程的内存
锁定与解锁 VirtualLock 可以将内存所动,不能交换到硬盘
VirtualUnlock 为内存解锁
修改保护属性 VirtualProtect 修改一块虚拟内存的属性
VirtualProtectEx 可以修改其他进程的内存属性
读写其他进程内存 ReadProcessMemory 读取远程进程内存数据
WriteProcessMemory 将数据写入远程进程内存
查询内存状态 VirtualQuery 查询内存状态
VirtualQueryEx 可以查询其他进程内存状态

申请与释放虚拟内存

申请一块虚拟内存

1
2
3
4
5
6
7
8
9
10
//申请虚拟内存(本进程)
LPVOID lpBuf = VirtualAlloc(
NULL, //分配的起始位置,函数会自动对齐到整数位置
1, //要分配的内存区域的大小
MEM_COMMIT, //这块内存是要预定还是提交
PAGE_READWRITE //内存的保护属性
);

memcpy(lpBuf, "hello world", sizeof("hello world"));
printf("%s", lpBuf);

释放内存

1
2
3
4
5
6
7
8
9
VirtualFree(
lpBuf, //需要改变状态的内存区域的起始地址
1, //需要改变状态的大小
MEM_DECOMMIT //设置为MEM_DECOMMIT,则将内存变为保留状态,
//当dwSize为0时,参数1必须为VirtualAlloc得
//到的申请好的内存的起始地址
//设置为MEM_RELEASE,则释放内存,将内存变为
//空闲状态
);

整体上来说,虚拟内存有三种状态:空闲的,保留的,提交的

状态 说明
空闲的 进程不能访问这种页面,此页面还没有被分配
保留的 这个页面已经被分配了。但是还未与物理内存映射,因此这里也是不能访问的
提交的 内存已经被分配了,并且也与物理内存映射了,进程已经可以访问这里

​ 当一个进程内核对象被创建成功后,系统内核会给其划拨一部分物理内存,并创建一块虚拟内存,刚创建的虚拟内存只是逻辑上存在的内存,并未与物理内存建立映射关系。此时,这些内存被称为闲置(Free)的或未分配的(unallocated)

​ 如果我们想要使用内存,就必须调用VirtualAlloc()函数来分配其中的某一个内存区域(region),这种分配行为被称之为预定

​ 当系统分配内存空间时,会确保分配内存的起始地址正好是其内存分配粒度的整数倍,X86架构下,粒度是64KB

​ 可以想象到,对于一个虚拟内存空间,大部分的区域都是空闲的,而提交的内存就像是汪洋无际的大海中的几块陆地。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
LPVOID lpvBase = VirtualAlloc(
NULL,
1024 * 64 * 5, //64KB*5
MEM_RESERVE, //预定内存区域
PAGE_NOACCESS //不可访问
);

LPVOID lpvResult = VirtualAlloc(
lpvBase,
1024 * 64 * 1, //64KB*1
MEM_COMMIT, //调拨内存区域
PAGE_READWRITE //可读写
);

//strsafe.h
StringCchCopy((LPWSTR)lpvResult, _countof(L"15PB!"), L"15PB!");
MessageBox(NULL, (LPWSTR)lpvResult, NULL, MB_OK);

虚拟内存的安全属性

每一个页的内存都具有自己的访问属性

属性 描述
PAGE_EXECUTE 可执行
PAGE_EXECUTE_READ 可读可执行
PAGE_EXECUTE_READWRITE 可写可执行
PAGE_EXECUTE_WRITECOPY 可执行,写时复制
PAGE_NOACCESS 不可访问
PAGE_READONLY 只读
PAGE_READWRITE 可读可写
PAGE_WRITECOPY 写时复制

虚拟内存的属性可以通过VirtualProtect来修改

1
2
3
4
5
6
7
8
9
10
11
12
13
LPVOID lpBuf = VirtualAlloc(
NULL,
1,
MEM_COMMIT,
PAGE_READONLY
);
//修改内存属性
DWORD dwOld;

VirtualProtect(lpBuf, 1, PAGE_READWRITE, &dwOld);

memcpy(lpBuf, L"hello world", sizeof(L"hello world"));
MessageBox(NULL, (LPCWSTR)lpBuf, NULL, MB_OK);

在其他进程中分配虚拟内存,读取和修改虚拟内存

VirtualAllocEx,ReadProcessMemory与WriteProcessMemory三个函数,可以实现跨进程的内存分配,读取,写入等操作,是很多安全技术的基础函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
DWORD dwPid;
scanf_s("%d", &dwPid);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
//加强版,跨进程申请内存
LPVOID lpBuf = VirtualAllocEx(hProcess, NULL, 1, MEM_COMMIT, PAGE_READWRITE);

//跨进程写内存
DWORD dwWrite;
WriteProcessMemory(hProcess,
lpBuf, "hello world",
sizeof("hello world"),
&dwWrite);
char szBuf[100] = {};
DWORD dwRead;
//跨进程读内存
ReadProcessMemory(hProcess,
lpBuf, szBuf,
sizeof(szBuf),
&dwRead);

printf("%s", szBuf);

内存映射(文件映射)

  • 文件映射(Mapping)是一种将文件内容映射到进程虚拟内存中的技术
  • 映射成功的文件可以用视图(View)来引用这段内存,从而达到操作位于此段内存中文件的目的
  • 在使用文件映射时需要先创建映射对象,映射对象分为命名的与未命名的,命名的映射对象可以进程跨进程读写

文件映射的作用及优势:

  1. 可以让文件操作变得简单易操作
  2. 文件还是再硬盘中,映射视图是一段内存,因此效率很高
  3. 可以在不同的进程件共享数据
API 说明
GetSystemInfo 获取系统信息,用于确定分配粒度
CreateFileMapping 创建一个mapping对象
OpenFileMapping 打开已命名的mapping对象(可跨进程)
UnmapViewOfFile 取消文件映射
MapViewOfFile 将mapping对象的文件映射到内存
FlushViewOfFile 将映射在内存中的文件写回到硬盘中

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
//获取文件句柄
HANDLE hFile = CreateFile(L"D:\\xxxx",
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE)
return FALSE;
//创建文件映射对象
HANDLE hMap = CreateFileMapping(hFile,
NULL,
PAGE_READWRITE,
NULL,
NULL, //GetFileSize(hFile,NULL)
NULL);
//将文件映射View
LPVOID lpMapAddress = MapViewOfFile(hMap,
FILE_MAP_ALL_ACCESS,
0, 0, 0);
if (lpMapAddress == NULL)
return FALSE;

*(int*)lpMapAddress = 0x12345678;
//将映射的数据写回到硬盘
FlushViewOfFile(lpMapAddress, 4);//This parameter cannot be set to zero?

//取消文件映射
UnmapViewOfFile(lpMapAddress);
//关闭文件映射对象句柄
CloseHandle(hMap);
CloseHandle(hFile);

进程间通讯

进程A:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//创建命名的文件映射对象
HANDLE hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0, 16,
L"hello"); //命名的映射对象
if (hMapFile == NULL || hMapFile == INVALID_HANDLE_VALUE)
return FALSE;
//创建View
LPVOID lpBuf = MapViewOfFile(hMapFile,
FILE_MAP_ALL_ACCESS,
0, 0, //映射偏移量必须是系统内存分配粒度(64KB)的整数倍
16);
if (lpBuf == NULL)
return FALSE;

//将共享数据赋值到文件映射中
memcpy(lpBuf, "hello girl", sizeof("hello girl"));

//循环等待
while (*(PBYTE)lpBuf)
Sleep(200);
//取消Mapping,关闭句柄
UnmapViewOfFile(lpBuf);
CloseHandle(hMapFile);

进程B:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//打开文件映射对象
HANDLE hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS,
FALSE, L"hello");
if (hMap == NULL)
return FALSE;
//创建View
LPVOID lpBuf = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS,
0, 0, 16);
if (lpBuf == NULL)
return FALSE;

//读取内存数据
printf("%s", lpBuf);
//修改内存数据
memcpy(lpBuf, "hello gay", sizeof("hello gay"));

//取消Mapping,关闭句柄
UnmapViewOfFile(lpBuf);
CloseHandle(hMap);

image-20191115165322043

虚拟内存遍历

通过系统提供的API函数VirtualQueryEx()来获取某进程的虚拟内存分布状态,原形如下:

1
2
3
4
5
6
SIZE_T  WINAPI  VirtualQueryEx(
_In_ HANDLE hProcess,
_In_opt_ LPCVOID lpAddress,
_Out_writes_bytes_to_(dwLength,return) PMEMORY_BASIC_INFORMATION lpBuffer,
_In_ SIZE_T dwLength
);

此函数执行后会返回一个MEMORY_BASIC_INFORMATION结构体,里面包含有关于此内存地址的详细信息

1
2
3
4
5
6
7
8
9
typedef struct _MEMORY_BASIC_INFORMATION {
PVOID BaseAddress; //将参数向下取整到页面大小
PVOID AllocationBase; //区域地址,此区域包含传入地址
DWORD AllocationProtect; //此区域在预定时的保护属性
SIZE_T RegionSize; //区域的大小
DWORD State; //区域的页面状态[注1]
DWORD Protect; //页面保护属性
DWORD Type; //页面类型[注2]
} MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;

[注1]:页面状态可以是MEM_FREE(闲置),MEM_RESERVE(预定),MEM_COMMIT(调拨),如果页面状态为MEM_FREE,则AllocationBase、AllocationBase、State、Protect的值都将无效,如果页面状态为MEM_RESERVE,则Protect的值无效
[注2]:页面类型可以为MEM_IMAGE、MEM_MAPPED或MEM_PRIVATE

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// 遍历虚拟内存.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <Windows.h>
#include <list>
using std::list;

enum MEMORYSTATE {
e_stat_free = MEM_FREE,
e_stat_reserve = MEM_RESERVE,
e_stat_commit = MEM_COMMIT
};

enum MEMORYTYPE {
e_type_image = MEM_IMAGE,
e_type_mapped = MEM_MAPPED,
e_type_private = MEM_PRIVATE,
};

typedef struct VMINFO {
DWORD address;
DWORD size;
MEMORYSTATE state;
}VMINFO;



void queryVirtualMemoryStatue(HANDLE hProcess, list<VMINFO>* memoryStatue) {

MEMORY_BASIC_INFORMATION mbi = { 0 };
VMINFO statue = { 0 };
DWORD dwAddress = 0;
DWORD dwSize = 0;
BOOL bRet = FALSE;
while (1) {

bRet = VirtualQueryEx(hProcess,
(LPCVOID)dwAddress,
&mbi,
sizeof(MEMORY_BASIC_INFORMATION));
if (bRet == FALSE)
break;


statue.address = dwAddress;
statue.state = (MEMORYSTATE)mbi.State;
dwSize = mbi.RegionSize;

// 输出内存状态,
// 内存状态用于描述虚拟内存有没有和物理存储器进行关联.
// 或是否被预定.
// free : 闲置,没有预定,没有和物理存储器关联
// reserve: 保留,被预定,没有和物理存储器关联
// commit : 提交,已经和物理存储器关联
switch (statue.state) {
case e_stat_free:
printf("0x%08X : Free\n", statue.address);
break;
case e_stat_reserve:
printf("0x%08X : reserve\n", statue.address);
break;
case e_stat_commit:
printf("0x%08X : commit\n", statue.address);
break;
}

// 如果内存地址已经提交到物理内存,则遍历提交到的每一个内存块.
if (statue.state == e_stat_commit) {

dwSize = 0;
LPVOID dwAllocationBase = mbi.AllocationBase;
DWORD dwBlockAddress = (DWORD)dwAddress;
while (1) {

bRet = VirtualQueryEx(hProcess,
(LPCVOID)dwBlockAddress,
&mbi,
sizeof(MEMORY_BASIC_INFORMATION));
if (bRet == FALSE) {
break;
}


// 判断遍历出来的内存块是否是同一块.(看它们的分配的首地址是否相等.)
// 如果不是,则跳出循环.
if (mbi.AllocationBase != dwAllocationBase)
break;

printf("\t0x%08X ", dwBlockAddress);

// 输出内存类型
// 内存类型表示虚拟内存是以何种方式和物理存储器进行关联
// image : 是从影像文件中映射而来
// mapped : 内存映射
// private: 私有内存,其它进程无法访问.
switch (mbi.Type) {
case e_type_image:
printf(" 类型: image ");
break;
case e_type_mapped:
printf(" 类型: mapped ");
break;
case e_type_private:
printf(" 类型: private ");
break;
default:
break;
}

// 输出内存分页属性
// 内存分页属性用于表示内存分页能够进行何种访问,如读,写,执行,写时拷贝.
if (mbi.Protect == 0)
printf("---");
else if (mbi.Protect & PAGE_EXECUTE)
printf("E--");
else if (mbi.Protect & PAGE_EXECUTE_READ)
printf("ER-");
else if (mbi.Protect & PAGE_EXECUTE_READWRITE)
printf("ERW");
else if (mbi.Protect & PAGE_READONLY)
printf("-R-");
else if (mbi.Protect & PAGE_READWRITE)
printf("-RW");
else if (mbi.Protect & PAGE_WRITECOPY)
printf("WCOPY");
else if (mbi.Protect & PAGE_EXECUTE_WRITECOPY)
printf("EWCOPY");

// 输出内存块的大小.
printf(" 大小:0x%X\n", mbi.RegionSize);

// 索引到下一个内存块
dwBlockAddress += mbi.RegionSize;

// 累加内存块的大小
dwSize += mbi.RegionSize;
}
}

statue.size = dwSize;
memoryStatue->push_back(statue);

// 遍历下一块虚拟内存.
dwAddress += dwSize;
}
}

int _tmain(int argc, _TCHAR* argv[])
{
list<VMINFO> vmList;

queryVirtualMemoryStatue(GetCurrentProcess(), &vmList);

return 0;
}

image-20191115201130599