pythonwinapictypesreadprocessmemory

Python ctypes - Receiving ERROR_PARTIAL_COPY when trying to ReadProcessMemory


My program, running elevated on Windows 10:

import ctypes
from ctypes import wintypes
import win32process
import psutil

targetProcess = "notepad.exe"
PROCESS_ALL_ACCESS = 0x1F0FFF
BUFFER_SIZE = 200

def getpid():
    for proc in psutil.process_iter():
        if proc.name() == targetProcess:
            return proc.pid

def main():
    status = ctypes.windll.ntdll.RtlAdjustPrivilege(20, 1, 0, ctypes.byref(ctypes.c_bool()))
    if(status == -1073741727):
        print("STATUS_PRIVILEGE_NOT_HELD - A required privilege is not held by the client.")

    hProcess = ctypes.windll.kernel32.OpenProcess(PROCESS_ALL_ACCESS, False, getpid())                        # handle to process
    lpBuffer = ctypes.create_string_buffer(BUFFER_SIZE)                                                       # Buffer we want to write results to
    targetProcessBaseAddress = None                                                                           # base address of the target processes entry module

    modules = win32process.EnumProcessModules(hProcess)                                                       # Retreive modules of target process
    for module in modules:
        name = str(win32process.GetModuleFileNameEx(hProcess, module))
        if targetProcess in name:
            targetProcessBaseAddress = hex(module)                                                                         

    count = ctypes.c_ulong(0)
    res = ctypes.windll.kernel32.ReadProcessMemory(hProcess, targetProcessBaseAddress, ctypes.byref(lpBuffer), BUFFER_SIZE, ctypes.byref(count))
    if res == 0:
        err = ctypes.windll.kernel32.GetLastError()
        if (err == 299):
            print("ERROR_PARTIAL_COPY - Only part of a ReadProcessMemory or WriteProcessMemory request was completed.")
        else:
            print(err)
    else:
        print(lpBuffer.raw)

if __name__ == '__main__':
    main()

Above is done via python3.8 using the native ctypes library.

I'm expecting to see a hexdump or any data other than 0x00,0x00.. but it seems my error is somewhere in the arguments provided to ReadProcessMemory, which is assumed due to error 299 returned from GetLastError(), which indicates:

"ERROR_PARTIAL_COPY - Only part of a ReadProcessMemory or WriteProcessMemory request was completed."

Not sure where I'm messing up, would be very grateful for suggestions and assistance!


Solution

    1. ReadProcessMemory second argument is a LPCVOID (long pointer to const void*) but you're passing the result of hex which returns a string (which then would translate to a pointer to string in ctypes context).

    2. Follow @CristiFati comment and use ctypes argtypes and restype which would have spotted the problem immediately.

    3. Do not use directly GetLastError from the win32 API. The interpreter is free to call any Windows API during its life, thus when you call this API you don't know if it's the result from your script or an API that was called by the interpreter for its own purpose. For this, ctypes proposes a specific variable which caches the result in the form of ctypes.get_last_error.

    The best way to do that is to start your script with something like that:

    import ctypes
    
    # obtain kernel32 WinDLL ensuring that we want to cache the last error for each API call.
    kernel32 = ctypes.WinDLL("kernel32", use_last_error = True)
    
    # start prototyping your APIs
    OpenProcess = kernel32.OpenProcess
    OpenProcess.argtypes = [ ... ]
    OpenProcess.restype = ...
    
    # then call the api
    res = OpenProcess( ... )
    
    #ensure you check the result by calling the cached last error
    if not res:
        err = ctypes.get_last_error()
        # you might also raise
        raise ctypes.WinError(err)