Trouble with ReadProcessMemory in python to read 64bit process memory
Question: I'm getting error code 998 (ERROR_NOACCESS) when using ReadProcessMemory to
read the memory of a 64bit process (Minesweeper). I'm using python 3.5 64bit
on windows 7 64bit.

The strange thing is, this error only happens with addresses that are higher
up, like for example 0x 0000 0000 FF3E 0000. Lower addresses, like 0x 0000
0000 0012 AE40 don't throw an error and return correct Minesweeper data.

When I write the same program using nearly identical code in C#.NET and look
at the same addresses, it works and I don't get an error!

I know the address I'm looking at is correct because I can see it with Cheat
Engine and VMMap. I don't know if it's relevant, but the higher address I'm
looking at is the base module address of the MineSweeper.exe module in
Minesweeper.

Why is the python code not working?

Python code (throws error for higher addresses but works for lower):

    
    
    import ctypes, struct
    
    pid = 3484  # Minesweeper
    processHandle = ctypes.windll.kernel32.OpenProcess(0x10, False, pid)
    
    addr = 0x00000000FF3E0000  # Minesweeper.exe module base address
    buffer = (ctypes.c_byte * 8)()
    bytesRead = ctypes.c_ulonglong(0)
    result = ctypes.windll.kernel32.ReadProcessMemory(processHandle, addr, buffer, len(buffer), ctypes.byref(bytesRead))
    e = ctypes.windll.kernel32.GetLastError()
    
    print('result: ' + str(result) + ', err code: ' + str(e))
    print('data: ' + str(struct.unpack('Q', buffer)[0]))
    
    ctypes.windll.kernel32.CloseHandle(processHandle)
    
    # Output:
    # result: 0, err code: 998
    # data: 0
    

C#.NET code (64bit project, no errors):

    
    
    [DllImport("kernel32.dll")]
    static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
    
    [DllImport("kernel32.dll")]
    static extern Int32 ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] buffer, UInt32 size, out IntPtr lpNumberOfBytesRead);
    
    [DllImport("kernel32.dll")]
    static extern bool CloseHandle(IntPtr hObject);
    
    static void Main(string[] args)
    {
        var pid = 3484;  // Minesweeper
        var processHandle = OpenProcess(0x10, false, pid);
    
        var addr = 0x00000000FF3E0000;  // Minesweeper.exe module base address
        var buffer = new byte[8];
        IntPtr bytesRead;
        var result = ReadProcessMemory(processHandle, new IntPtr(addr), buffer, (uint)buffer.Length, out bytesRead);
    
        Console.WriteLine("result: " + result);
        Console.WriteLine("data: " + BitConverter.ToInt64(buffer, 0).ToString());
    
        CloseHandle(processHandle);
    
        Console.ReadLine();
    }
    
    // Output:
    // result: 1
    // data: 12894362189
    


Answer: It's good to be explicit when using `ctypes`. Setting `argtypes` and `restype`
appropriately will help check number and type of arguments, just like the
`DllImport` statements in C#.

Here's a pedantic example:

    
    
    import ctypes as c
    from ctypes import wintypes as w
    
    pid = 4568  # Minesweeper
    
    k32 = c.windll.kernel32
    
    OpenProcess = k32.OpenProcess
    OpenProcess.argtypes = [w.DWORD,w.BOOL,w.DWORD]
    OpenProcess.restype = w.HANDLE
    
    ReadProcessMemory = k32.ReadProcessMemory
    ReadProcessMemory.argtypes = [w.HANDLE,w.LPCVOID,w.LPVOID,c.c_size_t,c.POINTER(c.c_size_t)]
    ReadProcessMemory.restype = w.BOOL
    
    GetLastError = k32.GetLastError
    GetLastError.argtypes = None
    GetLastError.restype = w.DWORD
    
    CloseHandle = k32.CloseHandle
    CloseHandle.argtypes = [w.HANDLE]
    CloseHandle.restype = w.BOOL
    
    processHandle = OpenProcess(0x10, False, pid)
    
    addr = 0x00000000FF900000  # Minesweeper.exe module base address
    data = c.c_ulonglong()
    bytesRead = c.c_ulonglong()
    result = ReadProcessMemory(processHandle, addr, c.byref(data), c.sizeof(data), c.byref(bytesRead))
    e = GetLastError()
    
    print('result: {}, err code: {}, bytesRead: {}'.format(result,e,bytesRead.value))
    print('data: {:016X}h'.format(data.value))
    
    CloseHandle(processHandle)
    

Output:

    
    
    result: 1, err code: 0, bytesRead: 8
    data: 0000000300905A4Dh
    

Also note that you can pass the address of a data variable instead of creating
a buffer and unpacking it with the `struct` module.

