'VirtualQueryEx() returns mostly useless data
I've written a little program to query the page-map of a process:
#include <Windows.h>
#include <iostream>
#include <vector>
#include <charconv>
#include <cstring>
#include <stdexcept>
using namespace std;
int main( int argc, char **argv )
{
if( argc < 2 )
return EXIT_FAILURE;
try
{
DWORD dwProcessId = [&]() -> DWORD
{
DWORD dwRet;
from_chars_result fcr = from_chars( argv[1], argv[1] + strlen( argv[1] ), dwRet );
if( fcr.ec != errc() || *fcr.ptr )
throw invalid_argument( "process-id unparseable" );
return dwRet;
}();
HANDLE hProcess = [&]() -> HANDLE
{
HANDLE hRet = OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, dwProcessId );
if( !hRet )
throw system_error( (int)GetLastError(), system_category(), "can't open process" );
return hRet;
}();
size_t pageSize = []() -> size_t
{
SYSTEM_INFO si;
GetSystemInfo( &si );
return si.dwPageSize;
}();
using mbi_t = MEMORY_BASIC_INFORMATION;
vector<mbi_t> mbis( 0x100 );
size_t nRegions;
while( !(nRegions = VirtualQueryEx( hProcess, nullptr, to_address( mbis.begin() ), mbis.size() * sizeof(mbi_t) )) )
if( GetLastError() == ERROR_BAD_LENGTH )
mbis.resize( mbis.size() * 2 );
else
throw system_error( (int)GetLastError(), system_category(), "can't query process pages" );
mbis.resize( nRegions );
for( mbi_t const &mbi : mbis )
{
cout << "base address: " << hex << mbi.BaseAddress << endl;
cout << "allocation base: " << hex << mbi.AllocationBase << endl;
cout << dec << mbi.RegionSize / pageSize << " pages" << endl;
static struct
{
DWORD dwProtect;
char const *str;
} const protectMaps[] =
{
{ PAGE_EXECUTE, "PAGE_EXECUTE" },
{ PAGE_EXECUTE_READ, "PAGE_EXECUTE_READ" },
{ PAGE_EXECUTE_READWRITE, "PAGE_EXECUTE_READWRITE" },
{ PAGE_EXECUTE_WRITECOPY, "PAGE_EXECUTE_WRITECOPY" },
{ PAGE_NOACCESS, "PAGE_NOACCESS" },
{ PAGE_READONLY, "PAGE_READONLY" },
{ PAGE_READWRITE, "PAGE_READWRITE" },
{ PAGE_WRITECOPY, "PAGE_WRITECOPY" }
};
for( auto const &pm : protectMaps )
if( pm.dwProtect == mbi.AllocationProtect )
{
cout << "state: " << pm.str << endl;
break;
}
if( mbi.Type == MEM_IMAGE )
cout << "image";
else if( mbi.Type == MEM_MAPPED )
cout << "mapped";
else if( mbi.Type == MEM_PRIVATE )
cout << "private";
cout << endl << endl;
}
}
catch( exception const &exc )
{
cout << exc.what() << endl;
}
}
Unfortunately the program returns mostly null-data except from the number of pages with the first entry, which is the number of logical pages of the process minus 32.
What am I doing wrong here ?
The process I tried to query runs under the same token, so there coudln't be any privilege issues.
Solution 1:[1]
Thank you Hans ! You were right. I thoughth VirtualQueryEx() fills just a number of MEMORY_BASIC_INFORMATION. If you don't see something obvious you say in Germany that you've got tomatoes on your eyes, and yes, I had tomatoes on my eyes (not because of my style ;-)).
Here's the working code:
#include <Windows.h>
#include <iostream>
#include <vector>
#include <charconv>
#include <cstring>
#include <vector>
#include <stdexcept>
using namespace std;
vector<vector<MEMORY_BASIC_INFORMATION>> pageTree( HANDLE hProcess );
int main( int argc, char **argv )
{
if( argc < 2 )
return EXIT_FAILURE;
try
{
DWORD dwProcessId = [&]() -> DWORD
{
DWORD dwRet;
from_chars_result fcr = from_chars( argv[1], argv[1] + strlen( argv[1] ), dwRet );
if( fcr.ec != errc() || *fcr.ptr )
throw invalid_argument( "process-id unparseable" );
return dwRet;
}();
HANDLE hProcess = [&]() -> HANDLE
{
HANDLE hRet = OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, dwProcessId );
if( !hRet )
throw system_error( (int)GetLastError(), system_category(), "can't open process" );
return hRet;
}();
size_t pageSize = []() -> size_t
{
SYSTEM_INFO si;
GetSystemInfo( &si );
return si.dwPageSize;
}();
vector<vector<MEMORY_BASIC_INFORMATION>> vvmbi = pageTree( hProcess );
for( vector<MEMORY_BASIC_INFORMATION> const &vmbi : vvmbi )
{
cout << "allocation base: " << hex << vmbi.front().AllocationBase << endl;
for( MEMORY_BASIC_INFORMATION const &mbi : vmbi )
{
cout << "\tbase address: " << hex << mbi.BaseAddress << endl;
cout << "\t" << dec << mbi.RegionSize / pageSize << " pages" << endl;
static struct
{
DWORD dwProtect;
char const *str;
} const protectMaps[] =
{
{ PAGE_EXECUTE, "PAGE_EXECUTE" },
{ PAGE_EXECUTE_READ, "PAGE_EXECUTE_READ" },
{ PAGE_EXECUTE_READWRITE, "PAGE_EXECUTE_READWRITE" },
{ PAGE_EXECUTE_WRITECOPY, "PAGE_EXECUTE_WRITECOPY" },
{ PAGE_NOACCESS, "PAGE_NOACCESS" },
{ PAGE_READONLY, "PAGE_READONLY" },
{ PAGE_READWRITE, "PAGE_READWRITE" },
{ PAGE_WRITECOPY, "PAGE_WRITECOPY" }
};
for( auto const &pm : protectMaps )
if( pm.dwProtect == mbi.AllocationProtect )
{
cout << "\tstate: " << pm.str << endl;
break;
}
if( mbi.Type == MEM_IMAGE )
cout << "\timage" << endl;
else if( mbi.Type == MEM_MAPPED )
cout << "\tmapped" << endl;
else if( mbi.Type == MEM_PRIVATE )
cout << "\tprivate" << endl;
cout << endl;
}
}
}
catch( exception const &exc )
{
cout << exc.what() << endl;
}
}
template<typename Fn>
requires requires( Fn fn, MEMORY_BASIC_INFORMATION &mbi ) { { fn( mbi ) } -> std::convertible_to<bool>; }
void enumProcessMemory( HANDLE hProcess, Fn fn );
vector<vector<MEMORY_BASIC_INFORMATION>> pageTree( HANDLE hProcess )
{
vector<vector<MEMORY_BASIC_INFORMATION>> vvmbis;
enumProcessMemory( hProcess, [&]( MEMORY_BASIC_INFORMATION &mbi ) -> bool
{
if( !vvmbis.size() || vvmbis.back().back().BaseAddress != mbi.BaseAddress )
vvmbis.emplace_back( vector<MEMORY_BASIC_INFORMATION>() );
vvmbis.back().emplace_back( mbi );
return true;
} );
return vvmbis;
}
template<typename Fn>
requires requires( Fn fn, MEMORY_BASIC_INFORMATION &mbi ) { { fn( mbi ) } -> std::convertible_to<bool>; }
void enumProcessMemory( HANDLE hProcess, Fn fn )
{
MEMORY_BASIC_INFORMATION mbi;
for( char *last = nullptr; ; last = (char *)mbi.BaseAddress + mbi.RegionSize )
{
size_t nBytes = VirtualQueryEx( hProcess, last, &mbi, sizeof mbi );
if( nBytes != sizeof mbi )
if( DWORD dwErr = GetLastError(); dwErr == ERROR_INVALID_PARAMETER )
break;
else
throw system_error( (int)dwErr, system_category(), "can't query process pages" );
if( !fn( mbi ) )
break;
}
}
The code now groups the allocation bases. Its segemented that parts can be extended without changing the framework.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |
