ERROR_PARTIAL_COPY 从 JNA 中的内存指针读取值
ERROR_PARTIAL_COPY reading value from memory pointer in JNA
我正在尝试从 window 称为 Age of Empires II: Definitive Edition
的进程中的指针读取一个(浮点)值。
这是我的代码:
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.win32.StdCallLibrary;
public class Main {
final static long baseAddress = 0x02BC6620L;
final static Pointer baseAddressPointer = new Pointer(baseAddress);
final static int[] offsets = new int[]{0x00, 0x1A8, 0x00, 0x158, 0x28, 0x270};
static Kernel32 kernel32 = (Kernel32) Native.loadLibrary("kernel32",Kernel32.class);
static User32 user32 = (User32) Native.loadLibrary("user32", User32.class);
public static int PROCESS_VM_READ = 0x0010;
public static int PROCESS_VM_WRITE = 0x0020;
public static int PROCESS_VM_OPERATION = 0x0008;
public static interface User32 extends StdCallLibrary
{
final User32 instance = (User32) Native.loadLibrary ("user32", User32.class);
WinDef.HWND FindWindowExA(WinDef.HWND hwndParent, WinDef.HWND childAfter, String className, String windowName);
WinDef.HWND FindWindowA(String className, String windowName);
void GetWindowThreadProcessId(WinDef.HWND hwnd, IntByReference intByReference);
}
public static void main(String[] args) {
System.out.println("System.getProperty(\"sun.arch.data.model\") = " + System.getProperty("sun.arch.data.model"));
int pid = getProcessId("Age of Empires II: Definitive Edition");
System.out.println("pid = " + pid);
com.sun.jna.platform.win32.WinNT.HANDLE process = openProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, pid);
long theDynAddress = findDynAddress(process, offsets);
System.out.println("theDynAddress = " + theDynAddress);
Pointer dynAddress = new Pointer(theDynAddress);
Memory scoreMem = readMemory(process, dynAddress, 4);
float score = scoreMem.getFloat(0);
System.out.println(score);
}
public static Memory readMemory(com.sun.jna.platform.win32.WinNT.HANDLE process, Pointer address, int bytesToRead) {
IntByReference read = new IntByReference(0);
Memory output = new Memory(bytesToRead);
boolean readProcessMemorySucceeded = kernel32.ReadProcessMemory(process, address, output, bytesToRead, read);
System.out.println("readProcessMemorySucceeded = " + readProcessMemorySucceeded);
if (!readProcessMemorySucceeded) {
int lasterrorReadMemory = kernel32.GetLastError();
System.out.println("lasterror = " + lasterrorReadMemory);
}
return output;
}
public static int getProcessId(String window) {
IntByReference pid = new IntByReference(0);
user32.GetWindowThreadProcessId(user32.FindWindowA(null, window), pid);
return pid.getValue();
}
public static com.sun.jna.platform.win32.WinNT.HANDLE openProcess(int permissions, int pid) {
return kernel32.OpenProcess(permissions, true, pid);
}
public static long findDynAddress(com.sun.jna.platform.win32.WinNT.HANDLE process, int[] offsets)
{
int size = 4;
Memory pTemp = new Memory(size);
long pointerAddress = 0;
for(int i = 0; i < offsets.length; i++)
{
if(i == 0)
{
boolean test = kernel32.ReadProcessMemory(process, baseAddressPointer, pTemp, size, null);
int lasterror = kernel32.GetLastError();
System.out.println("test = " + test + ", lasterror = " + lasterror);
}
pointerAddress = ((pTemp.getInt(0)+offsets[i]));
if(i != offsets.length-1)
kernel32.ReadProcessMemory(process, new Pointer(pointerAddress), pTemp, size, null);
}
return pointerAddress;
}
}
这是运行安装 jar 后的终端输出:
System.getProperty("sun.arch.data.model") = 64
pid = 2192
test = false, lasterror = 299
theDynAddress = 1177510878
readProcessMemorySucceeded = false
lasterror = 299
1.64011448E10
请注意:
- 如您所见,jar 是为 64 位编译的。
- 我 运行 具有管理权限的 jar。
- 我尝试访问的进程也是 64 位的。
我收到标记为 ERROR_PARTIAL_COPY
的错误 299。我得到的值似乎是随机的。我能做些什么来防止这种情况?
我使用 Cheat Engine 访问相同的值没有问题:
与其查看最终不正确的值,不如将调试重点放在错误的第一种情况上。在这种情况下,您已经表明它是 findDynAddress()
方法中的初始 ReadProcessMemory()
调用,它尝试从 Pointer
变量 baseAddressPointer
.
中读取
在您的代码的初始版本中,您定义了:
final static long baseAddress = 0x02B7C950L;
final static Pointer baseAddressPointer = new Memory(baseAddress);
你已经定义了一个基地址(我假设你是从其他地方得到的,硬编码通常是危险的)然后你试图用它创建一个指针,但是使用 Memory
class,Pointer
的子class。 Memory
构造函数的参数是一个大小,构造函数在给定其参数大小的情况下在新地址分配新内存,在本例中为 0x02B7C950
字节内存,大约 45.6 兆字节!
虽然您已经分配了该内存(因此您以后的代码不会读取失败,因为它正在读取您“拥有”的内存),但它包含随机数据。
您可能打算这样做:
final static Pointer baseAddressPointer = new Pointer(baseAddress);
编辑代码后,您仍然有错误。在这种情况下,您可能正在从本机代码读取内存地址,但您只分配了 4 个字节来存储它:
int size = 4;
Memory pTemp = new Memory(size);
并且您正在使用 getInt()
:
获取应该是 64 位指针的内容
pointerAddress = ((pTemp.getInt(0)+offsets[i]));
您应该使用 8 个字节来存储地址,并在调用后使用 getLong()
来获取它。您引用的 post 中答案的序言确实提到您在使用这种方法时需要注意指针大小。
关键是要理解 CheatEngine 使用的符号。
关于截图:
"AoE2DE_s.exe"
是同名模块的加载地址。 lpmodinfo.lpBaseOfDll
是在代码中检索到的位置。这个地址可以在 CheatEngine 的 Memory View -> View -> Enumerate DLL's and Symbols 中看到。
+02BC6620
部分是简单的算术运算。 02BC6620
在代码中存储为 baseAddress
。
->
表示访问了左侧的地址,在该地址下存储了右侧的另一个地址。
最后,代码应该是这样的:
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.Psapi;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.win32.StdCallLibrary;
import java.util.Arrays;
public class Main {
final static long baseAddress = 0x02BC6620L;
private static final String MODULE_FILENAME = "AoE2DE_s.exe";
private static final int SIZE_OF_LONG = 8;
private static final String WINDOW_NAME = "Age of Empires II: Definitive Edition";
final static int[] offsets = new int[]{0x00, 0x1A8, 0x00, 0x158, 0x28, 0x270};
static Kernel32 kernel32 = (Kernel32) Native.loadLibrary("kernel32",Kernel32.class);
static User32 user32 = (User32) Native.loadLibrary("user32", User32.class);
public static int PROCESS_VM_READ = 0x0010;
public static int PROCESS_VM_WRITE = 0x0020;
public static int PROCESS_VM_OPERATION = 0x0008;
public static interface User32 extends StdCallLibrary
{
final User32 instance = (User32) Native.loadLibrary ("user32", User32.class);
WinDef.HWND FindWindowExA(WinDef.HWND hwndParent, WinDef.HWND childAfter, String className, String windowName);
WinDef.HWND FindWindowA(String className, String windowName);
void GetWindowThreadProcessId(WinDef.HWND hwnd, IntByReference intByReference);
}
public static void main(String[] args) {
System.out.println("System.getProperty(\"sun.arch.data.model\") = " + System.getProperty("sun.arch.data.model"));
int pid = getProcessId(WINDOW_NAME);
System.out.println("pid = " + pid);
com.sun.jna.platform.win32.WinNT.HANDLE process = openProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, pid);;
long theDynAddress = findDynAddress(process, offsets);
System.out.printf("theDynAddress = %x\n", theDynAddress);
Pointer dynAddress = new Pointer(theDynAddress);
Memory scoreMem = readMemory(process, dynAddress, 8);
float score = scoreMem.getFloat(0);
System.out.println(score);
}
public static long getBaseAddress(String windowName, String moduleFilename) {
int pid = getProcessId(windowName);
com.sun.jna.platform.win32.WinNT.HANDLE process = openProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, pid);
WinDef.HMODULE[] hModules = new WinDef.HMODULE[100 * 4]; //I'm not sure how big the array should be, this is some big random number
IntByReference lpcbNeeded = new IntByReference();
Psapi.INSTANCE.EnumProcessModules(process, hModules, hModules.length, lpcbNeeded);
hModules = Arrays.copyOf(hModules, lpcbNeeded.getValue() / SIZE_OF_LONG);
for(WinDef.HMODULE m: hModules){
Psapi.MODULEINFO lpmodinfo = new Psapi.MODULEINFO();
if (!Psapi.INSTANCE.GetModuleInformation(process, m, lpmodinfo, lpmodinfo.size())) {
//TODO: Error handling
}
char[] filename = new char[100 * 4];
int filenameLength = Psapi.INSTANCE.GetModuleFileNameExW(process, m, filename, 100 * 4);
String filenameString = new String(filename);
if (filenameString.contains(moduleFilename)) {
return readMemory(process, new Pointer((Pointer.nativeValue(lpmodinfo.lpBaseOfDll) + baseAddress)), SIZE_OF_LONG).getLong(0);
}
}
return -1;
}
public static Memory readMemory(com.sun.jna.platform.win32.WinNT.HANDLE process, Pointer address, int bytesToRead) {
IntByReference read = new IntByReference(0);
Memory output = new Memory(bytesToRead);
boolean readProcessMemorySucceeded = kernel32.ReadProcessMemory(process, address, output, bytesToRead, read);
if (!readProcessMemorySucceeded) {
int lasterrorReadMemory = kernel32.GetLastError();
System.out.println("lasterror = " + lasterrorReadMemory);
}
return output;
}
public static int getProcessId(String window) {
IntByReference pid = new IntByReference(0);
user32.GetWindowThreadProcessId(user32.FindWindowA(null, window), pid);
return pid.getValue();
}
public static com.sun.jna.platform.win32.WinNT.HANDLE openProcess(int permissions, int pid) {
return kernel32.OpenProcess(permissions, true, pid);
}
public static long findDynAddress(com.sun.jna.platform.win32.WinNT.HANDLE process, int[] offsets)
{
long pointerAddress = getBaseAddress(WINDOW_NAME, MODULE_FILENAME);
for(int i = 0; i < offsets.length; i++)
{
if (i != offsets.length-1) {
pointerAddress = readMemory(process, new Pointer(pointerAddress + offsets[i]), SIZE_OF_LONG).getLong(0);
} else {
pointerAddress = pointerAddress + offsets[i];
}
}
return pointerAddress;
}
}
我正在尝试从 window 称为 Age of Empires II: Definitive Edition
的进程中的指针读取一个(浮点)值。
这是我的代码:
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.win32.StdCallLibrary;
public class Main {
final static long baseAddress = 0x02BC6620L;
final static Pointer baseAddressPointer = new Pointer(baseAddress);
final static int[] offsets = new int[]{0x00, 0x1A8, 0x00, 0x158, 0x28, 0x270};
static Kernel32 kernel32 = (Kernel32) Native.loadLibrary("kernel32",Kernel32.class);
static User32 user32 = (User32) Native.loadLibrary("user32", User32.class);
public static int PROCESS_VM_READ = 0x0010;
public static int PROCESS_VM_WRITE = 0x0020;
public static int PROCESS_VM_OPERATION = 0x0008;
public static interface User32 extends StdCallLibrary
{
final User32 instance = (User32) Native.loadLibrary ("user32", User32.class);
WinDef.HWND FindWindowExA(WinDef.HWND hwndParent, WinDef.HWND childAfter, String className, String windowName);
WinDef.HWND FindWindowA(String className, String windowName);
void GetWindowThreadProcessId(WinDef.HWND hwnd, IntByReference intByReference);
}
public static void main(String[] args) {
System.out.println("System.getProperty(\"sun.arch.data.model\") = " + System.getProperty("sun.arch.data.model"));
int pid = getProcessId("Age of Empires II: Definitive Edition");
System.out.println("pid = " + pid);
com.sun.jna.platform.win32.WinNT.HANDLE process = openProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, pid);
long theDynAddress = findDynAddress(process, offsets);
System.out.println("theDynAddress = " + theDynAddress);
Pointer dynAddress = new Pointer(theDynAddress);
Memory scoreMem = readMemory(process, dynAddress, 4);
float score = scoreMem.getFloat(0);
System.out.println(score);
}
public static Memory readMemory(com.sun.jna.platform.win32.WinNT.HANDLE process, Pointer address, int bytesToRead) {
IntByReference read = new IntByReference(0);
Memory output = new Memory(bytesToRead);
boolean readProcessMemorySucceeded = kernel32.ReadProcessMemory(process, address, output, bytesToRead, read);
System.out.println("readProcessMemorySucceeded = " + readProcessMemorySucceeded);
if (!readProcessMemorySucceeded) {
int lasterrorReadMemory = kernel32.GetLastError();
System.out.println("lasterror = " + lasterrorReadMemory);
}
return output;
}
public static int getProcessId(String window) {
IntByReference pid = new IntByReference(0);
user32.GetWindowThreadProcessId(user32.FindWindowA(null, window), pid);
return pid.getValue();
}
public static com.sun.jna.platform.win32.WinNT.HANDLE openProcess(int permissions, int pid) {
return kernel32.OpenProcess(permissions, true, pid);
}
public static long findDynAddress(com.sun.jna.platform.win32.WinNT.HANDLE process, int[] offsets)
{
int size = 4;
Memory pTemp = new Memory(size);
long pointerAddress = 0;
for(int i = 0; i < offsets.length; i++)
{
if(i == 0)
{
boolean test = kernel32.ReadProcessMemory(process, baseAddressPointer, pTemp, size, null);
int lasterror = kernel32.GetLastError();
System.out.println("test = " + test + ", lasterror = " + lasterror);
}
pointerAddress = ((pTemp.getInt(0)+offsets[i]));
if(i != offsets.length-1)
kernel32.ReadProcessMemory(process, new Pointer(pointerAddress), pTemp, size, null);
}
return pointerAddress;
}
}
这是运行安装 jar 后的终端输出:
System.getProperty("sun.arch.data.model") = 64
pid = 2192
test = false, lasterror = 299
theDynAddress = 1177510878
readProcessMemorySucceeded = false
lasterror = 299
1.64011448E10
请注意:
- 如您所见,jar 是为 64 位编译的。
- 我 运行 具有管理权限的 jar。
- 我尝试访问的进程也是 64 位的。
我收到标记为 ERROR_PARTIAL_COPY
的错误 299。我得到的值似乎是随机的。我能做些什么来防止这种情况?
我使用 Cheat Engine 访问相同的值没有问题:
与其查看最终不正确的值,不如将调试重点放在错误的第一种情况上。在这种情况下,您已经表明它是 findDynAddress()
方法中的初始 ReadProcessMemory()
调用,它尝试从 Pointer
变量 baseAddressPointer
.
在您的代码的初始版本中,您定义了:
final static long baseAddress = 0x02B7C950L;
final static Pointer baseAddressPointer = new Memory(baseAddress);
你已经定义了一个基地址(我假设你是从其他地方得到的,硬编码通常是危险的)然后你试图用它创建一个指针,但是使用 Memory
class,Pointer
的子class。 Memory
构造函数的参数是一个大小,构造函数在给定其参数大小的情况下在新地址分配新内存,在本例中为 0x02B7C950
字节内存,大约 45.6 兆字节!
虽然您已经分配了该内存(因此您以后的代码不会读取失败,因为它正在读取您“拥有”的内存),但它包含随机数据。
您可能打算这样做:
final static Pointer baseAddressPointer = new Pointer(baseAddress);
编辑代码后,您仍然有错误。在这种情况下,您可能正在从本机代码读取内存地址,但您只分配了 4 个字节来存储它:
int size = 4;
Memory pTemp = new Memory(size);
并且您正在使用 getInt()
:
pointerAddress = ((pTemp.getInt(0)+offsets[i]));
您应该使用 8 个字节来存储地址,并在调用后使用 getLong()
来获取它。您引用的 post 中答案的序言确实提到您在使用这种方法时需要注意指针大小。
关键是要理解 CheatEngine 使用的符号。
关于截图:
"AoE2DE_s.exe"
是同名模块的加载地址。lpmodinfo.lpBaseOfDll
是在代码中检索到的位置。这个地址可以在 CheatEngine 的 Memory View -> View -> Enumerate DLL's and Symbols 中看到。+02BC6620
部分是简单的算术运算。02BC6620
在代码中存储为baseAddress
。->
表示访问了左侧的地址,在该地址下存储了右侧的另一个地址。
最后,代码应该是这样的:
import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.Psapi;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.win32.StdCallLibrary;
import java.util.Arrays;
public class Main {
final static long baseAddress = 0x02BC6620L;
private static final String MODULE_FILENAME = "AoE2DE_s.exe";
private static final int SIZE_OF_LONG = 8;
private static final String WINDOW_NAME = "Age of Empires II: Definitive Edition";
final static int[] offsets = new int[]{0x00, 0x1A8, 0x00, 0x158, 0x28, 0x270};
static Kernel32 kernel32 = (Kernel32) Native.loadLibrary("kernel32",Kernel32.class);
static User32 user32 = (User32) Native.loadLibrary("user32", User32.class);
public static int PROCESS_VM_READ = 0x0010;
public static int PROCESS_VM_WRITE = 0x0020;
public static int PROCESS_VM_OPERATION = 0x0008;
public static interface User32 extends StdCallLibrary
{
final User32 instance = (User32) Native.loadLibrary ("user32", User32.class);
WinDef.HWND FindWindowExA(WinDef.HWND hwndParent, WinDef.HWND childAfter, String className, String windowName);
WinDef.HWND FindWindowA(String className, String windowName);
void GetWindowThreadProcessId(WinDef.HWND hwnd, IntByReference intByReference);
}
public static void main(String[] args) {
System.out.println("System.getProperty(\"sun.arch.data.model\") = " + System.getProperty("sun.arch.data.model"));
int pid = getProcessId(WINDOW_NAME);
System.out.println("pid = " + pid);
com.sun.jna.platform.win32.WinNT.HANDLE process = openProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, pid);;
long theDynAddress = findDynAddress(process, offsets);
System.out.printf("theDynAddress = %x\n", theDynAddress);
Pointer dynAddress = new Pointer(theDynAddress);
Memory scoreMem = readMemory(process, dynAddress, 8);
float score = scoreMem.getFloat(0);
System.out.println(score);
}
public static long getBaseAddress(String windowName, String moduleFilename) {
int pid = getProcessId(windowName);
com.sun.jna.platform.win32.WinNT.HANDLE process = openProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, pid);
WinDef.HMODULE[] hModules = new WinDef.HMODULE[100 * 4]; //I'm not sure how big the array should be, this is some big random number
IntByReference lpcbNeeded = new IntByReference();
Psapi.INSTANCE.EnumProcessModules(process, hModules, hModules.length, lpcbNeeded);
hModules = Arrays.copyOf(hModules, lpcbNeeded.getValue() / SIZE_OF_LONG);
for(WinDef.HMODULE m: hModules){
Psapi.MODULEINFO lpmodinfo = new Psapi.MODULEINFO();
if (!Psapi.INSTANCE.GetModuleInformation(process, m, lpmodinfo, lpmodinfo.size())) {
//TODO: Error handling
}
char[] filename = new char[100 * 4];
int filenameLength = Psapi.INSTANCE.GetModuleFileNameExW(process, m, filename, 100 * 4);
String filenameString = new String(filename);
if (filenameString.contains(moduleFilename)) {
return readMemory(process, new Pointer((Pointer.nativeValue(lpmodinfo.lpBaseOfDll) + baseAddress)), SIZE_OF_LONG).getLong(0);
}
}
return -1;
}
public static Memory readMemory(com.sun.jna.platform.win32.WinNT.HANDLE process, Pointer address, int bytesToRead) {
IntByReference read = new IntByReference(0);
Memory output = new Memory(bytesToRead);
boolean readProcessMemorySucceeded = kernel32.ReadProcessMemory(process, address, output, bytesToRead, read);
if (!readProcessMemorySucceeded) {
int lasterrorReadMemory = kernel32.GetLastError();
System.out.println("lasterror = " + lasterrorReadMemory);
}
return output;
}
public static int getProcessId(String window) {
IntByReference pid = new IntByReference(0);
user32.GetWindowThreadProcessId(user32.FindWindowA(null, window), pid);
return pid.getValue();
}
public static com.sun.jna.platform.win32.WinNT.HANDLE openProcess(int permissions, int pid) {
return kernel32.OpenProcess(permissions, true, pid);
}
public static long findDynAddress(com.sun.jna.platform.win32.WinNT.HANDLE process, int[] offsets)
{
long pointerAddress = getBaseAddress(WINDOW_NAME, MODULE_FILENAME);
for(int i = 0; i < offsets.length; i++)
{
if (i != offsets.length-1) {
pointerAddress = readMemory(process, new Pointer(pointerAddress + offsets[i]), SIZE_OF_LONG).getLong(0);
} else {
pointerAddress = pointerAddress + offsets[i];
}
}
return pointerAddress;
}
}