简体   繁体   English

ERROR_PARTIAL_COPY 从 JNA 中的内存指针读取值

[英]ERROR_PARTIAL_COPY reading value from memory pointer in JNA

I'm trying to read a (float) value from a pointer within a process which window is called Age of Empires II: Definitive Edition .我试图从一个进程中的一个指针读取一个(浮点)值,该窗口被称为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("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;
    }

}

This is the terminal output after running the jar:这是运行jar后的终端输出:

System.getProperty("sun.arch.data.model") = 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.如您所见,jar 是针对 64 位编译的。
  • I run the jar with administrative privileges.我以管理权限运行 jar。
  • The process I'm trying to get access to is also 64-bit.我试图访问的进程也是 64 位的。

I'm getting error 299 which is labeled as ERROR_PARTIAL_COPY .我收到标记为ERROR_PARTIAL_COPY错误 299。 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:我使用 Cheat Engine 访问相同的值没有问题:

作弊引擎截图

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 .在本例中,您已经展示了它是findDynAddress()方法内的初始ReadProcessMemory()调用,该调用尝试从Pointer变量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 .您已经定义了一个基地址(我假设您是从其他地方获得的,对其进行硬编码通常是危险的),然后您尝试用它创建一个指针,但使用Memory类,它是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! Memory构造函数的参数是一个大小,构造函数在给定参数大小的情况下在新地址分配新内存,在本例中为0x02B7C950字节的内存,大约 45.6 兆字节!

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);

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:在这种情况下,您可能正在从本机代码读取内存地址,但您只分配了 4 个字节来存储它:

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

And you're fetching what is supposed to be a 64-bit Pointer with getInt() :您正在使用getInt()获取应该是 64 位指针的内容:

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.您应该使用 8 个字节来存储地址,并在调用后使用getLong()获取它。 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.您引用的帖子中答案的序言确实提到在使用这种方法时需要注意指针大小。


One key thing is to understand symbols that CheatEngine uses.一个关键是理解 CheatEngine 使用的符号。

Regarding the screenshot:关于截图:

  • "AoE2DE_s.exe" is the load address of the module with the same name. "AoE2DE_s.exe"是同名模块的加载地址。 lpmodinfo.lpBaseOfDll is where it's retrieved in the code. lpmodinfo.lpBaseOfDll是在代码中检索它的位置。 This address can be seen in CheatEngine under Memory View -> View -> Enumerate DLL's and Symbols.该地址可以在 CheatEngine 中的 Memory View -> View -> Enumerate DLL's and Symbols 下看到。
  • +02BC6620 part is simple arithmetic operation. +02BC6620部分是简单的算术运算。 The 02BC6620 is stored in the code as baseAddress . 02BC6620作为baseAddress存储在代码中。
  • -> 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 = "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;
    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM