ReadProcessMemory with ctypes

John Doe picture John Doe · Oct 3, 2012 · Viewed 14.9k times · Source

im working on a little solitär trainer. I don't know why the function ReadProcessMemory doesn't work. Normally it returns a False or True but in that case nothing. The GetlastError() gives me the Errorcode 6.

#-*- coding: cp1252 -*-

import ctypes, win32ui, win32process ,win32api

PROCESS_ALL_ACCESS = 0x1F0FFF
HWND = win32ui.FindWindow(None,"Solitär").GetSafeHwnd()
print(HWND)
PID = win32process.GetWindowThreadProcessId(HWND)[1]
print(PID)
PROCESS = win32api.OpenProcess(PROCESS_ALL_ACCESS,0,PID).handle

rPM = ctypes.windll.kernel32.ReadProcessMemory
wPM = ctypes.windll.kernel32.WriteProcessMemory

ADDRESS1 = 0x00E97074
ADDRESS2 = ctypes.create_string_buffer(64)
pi = ctypes.pointer(ADDRESS2)
rPM(PROCESS,ADDRESS1,ADDRESS2,64,0)
print(ADDRESS2)
x=ctypes.windll.kernel32.GetLastError()
print(x)

Answer

Mark Tolonen picture Mark Tolonen · Oct 4, 2012

Check the community comment to the MSDN ReadProcessMemory page, quote(sic):

W7 wont run read process memory

You may need to check your access permissions for "SE_DEBUG_NAME" for the current processes token. If not enabled. Enabled it. This must be done as administrator of course.

Also fully declare the return types and use the use_last_error parameter, where ctypes will cache the GetLastError() value internally directly after the call. Otherwise, it may be incorrect. If you are on a 64-bit system, SIZE_T and pointers are 64-bit values so ctypes needs to know the types to set up the stack correctly for the call.

...
from ctypes import wintypes
...
rPM = ctypes.WinDLL('kernel32',use_last_error=True).ReadProcessMemory
rPM.argtypes = [wintypes.HANDLE,wintypes.LPCVOID,wintypes.LPVOID,ctypes.c_size_t,ctypes.POINTER(ctypes.c_size_t)]
rPM.restype = wintypes.BOOL
wPM = ctypes.WinDLL('kernel32',use_last_error=True).WriteProcessMemory
wPM.argtypes = [wintypes.HANDLE,wintypes.LPVOID,wintypes.LPCVOID,ctypes.c_size_t,ctypes.POINTER(ctypes.c_size_t)]
wPM.restype = wintypes.BOOL

ADDRESS1 = 0x00E97074
ADDRESS2 = ctypes.create_string_buffer(64)
bytes_read = ctypes.c_size_t()
print(rPM(PROCESS,ADDRESS1,ADDRESS2,64,ctypes.byref(bytes_read)))
print(ctypes.get_last_error())

Also, FYI, even with all the fixes I get the same error value, but I didn't go through the trouble of enabling SE_DEBUG_NAME.

SOLVED

The following line is the issue:

PROCESS = win32api.OpenProcess(PROCESS_ALL_ACCESS,0,PID).handle

win32api.OpenProcess returns a temporary PyHANDLE that gets destroyed and closes the handle after the handle is retrieved.

The solution is to use:

PROCESS = win32api.OpenProcess(PROCESS_ALL_ACCESS,0,PID)
...
rPM(PROCESS.handle,ADDRESS1,ADDRESS2,64,0)

PROCESS then holds the PyHANDLE object and the handle remains valid.