1

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
2
  • 2
    The default integer argument conversion is to a 32-bit signed C int. When 0xff3e0000 is stored to a 64-bit register, it gets sign extended to 0xffffffffff3e0000. Since the parameter is actually a pointer, this is an address in kernel space that's not accessible in user mode. You need to either set ReadProcessMemory.argtypes or manually wrap the address as an instance of c_void_p. Commented Nov 22, 2015 at 14:42
  • @eryksun Thank you so much! Changed 'addr' to 'ctypes.c_void_p(addr)' in the ReadProcessMemory call, now it works. I was stuck on this for 2 days and could just not figure it out, you da man/woman. Commented Nov 22, 2015 at 15:11

2 Answers 2

2

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', use_last_error=True)

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

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 = c.get_last_error()

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.

Sign up to request clarification or add additional context in comments.

Comments

1

See eryksun's comment, it fixed my problem! Changed 'addr' to 'ctypes.c_void_p(addr)' in the ReadProcessMemory call.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.