I'm using cheat engine to find a multipointer that leads to an address that has value of list of entities. I found said multipointer, but I have no clue how to read it using python.
1 Answer
Here's an example on how to read from a process, on Windows, using the Windows API and ctypes. Doesn't require external packages / modules.
Error and argument handling is minimal.
Basically:
Open the process using
OpenProcessfor address space read. This only requires the process PID.Read a whole page from the process, using
ReadProcessMemory. You'll need to offset in the returned buffer if you want something within the page.Demonstrate how to "cast" from
bytesto, in this instance,DWORD. You can of course use anotherctypesnumerous types instead ofDWORD(which is defined asctypes.c_uint32iirc).
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import ctypes
import ctypes.wintypes as wt
# Windows API
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
OpenProcess = kernel32.OpenProcess
OpenProcess.restype = wt.HANDLE
OpenProcess.argtypes = (
wt.DWORD, # DWORD dwAccess
wt.BOOL, # BOOL bInheritHandle
wt.DWORD, # DWORD ProcessId
)
ReadProcessMemory = kernel32.ReadProcessMemory
ReadProcessMemory.restype = wt.BOOL
ReadProcessMemory.argtypes = (
ctypes.c_void_p, # HANDLE hProcess
ctypes.c_void_p, # LPCVOID lpBaseAddress
ctypes.c_void_p, # LPVOID lpBuffer
ctypes.c_size_t, # SIZE_T nSize
ctypes.POINTER(ctypes.c_size_t) # SIZE_T *lpNumberOfBytesRead
)
CloseHandle = kernel32.CloseHandle
CloseHandle.restype = wt.BOOL
CloseHandle.argtypes = (wt.HANDLE,)
def read_from_process(base_addr: int, pid: int) -> bytes:
"""Read a whole page from the given process.
Args:
base_addr: The base address to read from. This address is always rounded down to a 4KB page boundary.
pid: the pid of the process.
Raises:
OsError: either the process couldn't be opened or there was an error reading from the process.
Returns:
The content of the page read from the process as a bytes instance.
"""
# size of a page, in bytes.
page_size = 4096
# base of the page.
base_addr &= 0xfffffffffffff000
# open the process for address space reading.
h_proc = OpenProcess(0x8 | 0x10, # PROCESS_VM_OPERATION | PROCESS_VM_READ
False,
pid)
if h_proc == 0:
# couldn't open the process; either wrong pid or not enough rights to open it.
raise ctypes.WinError()
# read from the process.
buffer = ctypes.create_string_buffer(b'', page_size)
nobr = ctypes.c_size_t(0)
ret_val = ReadProcessMemory(h_proc, base_addr, buffer, page_size, ctypes.byref(nobr))
if ret_val == 0:
CloseHandle(h_proc)
raise ctypes.WinError()
CloseHandle(h_proc)
return buffer.raw
def main():
pid = 9708
base_addr = 0x7ff6947c0000
buffer = read_from_process(base_addr, pid)
# example how to read 2 DWORDs (unsigned 32-bit) from the bytes buffer.
dword_buffer = (wt.DWORD * 2).from_buffer_copy(buffer)
# print some bytes from the bytes instance.
print(buffer[0:8])
# print DWORDs.
print(f"{dword_buffer[0]:08x}")
print(f"{dword_buffer[1]:08x}")
if __name__ == '__main__':
main()
Test on the base of a PE module:
b'MZ\x90\x00\x03\x00\x00\x00'
00905a4d
00000003