python ctypes memory read stops at first zero integer

Question:

To all ctypes experts:

I am running this code in Python to read 0x400 entries from memory.
Unfortunately the returned list only contains ~20 entries. It seems to stop reading at the first entry with the value equal to 0.

    read_buffer = (ctypes.c_char * buffsize)()
    lp_buffer = ctypes.byref(read_buffer)
    n_size = ctypes.sizeof(read_buffer)
    lp_number_of_bytes_read = ctypes.c_ulong(0)
    ctypes.windll.kernel32.ReadProcessMemory(self.handle, ctypes.c_void_p(lp_base_address), lp_buffer, n_size, lp_number_of_bytes_read)
    return read_buffer.value

Is there any explanation/fix for this?
Here is an example output from my C++ implementation, where all 0x400 entries were read:

enter image description here

Asked By: emilector

||

Answers:

.value reads a null-terminated string from the buffer. Use .raw and trim to the known returned size.

Here’s a comparison:

>>> import ctypes
>>> read_buffer = (ctypes.c_char * 20)(bytes([1,2,3,0,4,5,6]))
>>> read_buffer.value   # reads until null byte
b'x01x02x03'
>>> read_buffer.raw     # entire 20-byte buffer
b'x01x02x03x00x04x05x06x00x00x00x00x00x00x00x00x00x00x00x00x00'
>>> read_buffer.raw[:7] # trimmed to a known size
b'x01x02x03x00x04x05x06'

This example read’s CPython’s own memory space and compares the memory read using two methods. Note that in CPython, id() returns the memory address of a Python object:

import sys
import ctypes as ct
import ctypes.wintypes as w

k32 = ct.WinDLL('kernel32')
ReadProcessMemory = k32.ReadProcessMemory
ReadProcessMemory.argtypes = w.HANDLE, w.LPCVOID, w.LPVOID, ct.c_size_t, ct.POINTER(ct.c_size_t)
ReadProcessMemory.restype = w.BOOL
GetCurrentProcess = k32.GetCurrentProcess
GetCurrentProcess.argtypes = ()
GetCurrentProcess.restype = w.HANDLE

s = b'ABCx00DEF'  # object to example
obj_len = sys.getsizeof(s)  # total object length (not string length)
data = ct.create_string_buffer(obj_len)
read = ct.c_size_t()
if ReadProcessMemory(GetCurrentProcess(), id(s), data, len(data), ct.byref(read)):
    print(f'{data.raw[:read.value]}')
print(ct.string_at(id(s), obj_len))

Output:

b'x02x00x00x00x00x00x00x00xb0xb10xa8xf8x7fx00x00x07x00x00x00x00x00x00x00xc2xe23-xeax99xddx97ABCx00DEFx00'
b'x02x00x00x00x00x00x00x00xb0xb10xa8xf8x7fx00x00x07x00x00x00x00x00x00x00xc2xe23-xeax99xddx97ABCx00DEFx00'
Answered By: Mark Tolonen
Categories: questions Tags: , , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.