2

I'm trying to read another process' memory in Python and I have the static address of the program and all the offsets. I'm using the win32api to do this. I can already read a process' memory with an address without offsets but I don't know how to use offsets.

I've already tried the script in this answer but it returns -1. I've changed the PROCESS_ALL_ACCESS to win32con.PROCESS_VM_READ and even then it returns -1.

How do I use the offsets with ReadProcessMemory?

Here is the code I'm using:

import win32api
import win32process
import win32con
import ctypes
import ctypes.wintypes as wintypes


def get_process_by_name(process_name):
    """Find the process id of the given
    process name and returns the process id."""

    process_name = process_name.lower()

    # Enumerate all processes
    processes = win32process.EnumProcesses()

    for process_id in processes:
        # If process_id is the same as this program, skip it
        if process_id == -1:
            continue

        # Try to read the process memory
        try:
            p_handle = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, True, process_id)

            # Try to read the modules of the process
            try:
                modules = win32process.EnumProcessModules(p_handle)

                for module_id in modules:
                    name = str(win32process.GetModuleFileNameEx(p_handle, module_id))

                    if name.lower().find(process_name) != -1:
                        return process_id
            finally:
                win32api.CloseHandle(p_handle)
        except:
            pass


def read_process_memory(process_id, address, offsets, size_of_data=4):

    p_handle = ctypes.windll.kernel32.OpenProcess(win32con.PROCESS_VM_READ, False, p_id)

    data = ctypes.c_uint(size_of_data)
    bytesRead = ctypes.c_uint(size_of_data)

    current_address = address

    if offsets:
        # Do something to the offsets
        ctypes.windll.kernel32.ReadProcessMemory(p_handle, current_address, ctypes.byref(data), ctypes.sizeof(data), ctypes.byref(bytesRead))

    else:
        ctypes.windll.kernel32.ReadProcessMemory(p_handle, current_address, ctypes.byref(data), ctypes.sizeof(data), ctypes.byref(bytesRead))


    # Close the handle to the process
    ctypes.windll.kernel32.CloseHandle(p_handle)

    return data.value

p_id = get_process_by_name("program.exe")

# Without offsets it works fine
address = 0x2ADB1818
val = read_process_memory(p_id, address, None)
print(val)


# Does not point to the correct address
address = 0x00571160
offsets = [0xD84, 0x1B8, 0x38, 0x5C, 0x24, 0xF4, 0x1D08]
for offset in offsets:
    address += offset

val = read_process_memory(p_id, address, offsets)
print(val)
Vasco Ferreira
  • 2,151
  • 2
  • 15
  • 21

1 Answers1

4

I've figured out what I was missing. I've been interpreting the addresses wrong. They are pointers to the addresses and so, when using offsets I need to read them, and add them to one another to get access to the value I want to read. Also, I needed to use the base address of the program, for which I just needed to return the value of the module.

Here is the above script with the necessary changes to read a process' memory with offsets:

import win32api
import win32process
import win32con
import ctypes


def get_process_by_name(process_name):
    """Finds the process id of the given
    process name and returns the process id and its base address."""

    process_name = process_name.lower()

    # Enumerate all processes
    processes = win32process.EnumProcesses()

    for process_id in processes:
        # If process_id is the same as this program, skip it
        if process_id == -1:
            continue

        # Try to read the process memory
        try:
            h_process = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, True, process_id)

            # Try to read the modules of the process
            try:
                # modules is an array of base addresses of each module
                modules = win32process.EnumProcessModules(h_process)

                for base_address in modules:
                    # Get the name of the module
                    name = str(win32process.GetModuleFileNameEx(h_process, base_address))

                    # Compare it to the name of your program
                    if name.lower().find(process_name) != -1:
                        return process_id, base_address
            finally:
                win32api.CloseHandle(h_process)
        except:
            pass


def read_process_memory(process_id, address, offsets=[]):
    """Read a process' memory based on its process id, address and offsets.
    Returns the address without offsets and the value."""

    # The handle to the program's process
    # This will allow to use ReadProcessMemory
    h_process = ctypes.windll.kernel32.OpenProcess(win32con.PROCESS_VM_READ, False, p_id)

    # This is a pointer to the data you want to read
    # Use `data.value` to get the value at this pointer
    # In this case, this value is an Integer with 4 bytes
    data = ctypes.c_uint(0)

    # Size of the variable, it usually is 4 bytes
    bytesRead = ctypes.c_uint(0)

    # Starting address
    current_address = address

    if offsets:
        # Append a new element to the offsets array
        # This will allow you to get the value at the last offset
        offsets.append(None)

        for offset in offsets:
            # Read the memory of current address using ReadProcessMemory
            ctypes.windll.kernel32.ReadProcessMemory(h_process, current_address, ctypes.byref(data), ctypes.sizeof(data), ctypes.byref(bytesRead))

            # If current offset is `None`, return the value of the last offset
            if not offset:
                return current_address, data.value
            else:
                # Replace the address with the new data address
                current_address = data.value + offset

    else:
        # Just read the single memory address
        ctypes.windll.kernel32.ReadProcessMemory(h_process, current_address, ctypes.byref(data), ctypes.sizeof(data), ctypes.byref(bytesRead))

    # Close the handle to the process
    ctypes.windll.kernel32.CloseHandle(h_process)

    # Return a pointer to the value and the value
    # The pointer will be used to write to the memory
    return current_address, data.value


# Open the process
p_id, base_address = get_process_by_name("program.exe")

# The static address needs the program base_address
address = base_address + 0x00571160
offsets = [0xD84, 0x1B8, 0x38, 0x5C, 0x24, 0xF4, 0x1D08]
pointer_value, value = read_process_memory(p_id, address, offsets)
print(f"(Static Address) Value: {value}")

# Re-reading the memory with the last pointer
pointer_value, value = read_process_memory(p_id, pointer_value, None)
print(f"(Dynamic Address) Value: {value}")
Vasco Ferreira
  • 2,151
  • 2
  • 15
  • 21