VirtualQueryEx() fills a MEMORY_BASIC_INFORMATION record with information about a contiguous range of pages containing the queried address. This can be used to walk the address space of a process, by starting with 0 and then using mbi.BaseAddress + mbi.RegionSize as the next address to query and so on. GetMappedFileName() can give you the name of mapped binaries (mbi.Type == MEM_IMAGE) and mapped files (MEM_MAPPED).
Address overflow is an issue when a 32-bit process runs queries on a 64-bit process; it seems that VirtualQueryEx() fails to fail in that situation and instead saturates the field in question (by setting it to 0xFFFFFFFF). However, that is not documented and hence not reliable. Alternative tests are mbi.RegionSize > DWORD_PTR(-1) - mbi.BaseAddress
, or next_va <= curr_va
when iterating.
Results for 32-bit process querying 64-bit notepad.exe:
00000000 10000 -
00010000 00010000 10000 c m rw-- rw--
...
FF5F0000 FF5F0000 1000 c i rwxc r--- notepad.exe
FF5F0000 FF5F1000 B000 c i rwxc r-x-
FF5F0000 FF5FC000 4000 c i rwxc r---
FF5F0000 FF600000 2000 c i rwxc rw--
FF5F0000 FF602000 1000 c i rwxc rw-c
FF5F0000 FF603000 22000 c i rwxc r---
FF625000 FFFFFFFF -
overflow -> aborting
Dito for a 64-bit process:
0000000000000 10000 -
0000000010000 0000000010000 10000 c m rw-- rw--
...
00000FF5F0000 00000FF5F0000 1000 c i rwxc r--- notepad.exe
00000FF5F0000 00000FF5F1000 B000 c i rwxc r-x-
00000FF5F0000 00000FF5FC000 4000 c i rwxc r---
00000FF5F0000 00000FF600000 2000 c i rwxc rw--
00000FF5F0000 00000FF602000 1000 c i rwxc rw-c
00000FF5F0000 00000FF603000 22000 c i rwxc r---
00000FF625000 7FDFB2CB000 -
007FEFA8F0000 007FEFA8F0000 1000 c i rwxc r--- winspool.drv
...
007FFFFFE0000 007FFFFFE0000 10000 r r--- !---