在JNA中从内存指针读取值时发生ERROR_PARTIAL_COPY。

huangapple go评论94阅读模式
英文:

ERROR_PARTIAL_COPY reading value from memory pointer in JNA

问题

以下是翻译好的代码部分:

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 {

    // ... 其他代码 ...

    public static void main(String[] args) {
        int pid = getProcessId("Age of Empires II: Definitive Edition");
        com.sun.jna.platform.win32.WinNT.HANDLE process = openProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, pid);

        long theDynAddress = findDynAddress(process, offsets);
        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);
        if (!readProcessMemorySucceeded) {
            int lasterrorReadMemory = kernel32.GetLastError();
        }

        return output;
    }

    // ... 其他代码 ...

    public static long findDynAddress(com.sun.jna.platform.win32.WinNT.HANDLE process, int[] offsets) {

        // ... 其他代码 ...

        for (int i = 0; i < offsets.length; i++) {
            if (i == 0) {
                boolean test = kernel32.ReadProcessMemory(process, baseAddressPointer, pTemp, size, null);
                int lasterror = kernel32.GetLastError();
            }

            pointerAddress = ((pTemp.getInt(0) + offsets[i]));

            if (i != offsets.length - 1)
                kernel32.ReadProcessMemory(process, new Pointer(pointerAddress), pTemp, size, null);
        }

        return pointerAddress;
    }
}

请注意,此处只包括你提供的代码的翻译,不包括额外的内容。如果你有关于代码或问题的进一步疑问,请随时提问。

英文:

I'm trying to read a (float) value from a pointer within a process which window is called Age of Empires II: Definitive Edition.

This is my code:

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(&quot;kernel32&quot;,Kernel32.class);
static User32 user32 = (User32) Native.loadLibrary(&quot;user32&quot;, 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 (&quot;user32&quot;, 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(&quot;System.getProperty(\&quot;sun.arch.data.model\&quot;) = &quot; + System.getProperty(&quot;sun.arch.data.model&quot;));
int pid = getProcessId(&quot;Age of Empires II: Definitive Edition&quot;);
System.out.println(&quot;pid = &quot; + 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(&quot;theDynAddress = &quot; + 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(&quot;readProcessMemorySucceeded = &quot; + readProcessMemorySucceeded);
if (!readProcessMemorySucceeded) {
int lasterrorReadMemory = kernel32.GetLastError();
System.out.println(&quot;lasterror = &quot; + 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 &lt; offsets.length; i++)
{
if(i == 0)
{
boolean test = kernel32.ReadProcessMemory(process, baseAddressPointer, pTemp, size, null);
int lasterror = kernel32.GetLastError();
System.out.println(&quot;test = &quot; + test + &quot;, lasterror = &quot; + lasterror);
}
pointerAddress = ((pTemp.getInt(0)+offsets[i]));
if(i != offsets.length-1)
kernel32.ReadProcessMemory(process, new Pointer(pointerAddress), pTemp, size, null);
}
return pointerAddress;
}
}

This is the terminal output after running the jar:

System.getProperty(&quot;sun.arch.data.model&quot;) = 64
pid = 2192
test = false, lasterror = 299
theDynAddress = 1177510878
readProcessMemorySucceeded = false
lasterror = 299
1.64011448E10

Please note:

  • As you can see, the jar is compiled for 64-bit.
  • I run the jar with administrative privileges.
  • The process I'm trying to get access to is also 64-bit.

I'm getting error 299 which is labeled as ERROR_PARTIAL_COPY. The value I'm getting seems to be random. What can I do to prevent this?

I have no problem accessing the same value using Cheat Engine:

在JNA中从内存指针读取值时发生ERROR_PARTIAL_COPY。

答案1

得分: 1

以下是翻译好的内容:

与其查看最终错误值,您的调试应该集中在错误的第一个案例上。在这种情况下,您已经展示了它是findDynAddress()方法内部的初始ReadProcessMemory()调用,它试图从Pointer变量baseAddressPointer读取。

在代码的初始版本中,您定义了:

final static long baseAddress = 0x02B7C950L;
final static Pointer baseAddressPointer = new Memory(baseAddress);

您定义了一个基地址(我假设您从其他地方获取的,通常不建议硬编码这个),然后您试图使用Memory类创建一个指针。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()来获取它。您引用的帖子中的答案开头确实提到在使用这种方法时需要注意指针大小。


一个关键的事情是要理解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 {
    // ...(其他内容省略)
}

如果您有任何进一步的疑问,请随时问我。

英文:

Rather than looking at the final incorrect value, your debugging should focus on the first case of the error. In this case, you've shown it's the initial ReadProcessMemory() call inside your findDynAddress() method, which attempts to read from the Pointer variable baseAddressPointer.

In the initial version of your code, you defined:

final static long baseAddress = 0x02B7C950L;
final static Pointer baseAddressPointer = new Memory(baseAddress);

You've defined a base address (which I assume you got from somewhere else, it's generally dangerous to hardcode this) and then you attempt to create a pointer with it, but use the Memory class, a subclass of Pointer. The argument for the Memory constructor is a size, and the constructor allocates new memory at a new address, given the size of its argument, in this case 0x02B7C950 bytes of memory, about 45.6 megabytes!

While you've allocated that memory (and thus your later code doesn't fail to read, as it's reading memory you "own") it contains random data.

You probably meant to do:

final static Pointer baseAddressPointer = new Pointer(baseAddress);

<hr>

After editing your code, you still have an error. In this case, you may be reading a memory address from the native code, but you have only allocated 4 bytes in which to store it with this:

int size = 4;
Memory pTemp = new Memory(size);

And you're fetching what is supposed to be a 64-bit Pointer with getInt():

pointerAddress = ((pTemp.getInt(0)+offsets[i]));

You should be using 8 bytes to store the address, and use getLong() to fetch it after the call. The prologue to the answer in the post you are referencing does mention you need to be careful about pointer sizes when using this approach.

<hr>

One key thing is to understand symbols that CheatEngine uses.

Regarding the screenshot:

  • &quot;AoE2DE_s.exe&quot; is the load address of the module with the same name. lpmodinfo.lpBaseOfDll is where it's retrieved in the code. This address can be seen in CheatEngine under Memory View -> View -> Enumerate DLL's and Symbols.
  • +02BC6620 part is simple arithmetic operation. The 02BC6620 is stored in the code as baseAddress.
  • -&gt; means that the address on the left hand was accessed and under that address is stored another address which is on the right hand.

Finally, the code should look like this:

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 = &quot;AoE2DE_s.exe&quot;;
private static final int SIZE_OF_LONG = 8;
private static final String WINDOW_NAME = &quot;Age of Empires II: Definitive Edition&quot;;
final static int[] offsets = new int[]{0x00, 0x1A8, 0x00, 0x158, 0x28, 0x270};
static Kernel32 kernel32 = (Kernel32) Native.loadLibrary(&quot;kernel32&quot;,Kernel32.class);
static User32 user32 = (User32) Native.loadLibrary(&quot;user32&quot;, 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 (&quot;user32&quot;, 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(&quot;System.getProperty(\&quot;sun.arch.data.model\&quot;) = &quot; + System.getProperty(&quot;sun.arch.data.model&quot;));
int pid = getProcessId(WINDOW_NAME);
System.out.println(&quot;pid = &quot; + 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(&quot;theDynAddress = %x\n&quot;, 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&#39;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(&quot;lasterror = &quot; + 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 &lt; 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;
}
}

huangapple
  • 本文由 发表于 2020年10月1日 23:35:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/64158648.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定