0

I need to know how to monitor a memory address and its values in Python. For example: I have a game that is written in C. I want to write a Python script that read the memory address of my current HitPoints and take actions based on its values.

I already can get the memory addresses with CheatEngine, but I don't know how use this in Python.

Denis Callau
  • 285
  • 2
  • 13
  • http://stackoverflow.com/questions/8250625/access-memory-address-in-python – Adam Barnes Jul 04 '16 at 14:35
  • Could you add some information to narrow field? By reference to CheatEngine I'm throwing a guess that OS in question is Windows? if so you should probably look for python methods that use same handles to what CE is using (`ReadProcessMemory`). – Tomasz Plaskota Jul 04 '16 at 14:36
  • Yes, Windows, I marked as tag. I'll check this methods. Thanks. – Denis Callau Jul 04 '16 at 14:39

1 Answers1

0

Here's a read_process function. The result is either bytes (2.x str), or an array of ctypes structures. The default is to read 1 byte from the process. The optional dtype parameter must be a ctypes type, such as ctypes.c_cint or a ctypes.Structure subclass. It reads an array of the given type and length.

Be careful to avoid dereferencing pointer values. For example, if you pass dtype=c_char_p, then simply indexing the result array will try to dereference a remote pointer in the current process, which will likely crash Python. In a previous answer I wrote a read-only RemotePointer class if you need to handle that case.

ctypes definitions

import ctypes
from ctypes import wintypes

kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)

PROCESS_VM_READ = 0x0010
SIZE_T = ctypes.c_size_t
PSIZE_T = ctypes.POINTER(SIZE_T)

def _check_bool(result, func, args):
    if not result:
        raise ctypes.WinError(error)
    return args

kernel32.OpenProcess.errcheck = _check_bool
kernel32.OpenProcess.restype = wintypes.HANDLE
kernel32.OpenProcess.argtypes = (
    wintypes.DWORD, # _In_ dwDesiredAccess
    wintypes.BOOL,  # _In_ bInheritHandle
    wintypes.DWORD) # _In_ dwProcessId

kernel32.CloseHandle.errcheck = _check_bool
kernel32.CloseHandle.argtypes = (
    wintypes.HANDLE,)

kernel32.ReadProcessMemory.errcheck = _check_bool
kernel32.ReadProcessMemory.argtypes = (
    wintypes.HANDLE,  # _In_  hProcess
    wintypes.LPCVOID, # _In_  lpBaseAddress
    wintypes.LPVOID,  # _Out_ lpBuffer
    SIZE_T,           # _In_  nSize
    PSIZE_T)          # _Out_ lpNumberOfBytesRead

read_process definition

def read_process(pid, address, length=1, dtype=ctypes.c_char):        
    result = (dtype * length)()        
    nread = SIZE_T()
    hProcess = kernel32.OpenProcess(PROCESS_VM_READ, False, pid)
    try:
        kernel32.ReadProcessMemory(hProcess, address, result, 
                                   ctypes.sizeof(result),
                                   ctypes.byref(nread))
    finally:
        kernel32.CloseHandle(hProcess)
    if issubclass(dtype, ctypes.c_char):
        return result.raw
    return result

example

if __name__ == '__main__':
    import os

    class DType(ctypes.Structure):
        _fields_ = (('x', ctypes.c_int),
                    ('y', ctypes.c_double))

    source = (DType * 2)(*[(42, 3.14),
                           (84, 2.72)])

    pid = os.getpid()
    address = ctypes.addressof(source)
    sink = read_process(pid, address, 2, DType)
    for din, dout in zip(source, sink):
        assert din.x == dout.x
        assert din.y == dout.y

    size = ctypes.sizeof(source)
    buf_source = ctypes.string_at(source, size)
    buf_sink = read_process(pid, address, size)
    assert buf_source == buf_sink
Community
  • 1
  • 1
Eryk Sun
  • 33,190
  • 5
  • 92
  • 111