虛擬內存遍歷 VMView

本代碼由15PB創始人A1Pass原創,網絡資源稀少,轉載請註明出處,尊重作者勞動成果!


VMQuery.h:

#pragma once
#include <windows.h>
#include <vector>
using std::vector;

typedef struct _VMQUERYEX
{
    int    nRgnSize;       // 預定區域大小
    DWORD  dwRgnStorage;   // 預劃分區域的物理存儲器類型(MEM_*: Free, Image, Mapped, Private)
    DWORD  dwRgnBlocks;    // 區域中塊的數量
    DWORD  dwRgnGuardBlks; // 具有PAGE_GUARD保護屬性的塊的數量,如果>0,則此區域是爲線程棧而預定的
    BOOL   bRgnIsAStack;   // 此區域是否包含線程棧,是的話則此區域是爲線程棧而預定的
}VMQUERYEX;

typedef struct _VMQUERY
{
    // 區域信息
    PVOID  lpRgnBaseAddress; // 預劃分內存區域的起始地址
    DWORD  dwRgnProtection;  // 預劃分此內存區域時的原始保護屬性(PAGE_*)
    int    nRgnSize;         // 預劃分區域大小
    DWORD  dwRgnStorage;     // 預劃分區域的物理存儲器類型(MEM_*: Free, Image, Mapped, Private)
    DWORD  dwRgnBlocks;      // 預劃分區域中塊的數量
    DWORD  dwRgnGuardBlks;   // 具有PAGE_GUARD保護屬性的塊的數量,如果>0,則此區域是爲線程棧而預定的
    BOOL   bRgnIsAStack;     // 此區域是否包含線程棧,是的話則此區域是爲線程棧而預定的

    // 塊信息
    PVOID  lpBlkBaseAddress; // 內存塊的起始地址
    DWORD  dwBlkProtection;  // 內存塊的保護屬性(PAGE_*)
    int    nBlkSize;         // 內存塊的大小
    DWORD  dwBlkStorage;     // 內存塊的存儲類型(MEM_*: Free, Reserve, Image, Mapped, Private)

    // 文本信息
    BOOL   bIsExpandInfo;        // 此條信息是否爲區域展開後的塊信息
    WCHAR  szRgnBaseAddress[16]; // [文本]預劃分內存區域的起始地址
    WCHAR  szRgnProtection[16];  // [文本]原始保護屬性
    WCHAR  szRgnSize[16];        // [文本]區域大小
    WCHAR  szRgnStorage[16];     // [文本]物理存儲器類型
    WCHAR  szRgnBlocks[8];       // [文本]塊的數量
    WCHAR  szRgnGuardBlks[8];    // [文本]具有PAGE_GUARD保護屬性的塊的數量

    WCHAR  szBlkBaseAddress[10]; // [文本]內存塊的起始地址
    WCHAR  szBlkProtection[16];  // [文本]內存塊的保護屬性
    WCHAR  szBlkSize[16];        // [文本]內存塊的大小
    WCHAR  szBlkStorage[16];     // [文本]內存塊的存儲類型
} VMQUERY, *PVMQUERY;

class CVMQuery
{
public:
    CVMQuery();
    ~CVMQuery();
public:
    BOOL GetVMInfo(HANDLE hProcess, LPCVOID lpAddress, PVMQUERY pVMQ);

protected:
    BOOL GetVMBlockInfo(HANDLE hProcess, LPCVOID lpAddress, VMQUERYEX *pVMBlock);
    void GetProtectText(DWORD dwProtect, PTSTR szBuf, int nSize, BOOL bShowFlags);
    void GetMemStorageText(DWORD dwStorage, PTSTR szBuf, int nSize);
private:
    vector<VMQUERY> m_vecVMInfoList;
};


VMQuery.cpp:

#include "stdafx.h"
#include "VMQuery.h"
#include <Strsafe.h>

CVMQuery::CVMQuery()
{
}

CVMQuery::~CVMQuery()
{
}

void CVMQuery::GetProtectText(DWORD dwProtect, PTSTR szBuf, int nSize, BOOL bShowFlags)
{
    PCTSTR p = L"[Unknown]";
    switch ( dwProtect & ~(PAGE_GUARD|PAGE_NOCACHE|PAGE_WRITECOMBINE) )
    {
    case PAGE_READONLY:          p = L"[ -R--  ]"; break;
    case PAGE_READWRITE:         p = L"[ -RW-  ]"; break;
    case PAGE_WRITECOPY:         p = L"[ -RWC  ]"; break;
    case PAGE_EXECUTE:           p = L"[ E---  ]"; break;
    case PAGE_EXECUTE_READ:      p = L"[ ER--  ]"; break;
    case PAGE_EXECUTE_READWRITE: p = L"[ ERW-  ]"; break;
    case PAGE_EXECUTE_WRITECOPY: p = L"[ ERWC  ]"; break;
    case PAGE_NOACCESS:          p = L"[ ----  ]"; break;
    }
    StringCchCopy(szBuf, nSize, p);

    if (bShowFlags)
    {
        StringCchCat(szBuf, nSize, L" ");
        StringCchCat(szBuf, nSize, (dwProtect&PAGE_GUARD) ? L"G":L"-");
        StringCchCat(szBuf, nSize, (dwProtect&PAGE_NOCACHE) ? L"N":L"-");
        StringCchCat(szBuf, nSize, (dwProtect&PAGE_WRITECOMBINE) ? L"W":L"-");
    }
}

void CVMQuery::GetMemStorageText(DWORD dwStorage, PTSTR szBuf, int nSize)
{
    PCTSTR p = L"Unknown";
    switch (dwStorage)
    {
    case MEM_FREE:    p = L"Free   "; break; // 閒置:未被預訂的區域
    case MEM_RESERVE: p = L"Reserve"; break; // 預訂:已預訂區域
    case MEM_IMAGE:   p = L"Image  "; break; // 映像:保存執行映像
    case MEM_MAPPED:  p = L"Mapped "; break; // 映射:文件映射區域
    case MEM_PRIVATE: p = L"Private"; break; // 私有:數據保存在頁交換文件中的區域
    }
    StringCchCopy(szBuf, nSize, p);
}

// 將區域內的塊信息保存到VMQUERY_HELP中
BOOL CVMQuery::GetVMBlockInfo(HANDLE hProcess, LPCVOID lpAddress, VMQUERYEX *pVMBlock)
{
    ZeroMemory(pVMBlock, sizeof(*pVMBlock));
    // 1. 獲取地址空間中內存地址信息,並將其保存到 MEMORY_BASIC_INFORMATION 結構體中
    MEMORY_BASIC_INFORMATION mbiRgn;
    if ( !( VirtualQueryEx(hProcess,lpAddress,&mbiRgn,sizeof(mbiRgn)) == sizeof(mbiRgn) ) )
        return false; // 錯誤的內存地址

    // 2. 保存基地址與第一個內存塊的基址
    PVOID pvRgnBaseAddress = mbiRgn.AllocationBase; // 預劃分內存區域基址
    PVOID pvAddressBlk     = mbiRgn.AllocationBase; // 第一個內存塊的基址

    // 3. 循環遍歷此區域內的內存塊,並統計其個數及總大小
    MEMORY_BASIC_INFORMATION mbiBlk;
    while (true)
    {
        // 3.1 獲取內存塊的相關信息
        if ( !( VirtualQueryEx(hProcess,pvAddressBlk,&mbiBlk,sizeof(mbiBlk)) == sizeof(mbiBlk) ) )
            break;
        // 3.2 判斷是否需要結束循環,如果此數據塊的基址仍等於此內存區域的基址,則代
        //     表此數據塊是屬於此區域內的(繼續循環),否則則代表此內存塊是其他內存
        //     區域的(結束循環)
        // 注1:使用MEM_RESERVE方式調用VirtualAlloc即可預定內存區域
        // 注2:使用MEM_COMMIT方式調用VirtualAlloc即可分配內存塊
        if (mbiBlk.AllocationBase != pvRgnBaseAddress)
            break;
        // 3.3 累加此預劃分內存區域內的內存塊數量 、預劃分域的總大小與具有PAGE_GUARD
        //     保護屬性的塊的數量
        pVMBlock->dwRgnBlocks++;                 // 內存塊數量加1
        pVMBlock->nRgnSize += mbiBlk.RegionSize; // 將此內存塊的體積納入到此內存區域中
        if ( (mbiBlk.Protect&PAGE_GUARD)==PAGE_GUARD ) 
            pVMBlock->dwRgnGuardBlks++;      // 統具有PAGE_GUARD保護屬性的塊的數量
        // 3.4 保存此內存塊的理存儲器類型
        // 注1:由於內存塊可以從MEM_IMAGE轉換到MEM_PRIVATE,或從MEM_MAPPED轉換到
        //      MEM_PRIVATE,也就是說如果預劃分內存區域如果爲MEM_PRIVATE的話,那麼此
        //      內存區域的數據塊就可以是任意的內存類型,我們就可以認爲在這裏取到的內
        //      存塊的類型信息是有效的,當然,這一切都只是一種合理的猜測,實際上內存
        //      塊在實際使用時完全有可能並非是我們獲取到的類型
        if ( MEM_PRIVATE == mbiRgn.Type )
            pVMBlock->dwRgnStorage = mbiBlk.Type;
        // 3.5 獲取下一個內存塊的基址
        pvAddressBlk = (PVOID)((PBYTE)pvAddressBlk+mbiBlk.RegionSize);
    }

    // 設置棧標誌位
    // 注1:具有PAGE_GUARD保護屬性的塊的數量大於0,則此區域是爲線程棧而預定的
    pVMBlock->bRgnIsAStack = (pVMBlock->dwRgnGuardBlks > 0);

    return(TRUE);
}

BOOL CVMQuery::GetVMInfo(HANDLE hProcess, LPCVOID lpAddress, PVMQUERY pVMQ)
{
    // 1. 清空結構體
    ZeroMemory(pVMQ, sizeof(*pVMQ));

    // 2. 獲取地址空間中內存地址信息,並將其保存到 MEMORY_BASIC_INFORMATION 結構體中
    MEMORY_BASIC_INFORMATION mbi;
    if ( !( VirtualQueryEx(hProcess,lpAddress,&mbi,sizeof(mbi)) == sizeof(mbi) ) )
        return false;

    // 3. 填寫區域成員信息
    VMQUERYEX VMQHelp;
    switch (mbi.State) {
    case MEM_FREE:     /** 空閒塊(不保留) **********************************/
        pVMQ->lpRgnBaseAddress = mbi.BaseAddress;
        // 由於空閒塊的mbi.Protect是無效的,因此這裏將使用此內存塊分配時的保護屬性
        pVMQ->dwRgnProtection  = mbi.AllocationProtect;
        pVMQ->nRgnSize         = mbi.RegionSize;
        pVMQ->dwRgnStorage     = MEM_FREE;
        pVMQ->dwRgnBlocks      = 0;
        pVMQ->dwRgnGuardBlks   = 0;
        pVMQ->bRgnIsAStack     = FALSE;
        break;
    case MEM_RESERVE:  /** 在虛擬內存中保留虛擬地址,但並不分配實際物理內存 **/
        pVMQ->lpRgnBaseAddress = mbi.AllocationBase;
        // 由於未提交塊的mbi.Protect是無效的,因此這裏將使用此內存塊分配時的保護屬性
        pVMQ->dwRgnProtection  = mbi.AllocationProtect;
        // 迭代獲取所有塊的詳細信息        
        GetVMBlockInfo(hProcess, lpAddress, &VMQHelp);
        pVMQ->nRgnSize         = VMQHelp.nRgnSize;
        pVMQ->dwRgnStorage     = VMQHelp.dwRgnStorage;
        pVMQ->dwRgnBlocks      = VMQHelp.dwRgnBlocks;
        pVMQ->dwRgnGuardBlks   = VMQHelp.dwRgnGuardBlks;
        pVMQ->bRgnIsAStack     = VMQHelp.bRgnIsAStack;
        break;
    case MEM_COMMIT:   /** 保留且分配物理地址 ********************************/
        pVMQ->lpRgnBaseAddress = mbi.AllocationBase;
        pVMQ->dwRgnProtection  = mbi.AllocationProtect;
        // 迭代獲取所有塊的詳細信息          
        GetVMBlockInfo(hProcess, lpAddress, &VMQHelp);
        pVMQ->nRgnSize         = VMQHelp.nRgnSize;
        pVMQ->dwRgnStorage     = VMQHelp.dwRgnStorage;
        pVMQ->dwRgnBlocks      = VMQHelp.dwRgnBlocks;
        pVMQ->dwRgnGuardBlks   = VMQHelp.dwRgnGuardBlks;
        pVMQ->bRgnIsAStack     = VMQHelp.bRgnIsAStack;
        break;
    default:
        DebugBreak();
        break;
    }

    // 4. 解析爲文本信息
    GetProtectText(pVMQ->dwRgnProtection, pVMQ->szRgnProtection, _countof(pVMQ->szRgnProtection), true);
    GetMemStorageText(pVMQ->dwRgnStorage, pVMQ->szRgnStorage, _countof(pVMQ->szRgnStorage));

    StringCchPrintf(pVMQ->szRgnBaseAddress,_countof(pVMQ->szRgnBaseAddress), L"0x%08x", pVMQ->lpRgnBaseAddress );
    StringCchPrintf(pVMQ->szRgnSize,       _countof(pVMQ->szRgnSize),        L"%10d",   pVMQ->nRgnSize );
    StringCchPrintf(pVMQ->szRgnBlocks,     _countof(pVMQ->szRgnBlocks),      L"%3d",    pVMQ->dwRgnBlocks );

    return true;
}


VMView.cpp:

// VMView.cpp : 定義控制檯應用程序的入口點。
//

#include "stdafx.h"
#include "VMQuery.h"

bool ShowVM(DWORD dwProcessId)
{
    CVMQuery objVM;
    HANDLE   hProcess  = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessId);
    PVOID    lpAddress = NULL;
    VMQUERY  stcVMQ    = {0};
    while ( objVM.GetVMInfo(hProcess, lpAddress, &stcVMQ) )
    {
        wprintf(L"%s %s %s %s %s\n",
            stcVMQ.szRgnBaseAddress,
            stcVMQ.szRgnStorage,
            stcVMQ.szRgnSize,
            stcVMQ.szRgnBlocks,
            stcVMQ.szRgnProtection);

        lpAddress = ((PBYTE)stcVMQ.lpRgnBaseAddress + stcVMQ.nRgnSize);
        ZeroMemory(&stcVMQ,sizeof(VMQUERY));
    }

    CloseHandle(hProcess);
    return true;
}

int _tmain(int argc, _TCHAR* argv[])
{
    ShowVM( GetCurrentProcessId() );
    system("pause");
    return 0;
}


這段代碼沒獲取塊內信息  只獲取了塊的大小和數量(需要自己拓展獲取塊信息)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章