python 中的 ReadProcessMemory 读取 64 位进程内存时出现问题
Trouble with ReadProcessMemory in python to read 64bit process memory
我在使用 ReadProcessMemory 读取 64 位进程 (Minesweeper) 的内存时收到错误代码 998 (ERROR_NOACCESS)。我在 windows 7 64 位上使用 python 3.5 64 位。
奇怪的是,这个错误只发生在较高的地址,例如 0x 0000 0000 FF3E 0000。较低的地址,如 0x 0000 0000 0012 AE40 不会抛出错误 return 正确的扫雷数据。
当我在 C#.NET 中使用几乎相同的代码编写相同的程序并查看相同的地址时,它工作正常并且我没有收到错误!
我知道我正在查看的地址是正确的,因为我可以使用 Cheat Engine 和 VMMap 查看它。我不知道它是否相关,但我正在查看的更高地址是扫雷中 MineSweeper.exe 模块的基本模块地址。
为什么 python 代码不起作用?
Python 代码(高地址抛出错误但低地址有效):
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 代码(64 位项目,无错误):
[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
看到eryksun的评论,解决了我的问题!在 ReadProcessMemory 调用中将 'addr' 更改为 'ctypes.c_void_p(addr)'。
使用 ctypes
时最好明确一点。适当设置 argtypes
和 restype
将有助于检查参数的数量和类型,就像 C# 中的 DllImport
语句一样。
这是一个迂腐的例子:
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)
输出:
result: 1, err code: 0, bytesRead: 8
data: 0000000300905A4Dh
另请注意,您可以传递数据变量的地址,而不是创建缓冲区并使用 struct
模块将其解包。
我在使用 ReadProcessMemory 读取 64 位进程 (Minesweeper) 的内存时收到错误代码 998 (ERROR_NOACCESS)。我在 windows 7 64 位上使用 python 3.5 64 位。
奇怪的是,这个错误只发生在较高的地址,例如 0x 0000 0000 FF3E 0000。较低的地址,如 0x 0000 0000 0012 AE40 不会抛出错误 return 正确的扫雷数据。
当我在 C#.NET 中使用几乎相同的代码编写相同的程序并查看相同的地址时,它工作正常并且我没有收到错误!
我知道我正在查看的地址是正确的,因为我可以使用 Cheat Engine 和 VMMap 查看它。我不知道它是否相关,但我正在查看的更高地址是扫雷中 MineSweeper.exe 模块的基本模块地址。
为什么 python 代码不起作用?
Python 代码(高地址抛出错误但低地址有效):
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 代码(64 位项目,无错误):
[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
看到eryksun的评论,解决了我的问题!在 ReadProcessMemory 调用中将 'addr' 更改为 'ctypes.c_void_p(addr)'。
使用 ctypes
时最好明确一点。适当设置 argtypes
和 restype
将有助于检查参数的数量和类型,就像 C# 中的 DllImport
语句一样。
这是一个迂腐的例子:
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)
输出:
result: 1, err code: 0, bytesRead: 8
data: 0000000300905A4Dh
另请注意,您可以传递数据变量的地址,而不是创建缓冲区并使用 struct
模块将其解包。