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

请注意:

我收到标记为 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;
    }

}