Window权限管理

  1. UAC

    OnInitDialog() 函数中初始化:

    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(m_Button.m_hWnd,
    !bFullToken);
    else
    ::ShowWindow(m_Button.m_hWnd, SW_SHOW);
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    void CUACDlg::OnBnClickedButton1()
    {
    // TODO: 在此添加控件通知处理程序代码
    // 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(SW_SHOWNORMAL);
    }
  2. 提权:

    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
    //提升为调试权限
    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);
    }
    //遍历权限
    void ShowPrivilege()
    {
    //打开访问令牌
    HANDLE hToken;
    //OpenProcessToken函数用来打开与进程相关联的访问令牌
    //GetCurrentProcess获取当前进程的一个伪句柄
    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;
    }
    int main()
    {
    EnableDebugPrivilege(TRUE);
    ShowPrivilege();
    system("pause");
    return 0;
    }

虚拟内存

  • 无论物理内存多大,每个进程都有4GB的虚拟内存空间

  • 每个进程在虚拟内存空间的使用上都是相似的,低2GB是用户空间,高2GB是系统空间,低2GB的用户代码空间的代码无法访问高2GB的系统空间

  • 在进程中使用的全部是虚拟地址,具体虚拟地址到物理地址的转换由操作系统内核完成,故无法在自己的进程中访问到其他进程的内存

  • 一个进程的虚拟空间只有一部分与物理内存有映射关系,并且 Windows 尽量保证对于不同进程的同一份数据,在物理内存中只有一份,分别映射到多个进程中,从而节约内存

  • 当各个进程所使用的内存数量超出物理内存时,操作系统还能将物理内存中暂时用不到的数据交换到硬盘中

  1. 堆内存的管理 :可以通过 HeapCreate 创建一个堆,当应用完后直接 HeapDestroy 将该内存堆所有内存释放掉

    1
    2
    3
    4
    5
    6
    //创建一个堆使用
    HANDLE WINAPI HeapCreate(
    _In_ DWORD flOptions,
    _In_ SIZE_T dwInitialSize,
    _In_ SIZE_T dwMaximumSize
    );

    第一参数 flOptions 表示对堆的操作如何进行,可以是0,HEAP_NO_SERIALIZEHEAP_GENERATE_EXCEPTIONSHEAP_CREATE_ENABLE_EXECUTE
    默认情况下,对堆的访问会依次进行,多个线程会从同一个堆中分配释放内存,堆数据不被破坏。
    但在多线程情况下,要尽量避免使用 HEAP_NO_SERIALIZE。如果想在堆中放可执行代码,必须使用 HEAP_CREATE_ENABLE_EXECUTE
    第二参数 dwInitialSize 表示开始时分给堆的字节数。
    第三参数 dwMaximumSize 表示所能增长到的最大大小,如果指定为0的话,则堆可以在需要的情况下不断增大。

    从堆里分配内存块,需要调用 HeapAlloc() 函数

    1
    2
    3
    4
    5
    LPVOID WINAPI HeapAlloc(
    _In_ HANDLE hHeap,
    _In_ DWORD dwFlags,
    _In_ SIZE_T dwBytes
    );

    第二参数用来指定一些标志,会对分配结果产生影响。目前只支持这三个参数,HEAP_ZERO_MEMORYHEAP_GENERATE_EXCEPTIONSHEAP_NO_SERIALIZE

    HEAP_ZERO_MEMORY 是把内存清空

    HEAP_GENERATE_EXCEPTIONS 告诉系统,如果没有足够的空间,就抛出异常
    HEAP_NO_SERIALIZE 用来强制系统不要把这次分配结果与其他线程的访问排列起来,有可能破坏堆的完整

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    //创建一个可增长的堆
    HANDLE hHeap = HeapCreate(0, 0, 0);
    SYSTEM_INFO si; //系统信息
    GetSystemInfo(&si); // 获取系统信息
    //在堆上分配3个页面大小的内存
    LPVOID lpMem = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 3/*si.dwPageSize * 3*/);
    HeapFree(hHeap, 0, lpMem);
    HeapDestroy(hHeap);
    1
    2
    3
    4
    5
    6
    7
    8
    //获取默认堆 
    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 );//默认堆是不能被销毁的
  1. 虚拟内存的管理:虚拟内存是按照分页来管理的,一个内存页是4KB

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

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

    属性 描述
    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
    14
    15
    16
    //申请一块虚拟内存
    HANDLE VirtualAlloc(
    _In_opt_ LPVOID lpAddress, //分配的起始地址,函数会自动对齐到整数位置
    _In_SIZE_T dwSize, //要分配的内存区域的大小
    _In_DWORD flAllocationType, //这块内存是要预定还是提交
    _In_DWORD flProtect //内存的保护属性
    );

    //VirtualAllocEx函数的作用是在指定进程的虚拟空间保留或提交内存区域,除非指定MEM_RESET参数,否则将该内存区域置0
    LPVOID VirtualAllocEx(
    HANDLE hProcess, //申请内存所在的进程句柄
    LPVOID lpAddress, //保留页面的内存地址,一般用NULL自动分配
    SIZE_T dwSize, //欲分配的内存大小,字节单位,注意实际分配的内存大小是页内存大小的整数倍
    DWORD flAllocationType,//MEM_COMMIT,为特定的页面区域分配内存中或磁盘的页面文件中的物理存储,......
    DWORD flProtect//PAGE_READWRITE区域可被应用程序读写,......
    );
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //释放一块内存
    BOOL WINAPI VirtualFree(
    LPVOID lpAddress, //需要改变状态的内存区域的起始地址
    _In_SIZE_T dwSize, //需要改变状态的大小
    _In_DWORD dwFreeType //设为MEM_DECOMMIT,则将内存变为保留状态,当dwSize为0,参数1必须为VirtualAlloc得到的申请好的内存的起始地址;设为MEM_RELEASE,则释放内存,将内存变为空闲状态
    );

    //用VirtualFreeEx 在其它进程中释放申请的虚拟内存空间
    BOOL WINAPI VirtualFreeEx(
    HANDLE hProcess,//目标进程的句柄。该句柄必须拥有 PROCESS_VM_OPERATION 权限
    LPVOID lpAddress,//指向要释放的虚拟内存空间首地址的指针,如果 dwFreeType 为 MEM_RELEASE, 则该参数必须为VirtualAllocEx的返回值
    SIZE_T dwSize,//虚拟内存空间的字节数。如果 dwFreeType 为 MEM_RELEASE,则 dwSize 必须为0 . 按 VirtualAllocEx申请时的大小全部释放。如果dwFreeType 为 MEM_DECOMMIT, 则释放从lpAddress 开始的一个或多个字节 ,即 lpAddress +dwSize
    DWORD dwFreeType //释放类型
    );

    示例1:跨进程读写数据

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    void temp()
    {
    DWORD dwPid;
    scanf_s("%d", &dwPid);//需要输入一个进程ID
    //打开一个已存在的进程对象,并返回进程的句柄,PROCESS_ALL_ACCESS-获取所有权限
    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\n", szBuf);
    VirtualFreeEx(hProcess, lpBuf, 0, MEM_RELEASE);
    }

    运行此程序,输入另一个程序的进程ID,并附加在OD中

    image-20191115160934069

    image-20191115161107743

    VS中单步调试,获取 lpBuf 的地址,0x001d0000,在OD中查看

    image-20191115161340681

    内容为空,继续单步调试,到跨进程写内存,再查看

    image-20191115161515213

    发现写入成功,继续运行程序

    image-20191115161610982

    发现读取成功

    示例2:在本进程中读写数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    int main()
    {
    //temp();
    //申请本进程的虚拟内存
    LPVOID lpBuf = VirtualAlloc(NULL, 1, MEM_COMMIT, PAGE_READWRITE);
    //修改内存属性
    DWORD dwOld;
    //修改内存的保护属性为可读可写
    VirtualProtect(lpBuf, 1, PAGE_READWRITE, &dwOld);
    memcpy(lpBuf, "Hello World", sizeof("Hello World"));
    printf("%s", lpBuf);
    VirtualFree(lpBuf, 0, MEM_RELEASE);//释放虚拟内存
    return 0;
    }

    image-20191115163150253

    image-20191115163204515

  1. 内存映射:文件映射是一种将文件内容映射到进程虚拟内存中的技术

    文件映射的作用及优势:

    • 可让文件操作变得简单
    • 文件还是在硬盘中,映射视图是一段内存,效率高
    • 可以在不同的进程间共享数据
    API 说明
    GetSystemInfo 获取系统信息,用于确定分配粒度
    CreateFileMapping 创建一个 mapping 对象
    OpenFileMapping 打开已命名的 mapping 对象(可跨进度)
    UnmapViewOfFile 取消文件映射
    MapViewOfFile 将 mapping 对象的文件映射到内存
    FlushViewOfFile 将映射在内存中的文件写会到硬盘
    1
    2
    3
    4
    5
    6
    7
    8
    HANDLE WINAPI CreateFileMapping(
    _In_HANDLE hFile,//指定欲在其中创建映射的一个文件句柄,若为0xFFFFFFFF(-1,即INVALID_HANDLE_VALUE)表示在页面文件中创建一个可共享的文件映射
    _In_opt_LPSECURITY_ATTRIBUTES lpAttributes,//它指明返回的句柄是否可以被子进程所继承,指定一个安全对象,在创建文件映射时使用。如果为NULL(用ByVal As Long传递零),表示使用默认安全对象
    _In_DWORD flProtect,//
    _In_DWORD dwMaximumSizeHigh,//文件映射的最大长度的高32位
    _In_DWORD dwMaximumSizeLow,//文件映射的最大长度的低32位,如这个参数和dwMaximumSizeHigh都是0,就用磁盘文件的实际长度
    _In_opt_LPCTSTR lpName//指定文件映射对象的名字,如存在这个名字的一个映射,函数就会打开它,用vbNullString可以创建一个无名的文件映射
    );

示例:

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
#include<windows.h>
#include<stdio.h>
//创建文件映射(关联文件)
void FileMapToFile(const char* pFilePath) {
//获取文件句柄
HANDLE hFile = CreateFileA(pFilePath, GENERIC_READ | GENERIC_WRITE,
TRUE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
printf("文件打开失败\n");
return;
}
//创建文件映射对象
HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, NULL,
NULL, NULL);
//关联虚拟内存,将hMap映射到内存
LPVOID lpBuf = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
*(int*)lpBuf = 0x88888888;//修改前4个字节
//及时刷新到磁盘文件
FlushViewOfFile(lpBuf, 4);
//取消文件映射
UnmapViewOfFile(lpBuf);
//关闭文件映射句柄
CloseHandle(hMap);
CloseHandle(hFile);
}
int main()
{
FileMapToFile("CKme.exe");
return 0;
}

进程间的通讯示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//进程间通讯(共享内存),发送信息“Hello World”
void ShareMemory()
{
//创建文件映射对象
HANDLE hMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL,
PAGE_READWRITE, 0, 16, "hello");
//与虚拟内存关联
LPVOID lpBuf = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS,
0, 0, 16);
//写通讯数据
memcpy(lpBuf, "Hello World", sizeof("Hello World"));
//取消映射,不需要通讯再执行
//UnmapViewOfFile(lpBuf);
//CloseHandle(hMap);
}
int main()
{
ShareMemory();
system("pause");
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//接收前一个进程的消息
int main()
{
//打开已命名的文件映射对象
HANDLE hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, "hello");
//关联虚拟内存
LPVOID lpBuf = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 16);
getchar();//暂停等待另一头把数据写入到共享内存
printf("%s\n", lpBuf);
//取消映射
UnmapViewOfFile(hMap);
//关闭句柄
CloseHandle(hMap);
system("pause");
return 0;
}
  1. 虚拟内存遍历

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    SIZE_T WINAPI VirtualQueryEx(
    _In_ HANDLE hProcess, //进程句柄
    _In_opt_ LPCVOID lpAddress, //查询地址
    _Out_ PMEMORY_BASIC_INFORMATION lpBuffer, //内存的信息
    _In_ SIZE_T dwLengyh //传出结构体的大小
    );
    //此函数执行后会返回一个MEMORY_BASIC_INFORMATION结构体,里面包含有关于此内存地址的详细信息
    typedef struct _MEMORY_BASIC_INFORMATION{
    PVOID BaseAddress;//将参数向下取整到页面大小
    PVOID AllocationBase;//区域地址,包含传入地址
    DWORD AllocationProtect;//此区域在预定时的保护属性
    SIZE_T RegionSize;//区域的大小
    DWORD State;//区域的页面状态
    DWORD Protect;//页面保护属性
    DWORD Type;//页面类型
    }MEMORY_BASIC_INFORMATION, *PMEMORY_BASIC_INFORMATION;
    //注:页面状态可以是MEM_FREE(闲置),MEM_RESERVE(预定),MEM_COMMIT(调拨),若为MEM_FREE,则AllocationBase,AllocationProtect,State,Protect的值都将无效,若为MEM_RESERVE,则Protect的值无效
    //注:页面类型可以为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
#include<Windows.h>
#include<stdio.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 WMINFO {
DWORD address;
DWORD size;
MEMORYSTATE state;
}VMINFO;
void queryVirtualMemoryStatue(HANDLE hProcess, list<VMINFO>* memoryStatue) {
MEMORY_BASIC_INFORMATION mbi = { 0 };
WMINFO statue = { 0 };
DWORD dwAddress = 0;
DWORD dwSize = 0;
BOOL bRet = FALSE;
while (1) {
bRet = VirtualQueryEx(hProcess, (LPVOID)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 main()
{
list<VMINFO>vmList;
queryVirtualMemoryStatue(GetCurrentProcess(), &vmList);
system("pause");
return 0;
}