Oct-08-2017, 10:39 PM
Hi all, I trying to write a Python Windows Memory Scanner, that finds the addresses of a specific value. The complete code is at the bottom, and I'll explain how it works.
First, System info is called, and the values of
sysinfo.lpMinimumApplicationAddress
sysinfo.lpMaximumApplicationAddress
are obtained, so I can feed it to my loop which has VirtualQueryEx()
Kernel32.OpenProcess is used to be fed to ReadMemoryProcess.
Then finally, A loop is used to get VirtualQueryEx() to return base address to scan and it's
mbi.RegionSize.
And then:
I have the following code, and somehow I must have fed the read process
Memory incorrectly. what the code does is to check a region of memory to see
whether or not it can be scanned.
mbi.Protect == PAGE_READWRITE and mbi.State == MEM_COMMIT
If this is true,then it proceeds to scan the memory fro current_address to
current_address + mbi.RegionSize.
However, a strange thing happens: The loop runs twice successfully, and then it
pops:
raise ctypes.WinError(ctypes.get_last_error())
OSError: [WinError 299] Only part of a ReadProcessMemory or WriteProcessMemory request was completed.
Now, I know the problem is not with VirtualQueryEx, because if I comment out
the red part and just run VirtualQueryEx, it would actually skim through all regions
without a single error.
Somehow, if I use this:
index = current_address
end = current_address + mbi.RegionSize - 7
Where the end is less by 7, the loop would not pop any error and it would finish
the loop
What did I do wrong?
thanks!
This is the full code.
First, System info is called, and the values of
sysinfo.lpMinimumApplicationAddress
sysinfo.lpMaximumApplicationAddress
are obtained, so I can feed it to my loop which has VirtualQueryEx()
Kernel32.OpenProcess is used to be fed to ReadMemoryProcess.
Then finally, A loop is used to get VirtualQueryEx() to return base address to scan and it's
mbi.RegionSize.
And then:
I have the following code, and somehow I must have fed the read process
Memory incorrectly. what the code does is to check a region of memory to see
whether or not it can be scanned.
mbi.Protect == PAGE_READWRITE and mbi.State == MEM_COMMIT
If this is true,then it proceeds to scan the memory fro current_address to
current_address + mbi.RegionSize.
However, a strange thing happens: The loop runs twice successfully, and then it
pops:
raise ctypes.WinError(ctypes.get_last_error())
OSError: [WinError 299] Only part of a ReadProcessMemory or WriteProcessMemory request was completed.
Now, I know the problem is not with VirtualQueryEx, because if I comment out
the red part and just run VirtualQueryEx, it would actually skim through all regions
without a single error.
Somehow, if I use this:
index = current_address
end = current_address + mbi.RegionSize - 7
Where the end is less by 7, the loop would not pop any error and it would finish
the loop
What did I do wrong?
thanks!
This is the full code.
import ctypes from ctypes.wintypes import WORD, DWORD, LPVOID PVOID = LPVOID SIZE_T = ctypes.c_size_t # https://msdn.microsoft.com/en-us/library/aa383751#DWORD_PTR if ctypes.sizeof(ctypes.c_void_p) == ctypes.sizeof(ctypes.c_ulonglong): DWORD_PTR = ctypes.c_ulonglong elif ctypes.sizeof(ctypes.c_void_p) == ctypes.sizeof(ctypes.c_ulong): DWORD_PTR = ctypes.c_ulong class SYSTEM_INFO(ctypes.Structure): """https://msdn.microsoft.com/en-us/library/ms724958""" class _U(ctypes.Union): class _S(ctypes.Structure): _fields_ = (('wProcessorArchitecture', WORD), ('wReserved', WORD)) _fields_ = (('dwOemId', DWORD), # obsolete ('_s', _S)) _anonymous_ = ('_s',) _fields_ = (('_u', _U), ('dwPageSize', DWORD), ('lpMinimumApplicationAddress', LPVOID), ('lpMaximumApplicationAddress', LPVOID), ('dwActiveProcessorMask', DWORD_PTR), ('dwNumberOfProcessors', DWORD), ('dwProcessorType', DWORD), ('dwAllocationGranularity', DWORD), ('wProcessorLevel', WORD), ('wProcessorRevision', WORD)) _anonymous_ = ('_u',) LPSYSTEM_INFO = ctypes.POINTER(SYSTEM_INFO) Kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) Kernel32.GetSystemInfo.restype = None Kernel32.GetSystemInfo.argtypes = (LPSYSTEM_INFO,) sysinfo = SYSTEM_INFO() Kernel32.GetSystemInfo(ctypes.byref(sysinfo)) print(sysinfo.lpMinimumApplicationAddress) print(sysinfo.lpMaximumApplicationAddress) # maybe it will change, maybe it won't. Assuming it won't. # 2nd, get Open process. import psutil import sys PID = 1234 PROCESS_QUERY_INFORMATION = 0x0400 PROCESS_VM_READ = 0x0010 Process = Kernel32.OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ, False, PID) print('process:', Process) # 3rd class MEMORY_BASIC_INFORMATION(ctypes.Structure): """https://msdn.microsoft.com/en-us/library/aa366775""" _fields_ = (('BaseAddress', PVOID), ('AllocationBase', PVOID), ('AllocationProtect', DWORD), ('RegionSize', SIZE_T), ('State', DWORD), ('Protect', DWORD), ('Type', DWORD)) ##PMEMORY_BASIC_INFORMATION = ctypes.POINTER(MEMORY_BASIC_INFORMATION) mbi = MEMORY_BASIC_INFORMATION() ##sysinfo.lpMinimumApplicationAddress print('VirtualQueryEx ran properly?',Kernel32.VirtualQueryEx(Process, \ sysinfo.lpMinimumApplicationAddress, ctypes.byref(mbi),ctypes.sizeof(mbi))) ReadProcessMemory = Kernel32.ReadProcessMemory ## MEM_COMMIT = 0x00001000; PAGE_READWRITE = 0x04; ##buffer = ctypes.c_uint() buffer = ctypes.c_double() nread = SIZE_T() start = ctypes.c_void_p(mbi.BaseAddress) current_address = sysinfo.lpMinimumApplicationAddress end_address = sysinfo.lpMaximumApplicationAddress while current_address < end_address: Kernel32.VirtualQueryEx(Process, \ current_address, ctypes.byref(mbi),ctypes.sizeof(mbi)) if mbi.Protect == PAGE_READWRITE and mbi.State == MEM_COMMIT : print('This region can be scanned!') index = current_address end = current_address + mbi.RegionSize while index < end: if ReadProcessMemory(Process, index, ctypes.byref(buffer), \ ctypes.sizeof(buffer), ctypes.byref(nread)): ## value comparison to be implemented. pass else: print('else happend.') ## pass ## raise ctypes.WinError(ctypes.get_last_error()) index += 1 current_address += mbi.RegionSize