第14章 探索虚拟内存(2)

14.4 确定地址空间的状态

14.4.1 查询内存状态API 

(1)VirtualQuery(Ex)函数

参数

描述

HANDLE hProcess

要查询的另一个进程的句柄。(注意,这个参数只有VirtualQueryEx才有,VirtualQuery只能查本进程的信息)

LPCVOID pvAddress

需要查询的虚拟内存地址

PMEMORY_BASIC_INFORMATION pmbi

返回的信息被放入该指针指向的MEMORY_BASIC_INFORMATION结构体。

DWORD dwLength

上述结构体的大小,表示返回时复制到上述结构体中的字节数。

(2)MEMORY_BASIC_INFORMATION结构体

字段

描述

BaseAddress

这个值是VirtualQuery(Ex)第一个参数向下取整到页面的边界的值。如区域中的块,可以从AllocationBase开始以4KB倍数的偏移处作为其BaseAddress。

AllocationBase

标识出区域的基地址,该区域包含参数pvAddress所指向的地址。. VirtualAlloc分配的内存称为区域(Region),是一片连续的页.。Windows会以64KB为边界计算区域的起始地址,这里的64KB就是所谓的分配粒度,因此 AllocationBase正是一个区域(Region)的起始地址。

AllocationProtect

标识出在最开始预订区域时为该区域指定的保护属性

RegionSize

标识出区域的大小,以字节为单位。区域的起始地址为BaseAddress,区域中所有页面拥有相同的保护属性、状态及类型

State

标识出区域中页面的状态(MEM_FREE、MEM_RESERVE或MEM_COMMIT)

如果为MEM_FREE,则AllocationBase、AllocationProtect、Protect及Type成员都没有意义。

如果为MEM_RESERVE,那么Protect成员没有意义。

Protect

针对所有相邻页面(前提是其保护属性、状态和类型与其中包含 pvAddress参数中所指定地址的页面相同),标识出它们的保护属性(PAGE_*)

Type

标识出区域中页面的类型(MEM_IMAGE、MEM_MAPPED或MEM_PRIVATE)

14.4.2 自定义的VMQuery(Ex)函数——得到更完整的内存信息

(1)BOOL VMQuery(HANDLE hProcess,LPCVOID pvAddress,PVMQUERY pVMQ);

(2)VMQUERY结构体

字段

描述

备注

PVOID pvRgnBaseAddress

表示虚拟地址空间区域的起始地址,该区域包含了参数pvAddress所指定的地址

DWORD dwRgnProtection

表示在最开始预订地址空间区域时为该区域指定的保护属性(PAGE_*)

SIZE_T RgnSize

表示所预订区域的大小,以字节为单位

DWORD dwRgnStorage

表示用于区域中各块的物理存储器的类型。它可以是以下任一值:MEM_FREE、MEM_IMAGE、MEM_MAPPED或MEM_PRIVATE

DWORD dwRgnBlocks

表示区域中块的数量

DWORD dwRgnGuardBlks

表示区域中具有PAGE_GUARD保护属性标志的块的数量。通常这个值为0或1。如果为1,说明该区域是为了线程栈而预订的。

BOOL bRgnIsAStack

表示该区域是否包含线程栈。该值是通过近似猜测得到的,因为没有任何方法能百分百地肯定一个区域是否包含线程栈

PVOID pvBlkBaseAddress

表示块的起始地址,该块包含了参数pvAddress所指示的地址

DWORD dwBlkProtection

表示块的保护属性

SIZE_T BlkSize

表示块的大小,以字节为单位

DWORD dwBlkStorage

表示块的存储器类型。它可以是以下任一值:MEM_FREE、MEM_IMAGE、MEM_RESERVE、MEM_MAPPED或MEM_PRIVATE

【VMMap程序】显示虚拟内存映射情况

 

/*************************************************************************
Module: VMMap.cpp
Notices:Copyright(c) 2008 Jeffrey Richter & Christophe Nasarre
*************************************************************************/

#include "../../CommonFiles/CmnHdr.h"
#include "../../CommonFiles/ToolHelp.h"
#include "resource.h"
#include "VMQuery.h"
#include <Psapi.h>  //For GetMappedFileName函数
#include <tchar.h>
#include <strsafe.h>

#pragma  comment(lib,"psapi")

//////////////////////////////////////////////////////////////////////////
DWORD g_dwProcessID = 0; //要遍历的进程
BOOL  g_bExpandRegions = FALSE;
CToolhelp g_toolhelp;
//////////////////////////////////////////////////////////////////////////

void CopyControlToClipboard(HWND hWnd){
    TCHAR szClipData[128 * 1024] = { 0 };

    int nCount = ListBox_GetCount(hWnd);
    for (int nNum = 0; nNum < nCount;nNum++){
        TCHAR szLine[1000];
        ListBox_GetText(hWnd, nNum, szLine);
        _tcscat_s(szClipData, _countof(szClipData), szLine);
        _tcscat_s(szClipData, _countof(szClipData), TEXT("
"));
    }

    OpenClipboard(NULL);
    EmptyClipboard();

    //锁定内存块
    HGLOBAL hClipData = GlobalAlloc(GMEM_MOVEABLE |GMEM_ZEROINIT,
                                    sizeof(TCHAR)*(_tcslen(szClipData)+1));
    PTSTR  pClipData = (PTSTR)GlobalLock(hClipData);
    _tcscpy_s(pClipData, _tcslen(szClipData) + 1, szClipData);

#ifdef UNICODE
    BOOL bOk = (hClipData == SetClipboardData(CF_UNICODETEXT,hClipData));
#else
    BOOL bOk = (hClipData == SetClipboardData(CF_TEXT,hClipData));
#endif
    CloseClipboard();

    if (!bOk){
        GlobalFree(hClipData);
        chMB("数据复制到剪贴板时出错!");
    }
}
//////////////////////////////////////////////////////////////////////////

PCTSTR GetMemStorageText(DWORD dwStorage){
    PCTSTR p = TEXT("未知");
    switch (dwStorage){
        case MEM_FREE:        p = TEXT("空闲 "); break;
        case MEM_RESERVE:    p = TEXT("保留 "); break;
        case MEM_IMAGE:        p = TEXT("映像 "); break;
        case MEM_MAPPED:    p = TEXT("己映射"); break;
        case MEM_PRIVATE:   p = TEXT("私有 "); break;
    }
    return p;
}
//////////////////////////////////////////////////////////////////////////

PTSTR GetProtectText(DWORD dwProtect, PTSTR szBuf, size_t chSize, BOOL bShowFlags){

    PCTSTR p = TEXT("未知");
    switch (dwProtect & ~(PAGE_GUARD | PAGE_NOCACHE | PAGE_WRITECOMBINE))
    {
        case PAGE_READONLY:            p = TEXT("-R--"); break;
        case PAGE_READWRITE:        p = TEXT("-RW-"); break;
        case PAGE_WRITECOPY:        p = TEXT("-RWC"); break;
        case PAGE_EXECUTE:            p = TEXT("E---"); break;
        case PAGE_EXECUTE_READ:        p = TEXT("ER--"); break;
        case PAGE_EXECUTE_READWRITE:p = TEXT("ERW-"); break;
        case PAGE_EXECUTE_WRITECOPY:p = TEXT("ERWC"); break;
        case PAGE_NOACCESS:            p = TEXT("----"); break;
    }

    _tcscpy_s(szBuf, chSize, p);
    if (bShowFlags){
        _tcscat_s(szBuf, chSize, TEXT(" "));
        _tcscat_s(szBuf, chSize, (dwProtect & PAGE_GUARD) ? TEXT("G") : TEXT("-"));
        _tcscat_s(szBuf, chSize, (dwProtect & PAGE_NOCACHE) ? TEXT("N") : TEXT("-"));
        _tcscat_s(szBuf, chSize, (dwProtect & PAGE_WRITECOMBINE) ? TEXT("W") : TEXT("-"));
    
    }
    return (szBuf);
}
//////////////////////////////////////////////////////////////////////////

void ConstructRgnInfoLine(HANDLE hProcess, PVMQUERY pVMQ, PTSTR szLine, int cchMaxLen){
    
    StringCchPrintf(szLine, cchMaxLen, TEXT("%p	%s	%12u	"),
                    pVMQ->pvRgnBaseAddress,
                    GetMemStorageText(pVMQ->dwRgnStorage),
                    pVMQ->RgnSize);

    if (pVMQ->dwRgnStorage !=MEM_FREE){
        StringCchPrintf(_tcschr(szLine,0),cchMaxLen -_tcslen(szLine),
                        TEXT("%u	"),pVMQ->dwRgnBlocks);
        GetProtectText(pVMQ->dwRgnProtection, _tcschr(szLine, 0),
                       cchMaxLen - _tcslen(szLine), FALSE);
    
    } else{
        _tcscat_s(szLine, cchMaxLen, TEXT("	"));
    }

    _tcscat_s(szLine, cchMaxLen, TEXT("	"));

    //尝试获取区块的模块路径名称
    if ((pVMQ->dwRgnStorage != MEM_FREE)&&(pVMQ->pvRgnBaseAddress != NULL)){
        MODULEENTRY32 me = { sizeof(me) };
        if (g_toolhelp.ModuleFind(pVMQ->pvRgnBaseAddress,&me)){
            _tcscat_s(szLine, cchMaxLen, me.szExePath);        
        } else{
            int cchLen = _tcslen(szLine);

            //如果不是一个模块,查看一下是否是内存映射文件
            DWORD dwLen = GetMappedFileName(hProcess,
                                            pVMQ->pvRgnBaseAddress,            
                                            szLine + cchLen, cchMaxLen - cchLen);
            //注意:当GetMappedFileName调用失败时,会修改缓冲区中的内容
            if (dwLen == 0){
                szLine[cchLen] = 0; 
            }
        }
    }

    if (pVMQ->bRgnIsAStack){
        _tcscat_s(szLine, cchMaxLen, TEXT("线程栈"));
    }
}
//////////////////////////////////////////////////////////////////////////

//将区域中一个块的信息加入添加到列表框中
void ConstructBlkInfoLine(PVMQUERY pVMQ, PTSTR szLine, int cchMaxLen){
    _stprintf_s(szLine, cchMaxLen, TEXT("   %p	%s	%12u	"), 
                pVMQ->pvBlkBaseAddress,
                GetMemStorageText(pVMQ->dwBlkStorage),
                pVMQ->BlkSize);

    if (pVMQ->dwBlkStorage != MEM_FREE){
        //增加一个空的单元格(因为对于块来说,该列没有个数这列)
        _tcscat_s(szLine, cchMaxLen, TEXT("	"));
        GetProtectText(pVMQ->dwBlkProtection, _tcschr(szLine, 0),
                       cchMaxLen - _tcslen(szLine), TRUE);
    } else{
        _tcscat_s(szLine, cchMaxLen, TEXT("	"));
    }
    _tcscat_s(szLine, cchMaxLen, TEXT("	"));
}
//////////////////////////////////////////////////////////////////////////

void Refresh(HWND hWndLB, DWORD dwProcessID, BOOL bExpandRegions){
    //删除列表框内容,并增加水平滚动条
    ListBox_ResetContent(hWndLB);
    ListBox_SetHorizontalExtent(hWndLB, 300 * LOWORD(GetDialogBaseUnits()));

    //判断进程是否仍在运行
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessID);

    if (hProcess == NULL){
        ListBox_AddString(hWndLB, TEXT("")); //增加空行(为了更好看一点)
        ListBox_AddString(hWndLB, TEXT("   指定的进程己经退出!"));
        return;
    }

    //抓取指定进程的快照
    g_toolhelp.CreateSnapshot(TH32CS_SNAPALL, dwProcessID);

    //遍历进程地址空间
    BOOL bOk = TRUE;
    PVOID pvAddress = NULL;

    SetWindowRedraw(hWndLB, FALSE); //遍历期间,禁止刷新列表框界面
    while (bOk){
        VMQUERY vmq;
        bOk = VMQuery(hProcess, pvAddress, &vmq);

        if (bOk){
            //为区域内的每个块创建一行来显示
            TCHAR szLine[1024];
            ConstructRgnInfoLine(hProcess, &vmq, szLine, _countof(szLine));
            ListBox_AddString(hWndLB, szLine);

            //如果需要展开块,更增加显示各块的信息
            if (bExpandRegions){
                for (DWORD dwBlock = 0; bOk && (dwBlock < vmq.dwRgnBlocks);dwBlock++){
                
                    ConstructBlkInfoLine(&vmq, szLine, _countof(szLine));
                    ListBox_AddString(hWndLB, szLine);

                    //获取下一个区域的地址(注意,不是块)
                    pvAddress = ((PBYTE)pvAddress + vmq.BlkSize);
                    if (dwBlock<vmq.dwRgnBlocks-1){
                        //区域的最后一个块不需要查询
                        bOk = VMQuery(hProcess, pvAddress, &vmq);
                    }
                }
            }

            //获取下一个区域(注意是区域,不是区域内的块)
            pvAddress = ((PBYTE)vmq.pvRgnBaseAddress + vmq.RgnSize);
        }
    }

    SetWindowRedraw(hWndLB, TRUE);  //遍历完成,刷新列表框界面
    CloseHandle(hProcess);
}

//////////////////////////////////////////////////////////////////////////
void Dlg_OnSize(HWND hWnd, UINT state, int cx, int cy){
    //将列表框铺满客户区
    SetWindowPos(GetDlgItem(hWnd, IDC_LISTBOX), NULL, 0, 0, cx, cy,SWP_NOZORDER);
}

//////////////////////////////////////////////////////////////////////////
void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtrl, UINT codeNotity)
{
    switch (id)
    {
    case IDCANCEL:
        EndDialog(hwnd, id);
        break;

    case ID_REFRESH:
        Refresh(GetDlgItem(hwnd, IDC_LISTBOX), g_dwProcessID, g_bExpandRegions);
        break;

    case ID_EXPANDREGIONS:
        g_bExpandRegions = !g_bExpandRegions;
        Refresh(GetDlgItem(hwnd, IDC_LISTBOX), g_dwProcessID, g_bExpandRegions);
        break;

    case ID_COPYTOCLIPBOARD:
        CopyControlToClipboard(GetDlgItem(hwnd, IDC_LISTBOX));
        break;
    }
}

//////////////////////////////////////////////////////////////////////////
BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam)
{
    chSETDLGICONS(hwnd, IDI_VMMAP);

    //在标题栏中正在分析的是哪个进程
    TCHAR szCaption[MAX_PATH * 2];
    GetWindowText(hwnd, szCaption, _countof(szCaption));

    g_toolhelp.CreateSnapshot(TH32CS_SNAPALL, g_dwProcessID);
    PROCESSENTRY32 pe = { sizeof(pe) };
    StringCchPrintf(&szCaption[_tcslen(szCaption)], _countof(szCaption) - _tcslen(szCaption),
                    TEXT("(PID=%u "%s")"),
                    g_dwProcessID,
                    g_toolhelp.ProcessFind(g_dwProcessID, &pe) ? pe.szExeFile : TEXT("未知"));
    SetWindowText(hwnd, szCaption);

    //默认显示方式为最大化
    ShowWindow(hwnd, SW_MAXIMIZE);

    //设置列表框各列的宽度
    int  aTabs[5];
    aTabs[0] = 48;
    aTabs[1] = aTabs[0] + 40;
    aTabs[2] = aTabs[1] + 52;//Count
    aTabs[3] = aTabs[2] + 12;//flags
    aTabs[4] = aTabs[3] + 20;//描述

    ListBox_SetTabStops(GetDlgItem(hwnd, IDC_LISTBOX), _countof(aTabs), aTabs);

    //强制刷新列表框
    Refresh(GetDlgItem(hwnd,IDC_LISTBOX),g_dwProcessID,g_bExpandRegions);
    return (TRUE);
}

//////////////////////////////////////////////////////////////////////////
INT_PTR WINAPI Dlg_Proc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam){
    switch (uMsg)
    {
        chHANDLE_DLGMSG(hDlg, WM_INITDIALOG, Dlg_OnInitDialog);
        chHANDLE_DLGMSG(hDlg, WM_COMMAND, Dlg_OnCommand);
        chHANDLE_DLGMSG(hDlg, WM_SIZE, Dlg_OnSize);
    }
    return (FALSE);
}

//////////////////////////////////////////////////////////////////////////
int WINAPI _tWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd)
{
    CToolhelp::EnablePrivilege(SE_DEBUG_NAME, TRUE);

    g_dwProcessID = _ttoi(lpCmdLine);
    if (g_dwProcessID == 0){
        g_dwProcessID = GetCurrentProcessId();
    }
    DialogBox(hInstance, MAKEINTRESOURCE(IDD_VMMAP), NULL, Dlg_Proc);
    CToolhelp::EnablePrivilege(SE_DEBUG_NAME, FALSE);
}

//VMQuery.h

/*************************************************************************
Module: VMQuery.h
Notices:Copyright(c) 2008 Jeffrey Richter & Christophe Nasarre
*************************************************************************/
#pragma once
#include <windows.h>

//////////////////////////////////////////////////////////////////////////
typedef struct{
    //区域信息
    PVOID    pvRgnBaseAddress;
    DWORD    dwRgnProtection; //PAGE_*
    SIZE_T    RgnSize;
    DWORD   dwRgnStorage;    //MEM_*:Free,Image,Mapped,Private
    DWORD   dwRgnBlocks;
    DWORD   dwRgnGuardBlks;  //If>0,表示区域包含线程栈
    BOOL    bRgnIsAStack;    //TRUE:区域包含线程栈

    //块信息
    PVOID    pvBlkBaseAddress;
    DWORD    dwBlkProtection;  //PAGE_*
    SIZE_T  BlkSize;
    DWORD   dwBlkStorage;     //MEM_*:Free,Reserve,Image,Mapped,Private
}VMQUERY, *PVMQUERY;

//////////////////////////////////////////////////////////////////////////

BOOL VMQuery(HANDLE hProcess, LPCVOID pvAddress, PVMQUERY pVMQ);


///////////////////////////////文件结束///////////////////////////////////

//VMQuery.cpp

/*************************************************************************
Module: VMQuery.cpp
Notices:Copyright(c) 2008 Jeffrey Richter & Christophe Nasarre
*************************************************************************/

#include "../../CommonFiles/CmnHdr.h"
#include "VMQuery.h"

//////////////////////////////////////////////////////////////////////////
//Helper 结构体
typedef struct{
    SIZE_T    RgnSize;
    DWORD   dwRgnStorage;    //MEM_*:Free,Image,Mapped,Private
    DWORD   dwRgnBlocks;
    DWORD   dwRgnGuardBlks;  //If>0,表示区域包含线程栈
    BOOL    bRgnIsAStack;    //TRUE:区域包含线程栈
}VMQUERY_HELP,*PVMQUERY_HELP;
//////////////////////////////////////////////////////////////////////////

//全局变量用来保存CPU分配粒度
static DWORD gs_dwAllocGran = 0;
//////////////////////////////////////////////////////////////////////////

//遍历一个区域中所有的块,并将汇总信息通过VMQUERY_HELP返回
static BOOL VMQueryHelp(HANDLE hProcess, LPCVOID pvAddress, VMQUERY_HELP* pVMQHelp){
    ZeroMemory(pVMQHelp, sizeof(*pVMQHelp));
    
    //获得区域信息
    MEMORY_BASIC_INFORMATION  mbi;
    BOOL bOk = (sizeof(mbi) == 
                VirtualQueryEx(hProcess, pvAddress, &mbi, sizeof(mbi)));
    if (!bOk){
        return bOk;  //错误的内存地址,返回FALSE
    }

    pVMQHelp->dwRgnStorage = mbi.Type;

    //从区域起始地址开始遍历
    PVOID pvRgnBaseAddress = mbi.AllocationBase;//(这里是不会变的)

    //块的地址指针,先指向第1个块
    PVOID pvAddressBlk = pvRgnBaseAddress;
    for (;;){
        //获得当前块的信息
        bOk = (VirtualQueryEx(hProcess, pvAddressBlk, &mbi,
            sizeof(mbi))==sizeof(mbi));

        if (!bOk)
            break;  //无法获得信息,则退出循环

        //块是否在同一个区域?
        if (mbi.AllocationBase != pvRgnBaseAddress)
            break;   //如果是下一个区域中的块,则退出循环

        //己经获得了区域中的一个块
        pVMQHelp->dwRgnBlocks++;   //找到一个块,块数加1
        pVMQHelp->RgnSize += mbi.RegionSize; //将块大小累加到区域大小

        //如果块具有PAGE_GUARD属性,则计数加1
        if ((mbi.Protect & PAGE_GUARD) == PAGE_GUARD)
            pVMQHelp->dwRgnGuardBlks++;

        //判断区域的物理存储器类型,因为块可能从MEM_IMAGE转为MEM_PRIVATE
        //或从MEM_MAPPED转为MEM_PRIVATE;而MEM_PRIVATE总是可以被MEM_IMAGE
        //或MEM_MAPPED重写。
        if (pVMQHelp->dwRgnStorage == MEM_PRIVATE)
            pVMQHelp->dwRgnStorage = mbi.Type;

        //获取一下个块的地址
        pvAddressBlk = (PVOID)((PBYTE)pvAddressBlk + mbi.RegionSize);
    }

    //最后检查区域是否包含有线程栈
    pVMQHelp->bRgnIsAStack = (pVMQHelp->dwRgnGuardBlks > 0);
    return (TRUE);
}
//////////////////////////////////////////////////////////////////////////

BOOL VMQuery(HANDLE hProcess, LPCVOID pvAddress, PVMQUERY pVMQ){
    if (gs_dwAllocGran ==0){
        //第1次调用时,先获取CPU分配粒度
        SYSTEM_INFO  sinf;
        GetSystemInfo(&sinf);
        gs_dwAllocGran = sinf.dwAllocationGranularity;
    }

    ZeroMemory(pVMQ, sizeof(*pVMQ));//初始化VMQUERY结构体

    //通过传入的pvAddress获得MEMORY_BASIC_INFORMATION信息
    MEMORY_BASIC_INFORMATION mbi;
    BOOL bOk = (VirtualQueryEx(hProcess, pvAddress, &mbi, sizeof(mbi))
                == sizeof(mbi));

    if (!bOk){
        return (bOk);   //错误的内存地址,返回失败
    }

    //首先,填充块字段
    switch (mbi.State) //3种状态:FREE、RESERVE和COMMIT
    {
        case MEM_FREE:  //空闲块
            pVMQ->pvBlkBaseAddress = NULL;
            pVMQ->BlkSize = 0;
            pVMQ->dwBlkProtection = 0;
            pVMQ->dwBlkStorage = MEM_FREE;
            break;

        case MEM_RESERVE: //保留,(未提交到该块存储器里)
            pVMQ->pvBlkBaseAddress = mbi.BaseAddress;
            pVMQ->BlkSize = mbi.RegionSize;

            //对于一个未提交的块,mbi.Protect是无效的。所以可以让
            //块的属性继承自区域的保护属性
            pVMQ->dwBlkProtection = mbi.AllocationProtect;
            pVMQ->dwBlkStorage = MEM_RESERVE;
            break;

        case MEM_COMMIT: //己提交块
            pVMQ->pvBlkBaseAddress = mbi.BaseAddress;
            pVMQ->BlkSize = mbi.RegionSize;
            pVMQ->dwBlkProtection = mbi.Protect;
            pVMQ->dwBlkStorage = mbi.Type;
            break;

        default:
            DebugBreak();
            break;
    }

    //然后,填充区域字段
    VMQUERY_HELP VMQHelp;
    switch (mbi.State)
    {
        case MEM_FREE: //空闲
            pVMQ->pvRgnBaseAddress = mbi.BaseAddress;
            pVMQ->dwRgnProtection = mbi.AllocationProtect;
            pVMQ->RgnSize = mbi.RegionSize;
            pVMQ->dwRgnStorage = MEM_FREE;
            pVMQ->dwRgnBlocks = 0;
            pVMQ->dwRgnGuardBlks = 0; //1为线程栈所预订
            pVMQ->bRgnIsAStack = FALSE;
            break;

        case MEM_RESERVE:
            pVMQ->pvRgnBaseAddress = mbi.AllocationBase; //这里与上面的块是不同的。
            pVMQ->dwRgnProtection = mbi.AllocationProtect;

            //迭代,通过所有块属性来推荐整个区域的属性
            VMQueryHelp(hProcess, pvAddress, &VMQHelp);
            pVMQ->RgnSize = VMQHelp.RgnSize;
            pVMQ->dwRgnStorage = VMQHelp.dwRgnStorage;
            pVMQ->dwRgnBlocks = VMQHelp.dwRgnBlocks;
            pVMQ->dwRgnGuardBlks = VMQHelp.dwRgnGuardBlks;
            pVMQ->bRgnIsAStack = VMQHelp.bRgnIsAStack;        
            break;

        case MEM_COMMIT: //己提交
            pVMQ->pvRgnBaseAddress = mbi.AllocationBase; //这里与上面的块是不同的。
            pVMQ->dwRgnProtection = mbi.AllocationProtect;

            //迭代,通过所有块属性来推荐整个区域的属性
            VMQueryHelp(hProcess, pvAddress, &VMQHelp);
            pVMQ->RgnSize = VMQHelp.RgnSize;
            pVMQ->dwRgnStorage = VMQHelp.dwRgnStorage;
            pVMQ->dwRgnBlocks = VMQHelp.dwRgnBlocks;
            pVMQ->dwRgnGuardBlks = VMQHelp.dwRgnGuardBlks;
            pVMQ->bRgnIsAStack = VMQHelp.bRgnIsAStack;
            break;

        default:
            DebugBreak();
            break;
    }

    return bOk;
}
////////////////////////////////////文件结束////////////////////////////////

//resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 14_VMMap.rc 使用
//
#define IDD_VMMAP                       100
#define IDC_LISTBOX                     101
#define IDI_VMMAP                       102
#define IDR_MENU1                       102
#define IDR_VMMAP                       103
#define ID_REFRESH                      40001
#define ID_EXPANDREGIONS                40002
#define ID_COPYTOCLIPBOARD              40003

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        104
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1001
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

//VMMap.rc

// Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"

/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

/////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources

#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED

#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "resource.h"
END

2 TEXTINCLUDE 
BEGIN
    "#include ""winres.h""
"
    ""
END

3 TEXTINCLUDE 
BEGIN
    "
"
    ""
END

#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//

IDD_VMMAP DIALOGEX 0, 0, 337, 250
STYLE DS_SETFONT | DS_CENTER | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "虚拟内存映射"
MENU IDR_VMMAP
FONT 10, "宋体", 400, 0, 0x86
BEGIN
    LISTBOX         IDC_LISTBOX,0,0,334,248,LBS_USETABSTOPS | LBS_NOINTEGRALHEIGHT | NOT WS_BORDER | WS_VSCROLL | WS_HSCROLL | WS_GROUP | WS_TABSTOP
END


/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//

#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
    IDD_VMMAP, DIALOG
    BEGIN
        RIGHTMARGIN, 250
    END
END
#endif    // APSTUDIO_INVOKED


/////////////////////////////////////////////////////////////////////////////
//
// Menu
//

IDR_VMMAP MENU
BEGIN
    MENUITEM "刷新(&R)!",                     ID_REFRESH
    MENUITEM "展开区域(&E)!",                   ID_EXPANDREGIONS
    MENUITEM "复制(&C)!",                     ID_COPYTOCLIPBOARD
END


/////////////////////////////////////////////////////////////////////////////
//
// Icon
//

// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_VMMAP               ICON                    "VMMap.ico"
#endif    // 中文(简体,中国) resources
/////////////////////////////////////////////////////////////////////////////



#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//


/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED
原文地址:https://www.cnblogs.com/5iedu/p/4853561.html