João P 2018
João P 2018

Reputation: 7

VirtualQueryEx returning impossible mbi.RegionSize value in ctypes

I'm building a Memory Scanner and in the middle of it I use VirtualQueryEx to get the size in bytes of the process. This is my VirtualQueryEx and MEMORY_BASIC_INFORMATION structure setups:

    VirtualQueryEx = windll.kernel32.VirtualQueryEx
    VirtualQueryEx.restype = c_size_t

    class MEMORY_BASIC_INFORMATION(Structure):
         _fields_ = [
    ("BaseAddress", c_ulonglong),
    ("AllocationBase", c_ulonglong),
    ("AllocationProtect", c_ulong),
    ("__alignment1", c_ulong),
    ("RegionSize", c_ulonglong),
    ("State", c_ulong),
    ("Protect", c_ulong),
    ("Type", c_ulong),
    ("__alignment2", c_ulong),
        ]


    VirtualQueryEx.argtypes = [

     wintypes.HANDLE,                   # [in]  HANDLE                    hProcess
     wintypes.LPCVOID,                  # [in]  LPCVOID                   lpAdress
     POINTER(MEMORY_BASIC_INFORMATION), # [out] PMEMORY_BASIC_INFORMATION lpBuffer
     c_size_t,                          # [in]  SIZE_T                    lpBuffer

     ]

and before I call the function I also define some variables

    mbi      = MEMORY_BASIC_INFORMATION()
    lpAdress = wintypes.LPVOID(None)
    hProcess which is a OpenProcess() Handle

then I call it

    VirtualQueryEx( hProcess, lpAdress, byref(mbi), sizeof(mbi)) 
    

and after I check the output of RegionSide field of MEMORY_BASIC_INFORMATION

    mbi.RegionSize

the result is a crazy number of bytes like 135762539134976 which is so big that is probably a wrong value or at least something I dont understand, any ideas ?

Upvotes: 0

Views: 742

Answers (1)

Mark Tolonen
Mark Tolonen

Reputation: 177901

Here's a verified working version. The numbers printed for my Python process agree with the SysInternals vmmap utility:

import ctypes as ct
from ctypes import wintypes as w

SIZE_T = ct.c_size_t

# https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-memory_basic_information
class MEMORY_BASIC_INFORMATION(ct.Structure):

    _fields_ = [("BaseAddress", w.LPVOID),
                ("AllocationBase", w.LPVOID),
                ("AllocationProtect", w.DWORD),
                ("PartitionId", w.WORD),
                ("RegionSize", SIZE_T),
                ("State", w.DWORD),
                ("Protect", w.DWORD),
                ("Type", w.DWORD)]

    # Allow this structure to print itself
    def __repr__(self):
        return f'MEMORY_BASIC_INFORMATION(BaseAddress={self.BaseAddress if self.BaseAddress is not None else 0:#x}, ' \
                                        f'AllocationBase={self.AllocationBase if self.AllocationBase is not None else 0:#x}, ' \
                                        f'AllocationProtect={self.AllocationProtect:#x}, ' \
                                        f'PartitionId={self.PartitionId:#x}, ' \
                                        f'RegionSize={self.RegionSize:#x}, ' \
                                        f'State={self.State:#x}, ' \
                                        f'Protect={self.Protect:#x}, ' \
                                        f'Type={self.Type:#x})'

PMEMORY_BASIC_INFORMATION = ct.POINTER(MEMORY_BASIC_INFORMATION)

dll = ct.WinDLL('kernel32', use_last_error=True)
# https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualqueryex
VirtualQueryEx = dll.VirtualQueryEx
VirtualQueryEx.argtypes = w.HANDLE, w.LPCVOID, PMEMORY_BASIC_INFORMATION, SIZE_T
VirtualQueryEx.restype = SIZE_T
# https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentprocess
GetCurrentProcess = dll.GetCurrentProcess
GetCurrentProcess.argtypes = ()
GetCurrentProcess.restype = w.HANDLE

h = GetCurrentProcess()
mbi = MEMORY_BASIC_INFORMATION()

# Starting at the beginning of the process' virtual memory,
# query the region and its size, then advance by the region size
# and continue to query until failure.
address = 0
while True:
    result = VirtualQueryEx(h, address, ct.byref(mbi), ct.sizeof(mbi))
    if result:
        print(mbi)
        address += mbi.RegionSize
    else:
        print(f'err={ct.get_last_error()}')
        break

Output:

MEMORY_BASIC_INFORMATION(BaseAddress=0x0, AllocationBase=0x0, AllocationProtect=0x0, PartitionId=0x0, RegionSize=0x7ffe0000, State=0x10000, Protect=0x1, Type=0x0)
MEMORY_BASIC_INFORMATION(BaseAddress=0x7ffe0000, AllocationBase=0x7ffe0000, AllocationProtect=0x2, PartitionId=0x0, RegionSize=0x1000, State=0x1000, Protect=0x2, Type=0x20000)
MEMORY_BASIC_INFORMATION(BaseAddress=0x7ffe1000, AllocationBase=0x0, AllocationProtect=0x0, PartitionId=0x0, RegionSize=0x5000, State=0x10000, Protect=0x1, Type=0x0)
MEMORY_BASIC_INFORMATION(BaseAddress=0x7ffe6000, AllocationBase=0x7ffe6000, AllocationProtect=0x2, PartitionId=0x0, RegionSize=0x1000, State=0x1000, Protect=0x2, Type=0x20000)
MEMORY_BASIC_INFORMATION(BaseAddress=0x7ffe7000, AllocationBase=0x0, AllocationProtect=0x0, PartitionId=0x0, RegionSize=0x16bce19000, State=0x10000, Protect=0x1, Type=0x0)
 ...
MEMORY_BASIC_INFORMATION(BaseAddress=0x7ffaca6f6000, AllocationBase=0x7ffaca590000, AllocationProtect=0x80, PartitionId=0x0, RegionSize=0x1000, State=0x1000, Protect=0x4, Type=0x1000000)
MEMORY_BASIC_INFORMATION(BaseAddress=0x7ffaca6f7000, AllocationBase=0x7ffaca590000, AllocationProtect=0x80, PartitionId=0x0, RegionSize=0x2000, State=0x1000, Protect=0x8, Type=0x1000000)
MEMORY_BASIC_INFORMATION(BaseAddress=0x7ffaca6f9000, AllocationBase=0x7ffaca590000, AllocationProtect=0x80, PartitionId=0x0, RegionSize=0x9000, State=0x1000, Protect=0x4, Type=0x1000000)
MEMORY_BASIC_INFORMATION(BaseAddress=0x7ffaca702000, AllocationBase=0x7ffaca590000, AllocationProtect=0x80, PartitionId=0x0, RegionSize=0x86000, State=0x1000, Protect=0x2, Type=0x1000000)
MEMORY_BASIC_INFORMATION(BaseAddress=0x7ffaca788000, AllocationBase=0x0, AllocationProtect=0x0, PartitionId=0x0, RegionSize=0x535868000, State=0x10000, Protect=0x1, Type=0x0)
err=87

Upvotes: 1

Related Questions