Determining the State of an Address Space

[Previous] [Next]

Windows offers a function that lets you query certain information (for example, size, storage type, and protection attributes) about a memory address in your address space. In fact, the VMMap sample application shown later in this chapter uses this function to produce the virtual memory map dumps that appeared in Chapter 13. This function is called VirtualQuery:

 DWORD VirtualQuery( LPCVOID pvAddress, PMEMORY_BASIC_INFORMATION pmbi, DWORD dwLength); 

Windows also offers a function that allows one process to query memory information about another process:

 DWORD VirtualQueryEx( HANDLE hProcess, LPCVOID pvAddress, PMEMORY_BASIC_INFORMATION pmbi, DWORD dwLength); 

The two functions are identical except that VirtualQueryEx allows you to pass the handle of a process whose address space you want to query. Debuggers and other utilities most often use this function—nearly all applications will need only to call VirtualQuery. When you call VirtualQuery(Ex), the pvAddress parameter must contain the virtual memory address that you want information about. The pmbi parameter is the address to a MEMORY_BASIC_INFORMATION structure that you must allocate. This structure is defined in WinNT.h as follows:

 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; 

The last parameter, dwLength, specifies the size of the MEMORY_BASIC_INFORMATION structure. VirtualQuery(Ex) returns the number of bytes copied into the buffer.

Based on the address that you pass in the pvAddress parameter, VirtualQuery(Ex) fills the MEMORY_BASIC_INFORMATION structure with information about the range of adjoining pages that share the same state, protection attributes, and type. The following table offers a description of the structure's members.

Member Name Description
BaseAddress The same value as the pvAddress parameter rounded down to a page boundary.
AllocationBase Identifies the base address of the region containing the address specified in the pvAddress parameter.
AllocationProtect Identifies the protection attribute assigned to the region when it was initially reserved.
RegionSize Identifies the size, in bytes, for all pages starting at BaseAddress that have the same protection attributes, state, and type as the page containing the address specified in the pvAddress parameter.
State Identifies the state (MEM_FREE, MEM_RESERVE, or MEM_COMMIT) for all adjoining pages that have the same protection attributes, state, and type as the page containing the address specified in the pvAddress parameter.

If the state is free, the AllocationBase, AllocationProtect, Protect, and Type members are undefined.

If the state is MEM_RESERVE, the Protect member is undefined.

Protect Identifies the protection attribute (PAGE_*) for all adjoining pages that have the same protection attributes, state, and type as the page containing the address specified in the pvAddress parameter.
Type Identifies the type of physical storage (MEM_IMAGE, MEM_MAPPED, or MEM_PRIVATE) that is backing all adjoining pages that have the same protection attributes, state, and type as the page containing the address specified in the pvAddress parameter. For Windows 98, this member will always indicate MEM_PRIVATE.

The VMQuery Function

When I was first learning about Windows memory architecture, I used VirtualQuery as my guide. In fact, if you examine the first edition of this book, you'll see that the VMMap sample application was much simpler than the new version I present in the next section. In the old version, I had a simple loop that repeatedly called VirtualQuery, and for each call I simply constructed a single line containing the members of the MEMORY_BASIC_INFORMATION structure. I studied this dump and tried to piece the memory management architecture together while referring to the SDK documentation (which was rather poor at the time). Well, I've learned a lot since then. While VirtualQuery and the MEMORY_BASIC_INFORMATION structure give you a lot of insight into what's going on, I now know that they don't give you enough information to really understand it all.

The problem is that the MEMORY_BASIC_INFORMATION structure does not return all of the information that the system has stored internally. If you have a memory address and want to obtain some simple information about it, VirtualQuery is great. If you just want to know whether there is committed physical storage to an address or whether a memory address can be read from or written to, VirtualQuery works fine. But if you want to know the total size of a reserved region or the number of blocks in a region or whether a region contains a thread's stack, a single call to VirtualQuery is just not going to give you the information you're looking for.

To obtain much more complete memory information, I have created my own function, VMQuery:

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

This function is similar to VirtualQueryEx in that it takes a process handle (in hProcess), a memory address (in pvAddress), and a pointer to a structure that is to be filled (specified by pVMQ). This structure is a VMQUERY structure that I have also defined:

 typedef struct { // Region information PVOID pvRgnBaseAddress; DWORD dwRgnProtection; // PAGE_* SIZE_T RgnSize; DWORD dwRgnStorage; // MEM_*: Free, Image, Mapped, Private DWORD dwRgnBlocks; DWORD dwRgnGuardBlks; // If > 0, region contains thread stack BOOL fRgnIsAStack; // TRUE if region contains thread stack // Block information PVOID pvBlkBaseAddress; DWORD dwBlkProtection; // PAGE_* SIZE_T BlkSize; DWORD dwBlkStorage; //  MEM_*: Free, Reserve, Image, Mapped, Private } VMQUERY, *PVMQUERY; 

As you can see from just a quick glance, my VMQUERY structure contains much more information than the Windows MEMORY_BASIC_ INFORMATION structure. My structure is divided into two distinct parts: region information and block information. The region portion describes information about the region, and the block portion includes information about the block containing the address specified by the pvAddress parameter. The following table describes all the members.

Member Name Description
pvRgnBaseAddress Identifies the base address of the virtual address space region containing the address specified in the pvAddress parameter.
dwRgnProtection Identifies the protection attribute (PAGE_*) that was assigned to the region of address space when it was initially reserved.
RgnSize Identifies the size, in bytes, of the region that was reserved.
dwRgnStorage Identifies the type of physical storage that is used for the bulk of the blocks in the region. The value is one of the following: MEM_FREE, MEM_IMAGE, MEM_MAPPED, or MEM_ PRIVATE. Windows 98 doesn't distinguish between different storage types, so this member will always be MEM_FREE or MEM_PRIVATE under Windows 98.
dwRgnBlocks Identifies the number of blocks contained within the region.
dwRgnGuardBlks Identifies the number of blocks that have the PAGE_GUARD protection attribute flag turned on. This value will usually be either 0 or 1. If it's 1, that's a good indicator that the region was reserved to contain a thread's stack. Under Windows 98, this member will always be 0.
fRgnIsAStack Identifies whether the region contains a thread's stack. This value is determined by taking a "best guess" because it is impossible to be 100 percent sure whether a region contains a stack.
pvBlkBaseAddress Identifies the base address of the block that contains the address specified in the pvAddress parameter.
dwBlkProtection Identifies the protection attribute for the block that contains the address specified in the pvAddress parameter.
BlkSize Identifies the size, in bytes, of the block that contains the address specified in the pvAddress parameter.
dwBlkStorage Identifies the content of the block that contains the address specified in the pvAddress parameter. The value is one of the following: MEM_FREE, MEM_RESERVE, MEM_IMAGE, MEM_MAPPED, or MEM_PRIVATE. Under Windows 98, this member will never be MEM_IMAGE or MEM_MAPPED.

No doubt VMQuery must do a significant amount of processing, including many calls to VirtualQueryEx, in order to obtain all this information—which means it executes much more slowly than VirtualQueryEx. For this reason, you should think carefully when deciding which of these two functions to call. If you do not need the extra information obtained by VMQuery, call VirtualQuery or VirtualQueryEx.

The VMQuery.cpp file, listed in Figure 14-3, shows how I obtain and massage all the information needed to set the members of the VMQUERY structure. The VMQuery.cpp and VMQuery.h files are in the 14-VMMap directory on the companion CD-ROM. Rather than go into detail in the text about how I process this data, I'll let my comments (sprinkled liberally throughout the code) speak for themselves.

Figure 14-3. The VMQuery listings

VMQuery.cpp

 /****************************************************************************** Module: VMQuery.cpp Notices: Copyright (c) 2000 Jeffrey Richter ******************************************************************************/ #include "..\CmnHdr.h" /* See Appendix A. */ #include <windowsx.h> #include "VMQuery.h" /////////////////////////////////////////////////////////////////////////////// // Helper structure typedef struct { SIZE_T RgnSize; DWORD dwRgnStorage; // MEM_*: Free, Image, Mapped, Private DWORD dwRgnBlocks; DWORD dwRgnGuardBlks; // If > 0, region contains thread stack BOOL fRgnIsAStack; // TRUE if region contains thread stack } VMQUERY_HELP; // This global, static variable holds the allocation granularity value for // this CPU platform. Initialized the first time VMQuery is called. static DWORD gs_dwAllocGran = 0; /////////////////////////////////////////////////////////////////////////////// // Iterates through a region's blocks and returns findings in VMQUERY_HELP static BOOL VMQueryHelp(HANDLE hProcess, LPCVOID pvAddress, VMQUERY_HELP *pVMQHelp) { // Each element contains a page protection // (i.e.: 0=reserved, PAGE_NOACCESS, PAGE_READWRITE, etc.) DWORD dwProtectBlock[4] = { 0 }; ZeroMemory(pVMQHelp, sizeof(*pVMQHelp)); // Get address of region containing passed memory address. MEMORY_BASIC_INFORMATION mbi; BOOL fOk = (VirtualQueryEx(hProcess, pvAddress, &mbi, sizeof(mbi)) == sizeof(mbi)); if (!fOk) return(fOk); // Bad memory address, return failure // Walk starting at the region's base address (which never changes) PVOID pvRgnBaseAddress = mbi.AllocationBase; // Walk starting at the first block in the region (changes in the loop) PVOID pvAddressBlk = pvRgnBaseAddress; // Save the memory type of the physical storage block. pVMQHelp->dwRgnStorage = mbi.Type; for (;;) { // Get info about the current block. fOk = (VirtualQueryEx(hProcess, pvAddressBlk, &mbi, sizeof(mbi)) == sizeof(mbi)); if (!fOk) break; // Couldn't get the information, end loop. // Is this block in the same region? if (mbi.AllocationBase != pvRgnBaseAddress) break; // Found a block in the next region; end loop. // We have a block contained in the region. // The following if statement is for detecting stacks in Windows 98. // A Windows 98 stack region's last 4 blocks look like this: // reserved block, no access block, read-write block, reserved block if (pVMQHelp->dwRgnBlocks < 4) { // 0th through 3rd block, remember the block's protection dwProtectBlock[pVMQHelp->dwRgnBlocks] = (mbi.State == MEM_RESERVE) ? 0 : mbi.Protect; } else { // We've seen 4 blocks in this region. // Shift the protection values down in the array. MoveMemory(&dwProtectBlock[0], &dwProtectBlock[1], sizeof(dwProtectBlock) - sizeof(DWORD)); // Add the new protection value to the end of the array. dwProtectBlock[3] = (mbi.State == MEM_RESERVE) ? 0 : mbi.Protect; } pVMQHelp->dwRgnBlocks++; // Add another block to the region pVMQHelp->RgnSize += mbi.RegionSize; // Add block's size to region size // If block has PAGE_GUARD attribute, add 1 to this counter if ((mbi.Protect & PAGE_GUARD) == PAGE_GUARD) pVMQHelp->dwRgnGuardBlks++; // Take a best guess as to the type of physical storage committed to the // block. This is a guess because some blocks can convert from MEM_IMAGE // to MEM_PRIVATE or from MEM_MAPPED to MEM_PRIVATE; MEM_PRIVATE can // always be overridden by MEM_IMAGE or MEM_MAPPED. if (pVMQHelp->dwRgnStorage == MEM_PRIVATE) pVMQHelp->dwRgnStorage = mbi.Type; // Get the address of the next block. pvAddressBlk = (PVOID) ((PBYTE) pvAddressBlk + mbi.RegionSize); } // After examining the region, check to see whether it is a thread stack // Windows 2000: Assume stack if region has at least 1 PAGE_GUARD block // Windows 9x: Assume stack if region has at least 4 blocks with // 3rd block from end: reserved // 2nd block from end: PAGE_NOACCESS // 1st block from end: PAGE_READWRITE // block at end: another reserved block. pVMQHelp->fRgnIsAStack = (pVMQHelp->dwRgnGuardBlks > 0) || ((pVMQHelp->dwRgnBlocks >= 4) && (dwProtectBlock[0] == 0) && (dwProtectBlock[1] == PAGE_NOACCESS) && (dwProtectBlock[2] == PAGE_READWRITE) && (dwProtectBlock[3] == 0)); return(TRUE); } /////////////////////////////////////////////////////////////////////////////// BOOL VMQuery(HANDLE hProcess, LPCVOID pvAddress, PVMQUERY pVMQ) { if (gs_dwAllocGran == 0) { // Set allocation granularity if this is the first call SYSTEM_INFO sinf; GetSystemInfo(&sinf); gs_dwAllocGran = sinf.dwAllocationGranularity; } ZeroMemory(pVMQ, sizeof(*pVMQ)); // Get the MEMORY_BASIC_INFORMATION for the passed address. MEMORY_BASIC_INFORMATION mbi; BOOL fOk = (VirtualQueryEx(hProcess, pvAddress, &mbi, sizeof(mbi)) == sizeof(mbi)); if (!fOk) return(fOk); // Bad memory address, return failure // The MEMORY_BASIC_INFORMATION structure contains valid information. // Time to start setting the members of our own VMQUERY structure. // First, fill in the block members. We'll fill the region members later. switch (mbi.State) { case MEM_FREE: // Free block (not reserved) pVMQ->pvBlkBaseAddress = NULL; pVMQ->BlkSize = 0; pVMQ->dwBlkProtection = 0; pVMQ->dwBlkStorage = MEM_FREE; break; case MEM_RESERVE: // Reserved block without committed storage in it. pVMQ->pvBlkBaseAddress = mbi.BaseAddress; pVMQ->BlkSize = mbi.RegionSize; // For an uncommitted block, mbi.Protect is invalid. So we will // show that the reserved block inherits the protection attribute // of the region in which it is contained. pVMQ->dwBlkProtection = mbi.AllocationProtect; pVMQ->dwBlkStorage = MEM_RESERVE; break; case MEM_COMMIT: // Reserved block with committed storage in it. pVMQ->pvBlkBaseAddress = mbi.BaseAddress; pVMQ->BlkSize = mbi.RegionSize; pVMQ->dwBlkProtection = mbi.Protect; pVMQ->dwBlkStorage = mbi.Type; break; default: DebugBreak(); break; } // Now fill in the region data members. VMQUERY_HELP VMQHelp; switch (mbi.State) { case MEM_FREE: // Free block (not reserved) pVMQ->pvRgnBaseAddress = mbi.BaseAddress; pVMQ->dwRgnProtection = mbi.AllocationProtect; pVMQ->RgnSize = mbi.RegionSize; pVMQ->dwRgnStorage = MEM_FREE; pVMQ->dwRgnBlocks = 0; pVMQ->dwRgnGuardBlks = 0; pVMQ->fRgnIsAStack = FALSE; break; case MEM_RESERVE: // Reserved block without committed storage in it. pVMQ->pvRgnBaseAddress = mbi.AllocationBase; pVMQ->dwRgnProtection = mbi.AllocationProtect; // Iterate through all blocks to get complete region information. VMQueryHelp(hProcess, pvAddress, &VMQHelp); pVMQ->RgnSize = VMQHelp.RgnSize; pVMQ->dwRgnStorage = VMQHelp.dwRgnStorage; pVMQ->dwRgnBlocks = VMQHelp.dwRgnBlocks; pVMQ->dwRgnGuardBlks = VMQHelp.dwRgnGuardBlks; pVMQ->fRgnIsAStack = VMQHelp.fRgnIsAStack; break; case MEM_COMMIT: // Reserved block with committed storage in it. pVMQ->pvRgnBaseAddress = mbi.AllocationBase; pVMQ->dwRgnProtection = mbi.AllocationProtect; // Iterate through all blocks to get complete region information. VMQueryHelp(hProcess, pvAddress, &VMQHelp); pVMQ->RgnSize = VMQHelp.RgnSize; pVMQ->dwRgnStorage = VMQHelp.dwRgnStorage; pVMQ->dwRgnBlocks = VMQHelp.dwRgnBlocks; pVMQ->dwRgnGuardBlks = VMQHelp.dwRgnGuardBlks; pVMQ->fRgnIsAStack = VMQHelp.fRgnIsAStack; break; default: DebugBreak(); break; } return(fOk); } //////////////////////////////// End of File ////////////////////////////////// 

VMQuery.h

 /****************************************************************************** Module: VMQuery.h Notices: Copyright (c) 2000 Jeffrey Richter ******************************************************************************/ typedef struct { // Region information PVOID pvRgnBaseAddress; DWORD dwRgnProtection; // PAGE_* SIZE_T RgnSize; DWORD dwRgnStorage; // MEM_*: Free, Image, Mapped, Private DWORD dwRgnBlocks; DWORD dwRgnGuardBlks; // If > 0, region contains thread stack BOOL fRgnIsAStack; // TRUE if region contains thread stack // Block information 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); //////////////////////////////// End of File ////////////////////////////////// 

The Virtual Memory Map Sample Application

The VMMap application (14 VMMap.exe), listed in Figure 14-4, walks a process's address space and shows the regions and the blocks within regions. The source code and resource files for the application are in the 14-VMMap directory on the companion CD-ROM. When you start the program, the following window appears.

click to view at full size.

I used the contents of this application's list box to produce the virtual memory map dumps presented in Table 13-2, Table 13-3, and Table 13-4 in Chapter 13.

Each entry in the list box shows the result of information obtained by calling my VMQuery function. The main loop, in the Refresh function, looks like this:

 BOOL fOk = TRUE; PVOID pvAddress = NULL;  while (fOk) { VMQUERY vmq; fOk = VMQuery(hProcess, pvAddress, &vmq); if (fOk) { // Construct the line to be displayed, and add it to the list box. TCHAR szLine[1024]; ConstructRgnInfoLine(hProcess, &vmq, szLine, sizeof(szLine)); ListBox_AddString(hwndLB, szLine); if (fExpandRegions) { for (DWORD dwBlock = 0; fOk && (dwBlock < vmq.dwRgnBlocks); dwBlock++) { ConstructBlkInfoLine(&vmq, szLine, sizeof(szLine)); ListBox_AddString(hwndLB, szLine); // Get the address of the next region to test. pvAddress = ((PBYTE) pvAddress + vmq.BlkSize); if (dwBlock < vmq.dwRgnBlocks - 1) { // Don't query the memory info after the last block. fOk = VMQuery(hProcess, pvAddress, &vmq); } } } // Get the address of the next region to test. pvAddress = ((PBYTE) vmq.pvRgnBaseAddress + vmq.RgnSize); } } 

This loop starts walking from virtual address NULL and ends when VMQuery returns FALSE, indicating that it can no longer walk the process's address space. With each iteration of the loop, there is a call to ConstructRgnInfoLine; this function fills a character buffer with information about the region. Then this information is appended to the list box.

Within this main loop is a nested loop that iterates through each block in the region. Each iteration of this loop calls ConstructBlkInfoLine to fill a character buffer with information about the region's blocks. Then the information is appended to the list box. It's easy to walk the process's address space using the VMQuery function.

Figure 14-4. The VMMap application

VMMap.cpp

 /****************************************************************************** Module: VMMap.cpp Notices: Copyright (c) 2000 Jeffrey Richter ******************************************************************************/ #include "..\CmnHdr.h" /* See Appendix A. */ #include <psapi.h> #include <windowsx.h> #include <tchar.h> #include <stdio.h> // For sprintf #include "..\04-ProcessInfo\Toolhelp.h" #include "Resource.h" #include "VMQuery.h" /////////////////////////////////////////////////////////////////////////////// DWORD g_dwProcessId = 0; // Which process to walk? BOOL g_fExpandRegions = FALSE; CToolhelp g_toolhelp; // GetMappedFileName is only on Windows 2000 in PSAPI.DLL // If this function exists on the host system, we'll use it typedef DWORD (WINAPI* PFNGETMAPPEDFILENAME)(HANDLE, PVOID, PTSTR, DWORD); static PFNGETMAPPEDFILENAME g_pfnGetMappedFileName = NULL; /////////////////////////////////////////////////////////////////////////////// // I use this function to obtain the dump figures in the book. 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(szClipData, szLine); _tcscat(szClipData, TEXT("\r\n")); } OpenClipboard(NULL); EmptyClipboard(); // Clipboard accepts only data that is in a block allocated // with GlobalAlloc using the GMEM_MOVEABLE and GMEM_DDESHARE flags. HGLOBAL hClipData = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, sizeof(TCHAR) * (_tcslen(szClipData) + 1)); PTSTR pClipData = (PTSTR) GlobalLock(hClipData); _tcscpy(pClipData, szClipData); #ifdef UNICODE BOOL fOk = (SetClipboardData(CF_UNICODETEXT, hClipData) == hClipData); #else BOOL fOk = (SetClipboardData(CF_TEXT, hClipData) == hClipData); #endif CloseClipboard(); if (!fOk) { GlobalFree(hClipData); chMB("Error putting text on the clipboard"); } } /////////////////////////////////////////////////////////////////////////////// PCTSTR GetMemStorageText(DWORD dwStorage) { PCTSTR p = TEXT("Unknown"); switch (dwStorage) { case MEM_FREE: p = TEXT("Free "); break; case MEM_RESERVE: p = TEXT("Reserve"); break; case MEM_IMAGE: p = TEXT("Image "); break; case MEM_MAPPED: p = TEXT("Mapped "); break; case MEM_PRIVATE: p = TEXT("Private"); break; } return(p); } /////////////////////////////////////////////////////////////////////////////// PTSTR GetProtectText(DWORD dwProtect, PTSTR szBuf, BOOL fShowFlags) { PCTSTR p = TEXT("Unknown"); 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(szBuf, p); if (fShowFlags) { _tcscat(szBuf, TEXT(" ")); _tcscat(szBuf, (dwProtect & PAGE_GUARD) ? TEXT("G") : TEXT("-")); _tcscat(szBuf, (dwProtect & PAGE_NOCACHE) ? TEXT("N") : TEXT("-")); _tcscat(szBuf, (dwProtect & PAGE_WRITECOMBINE) ? TEXT("W") : TEXT("-")); } return(szBuf); } /////////////////////////////////////////////////////////////////////////////// void ConstructRgnInfoLine(HANDLE hProcess, PVMQUERY pVMQ, PTSTR szLine, int nMaxLen) { _stprintf(szLine, TEXT("%p %s %16u "), pVMQ->pvRgnBaseAddress, GetMemStorageText(pVMQ->dwRgnStorage), pVMQ->RgnSize); if (pVMQ->dwRgnStorage != MEM_FREE) { wsprintf(_tcschr(szLine, 0), TEXT("%5u "), pVMQ->dwRgnBlocks); GetProtectText(pVMQ->dwRgnProtection, _tcschr(szLine, 0), FALSE); } _tcscat(szLine, TEXT(" ")); // Try to obtain the module pathname for this region. int nLen = _tcslen(szLine); if (pVMQ->pvRgnBaseAddress != NULL) { MODULEENTRY32 me = { sizeof(me) }; if (g_toolhelp.ModuleFind(pVMQ->pvRgnBaseAddress, &me)) { lstrcat(&szLine[nLen], me.szExePath); } else { // This is not a module; see if it's a memory-mapped file if (g_pfnGetMappedFileName != NULL) { DWORD d = g_pfnGetMappedFileName(hProcess, pVMQ->pvRgnBaseAddress, szLine + nLen, nMaxLen - nLen); if (d == 0) { // NOTE: GetMappedFileName modifies the string when it fails szLine[nLen] = 0; } } } } if (pVMQ->fRgnIsAStack) { _tcscat(szLine, TEXT("Thread Stack")); } } /////////////////////////////////////////////////////////////////////////////// void ConstructBlkInfoLine(PVMQUERY pVMQ, PTSTR szLine, int nMaxLen) { _stprintf(szLine, TEXT(" %p %s %16u "), pVMQ->pvBlkBaseAddress, GetMemStorageText(pVMQ->dwBlkStorage), pVMQ->BlkSize); if (pVMQ->dwBlkStorage != MEM_FREE) { GetProtectText(pVMQ->dwBlkProtection, _tcschr(szLine, 0), TRUE); } } /////////////////////////////////////////////////////////////////////////////// void Refresh(HWND hwndLB, DWORD dwProcessId, BOOL fExpandRegions) { // Delete contents of list box & add a horizontal scroll bar ListBox_ResetContent(hwndLB); ListBox_SetHorizontalExtent(hwndLB, 300 * LOWORD(GetDialogBaseUnits())); // Is the process still running? HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessId); if (hProcess == NULL) { ListBox_AddString(hwndLB, TEXT("")); // Blank line, looks better ListBox_AddString(hwndLB, TEXT(" The process ID identifies a process that is not running")); return; } // Grab a new snapshot of the process g_toolhelp.CreateSnapshot(TH32CS_SNAPALL, dwProcessId); // Walk the virtual address space, adding entries to the list box. BOOL fOk = TRUE; PVOID pvAddress = NULL; SetWindowRedraw(hwndLB, FALSE); while (fOk) { VMQUERY vmq; fOk = VMQuery(hProcess, pvAddress, &vmq); if (fOk) { // Construct the line to be displayed, and add it to the list box. TCHAR szLine[1024]; ConstructRgnInfoLine(hProcess, &vmq, szLine, sizeof(szLine)); ListBox_AddString(hwndLB, szLine); if (fExpandRegions) { for (DWORD dwBlock = 0; fOk && (dwBlock < vmq.dwRgnBlocks); dwBlock++) { ConstructBlkInfoLine(&vmq, szLine, sizeof(szLine)); ListBox_AddString(hwndLB, szLine); // Get the address of the next region to test. pvAddress = ((PBYTE) pvAddress + vmq.BlkSize); if (dwBlock < vmq.dwRgnBlocks - 1) { // Don't query the memory info after the last block. fOk = VMQuery(hProcess, pvAddress, &vmq); } } } // Get the address of the next region to test. pvAddress = ((PBYTE) vmq.pvRgnBaseAddress + vmq.RgnSize); } } SetWindowRedraw(hwndLB, TRUE); CloseHandle(hProcess); /////////////////////////////////////////////////////////////////////////////// BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam) { chSETDLGICONS(hwnd, IDI_VMMAP); // Show which process we're walking in the window's caption TCHAR szCaption[MAX_PATH * 2]; GetWindowText(hwnd, szCaption, chDIMOF(szCaption)); g_toolhelp.CreateSnapshot(TH32CS_SNAPALL, g_dwProcessId); PROCESSENTRY32 pe = { sizeof(pe) }; wsprintf(&szCaption[lstrlen(szCaption)], TEXT(" (PID=%u \"%s\")"), g_dwProcessId, g_toolhelp.ProcessFind(g_dwProcessId, &pe) ? pe.szExeFile : TEXT("unknown")); SetWindowText(hwnd, szCaption); // VMMap has so much info to show, let's maximize it by default ShowWindow(hwnd, SW_MAXIMIZE); // Force the list box to refresh itself Refresh(GetDlgItem(hwnd, IDC_LISTBOX), g_dwProcessId, g_fExpandRegions); return(TRUE); } /////////////////////////////////////////////////////////////////////////////// void Dlg_OnSize(HWND hwnd, UINT state, int cx, int cy) { // The list box always fills the whole client area SetWindowPos(GetDlgItem(hwnd, IDC_LISTBOX), NULL, 0, 0, cx, cy, SWP_NOZORDER); } /////////////////////////////////////////////////////////////////////////////// void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { switch (id) { case IDCANCEL: EndDialog(hwnd, id); break; case ID_REFRESH: Refresh(GetDlgItem(hwnd, IDC_LISTBOX), g_dwProcessId, g_fExpandRegions); break; case ID_EXPANDREGIONS: g_fExpandRegions = g_fExpandRegions ? FALSE: TRUE; Refresh(GetDlgItem(hwnd, IDC_LISTBOX), g_dwProcessId, g_fExpandRegions); break; case ID_COPYTOCLIPBOARD: CopyControlToClipboard(GetDlgItem(hwnd, IDC_LISTBOX)); break; } } /////////////////////////////////////////////////////////////////////////////// INT_PTR WINAPI Dlg_Proc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { chHANDLE_DLGMSG(hwnd, WM_INITDIALOG, Dlg_OnInitDialog); chHANDLE_DLGMSG(hwnd, WM_COMMAND, Dlg_OnCommand); chHANDLE_DLGMSG(hwnd, WM_SIZE, Dlg_OnSize); } return(FALSE); } /////////////////////////////////////////////////////////////////////////////// int WINAPI _tWinMain(HINSTANCE hinstExe, HINSTANCE, PTSTR pszCmdLine, int) { CToolhelp::EnableDebugPrivilege(); // Try to load PSAPI.DLL and get the address of GetMappedFileName HMODULE hmodPSAPI = LoadLibrary(TEXT("PSAPI")); if (hmodPSAPI != NULL) { #ifdef UNICODE g_pfnGetMappedFileName = (PFNGETMAPPEDFILENAME) GetProcAddress(hmodPSAPI, "GetMappedFileNameW"); #else g_pfnGetMappedFileName = (PFNGETMAPPEDFILENAME) GetProcAddress(hmodPSAPI, "GetMappedFileNameA"); #endif } g_dwProcessId = _ttoi(pszCmdLine); if (g_dwProcessId == 0) { g_dwProcessId = GetCurrentProcessId(); } DialogBox(hinstExe, MAKEINTRESOURCE(IDD_VMMAP), NULL, Dlg_Proc); if (hmodPSAPI != NULL) FreeLibrary(hmodPSAPI); // Free PSAPI.DLL if we loaded it return(0); } //////////////////////////////// End of File ////////////////////////////////// 

VMMap.rc

 //Microsoft Developer Studio generated resource script. // #include "Resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // English (U.S.) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #endif //_WIN32 #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "Resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Dialog // IDD_VMMAP DIALOG DISCARDABLE 10, 18, 250, 250 STYLE WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME CAPTION "Virtual Memory Map" MENU IDR_VMMAP FONT 8, "Courier" BEGIN LISTBOX IDC_LISTBOX,0,0,248,248,LBS_NOINTEGRALHEIGHT | NOT WS_BORDER | WS_VSCROLL | WS_HSCROLL | WS_GROUP | WS_TABSTOP END ///////////////////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. IDI_VMMAP ICON DISCARDABLE "VMMap.Ico" ///////////////////////////////////////////////////////////////////////////// // // Menu // IDR_VMMAP MENU DISCARDABLE BEGIN MENUITEM "&Refresh!", ID_REFRESH MENUITEM "&Expand Regions!", ID_EXPANDREGIONS MENUITEM &Copy to Clipboard! , ID_COPYTOCLIPBOARD END #endif // English (U.S.) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED 



Programming Applications for Microsoft Windows
Programming Applications for Microsoft Windows (Microsoft Programming Series)
ISBN: 1572319968
EAN: 2147483647
Year: 1999
Pages: 193

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net