如何在Windows上使用JNA从Java操作内存 [英] How to manipulate memory from Java with JNA on Windows

查看:1366
本文介绍了如何在Windows上使用JNA从Java操作内存的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何从Java操作内存?我知道Java在它自己的JVM中运行,所以它无法直接访问进程内存。

How do I manipulate memory from Java? I know that Java runs in it's own JVM, so it can't access process memory directly.

我听说JNA可用于获取操作系统之间的接口和我的Java代码。

I heard about JNA which can be used to get interfaces between the Operating System and my Java code.

假设我想操纵Solitaire的得分。尝试将是这样的:

Let's say I want to manipulate the score of Solitaire. The attempt would be something like this:


  1. 获得纸牌的过程

  2. 获取访问权限记忆单人纸币

  3. 找出分数存储在内存中的位置

  4. 在地址中写下我的新值

  1. get the process of solitaire
  2. get access to the memory of solitaire
  3. find out where the score is stored in the memory
  4. write my new value in the address

Java本身无法访问该内存,因此如何使用JNA执行此操作?

Java itself can't access that memory, so how can I do this using JNA?

推荐答案

您需要使用JNA库。 下载两个Jar文件(jna.jar和jna-platform.jar)

You need to use the JNA Library. Download the two Jar-Files (jna.jar and jna-platform.jar)

我在pastebin上找到了教程,其中介绍了如何使用此库。但是没有必要阅读它以理解以下内容。

I found a tutorial on pastebin, which explains how to use this Library. But it won't be necessary to read it to understand the following.

假设您想要操纵Windows游戏Solitaire的地址及其值

Let's say, you want to manipulate the addresses and their values of the Windows game "Solitaire"


  1. 如果你想操纵地址及其值,知道你做了什么!

    你需要知道,存储在地址中的值的大小是多少。它是4Byte,还是8Byte或者是什么。

  1. If you want to manipulate addresses and their values, know what you do!
    You need to know, what size the value, stored in the address, has. Is it 4Byte, or 8Byte or whatsoever.

知道如何使用工具来获取动态和基地址。我使用 CheatEngine

Know how to use tools to get dynamic and base-addresses. I use CheatEngine.

了解差异基数地址和动态地址:

Know the difference bewteen base-addresses, and dynamic-addresses:


  • 动态地址每次都会更改你重新启动应用程序(Solitaire)。

    它们将包含所需的值,但你需要每次都找到该地址。所以你需要先学习的是如何获得基地址。

    通过阅读CheatEngine教程来学习。

  • Dynamic-addresses will change every time you restart the application (Solitaire).
    They will contain the needed value, but you would need to find the address again every time. So what you need to learn first, is how to get the base-address.
    Learn this by playing through the CheatEngine Tutorial.

基地址是静态地址。这些地址主要通过以下方式指向其他地址:[[base-addres + offset] + offset] - > value。因此,您需要了解基址,以及需要添加到地址以获取动态地址的偏移量。

Base-addresses are static addresses. Those addresses point to other addresses mostly the following way: [[base-addres + offset] + offset] -> value. So what you need is to know the base-address, and the offsets you need to add to the addresses to get the dynamic-address.

现在你知道了什么需要了解你使用CheatEngine on Solitaire进行一些研究。

So now that you know what you need to know you do some research with the CheatEngine on Solitaire.

您找到了动态地址并搜索了基地址?好的,让我们分享我们的结果:

You found your dynamic-address and searched for the base-address? Good, let's share our results:


得分的基地址: 0x10002AFA8

到达动态地址的偏移量: 0x50 (第一个)和 0x14 (秒)

Base-address for the score: 0x10002AFA8
Offsets to get to the dynamic-address: 0x50 (first) and 0x14 (second)

一切正常吗?好!让我们继续实际编写一些代码。

Got everything right? Good! Let's continue with actually writing some code.

在新项目中,您需要导入这些库。我使用Eclipse,但它应该适用于任何其他IDE。

In your new project, you need to import those libraries. I use Eclipse, but it should work on any other IDE.

感谢Todd Fast,用于设置 User32界面。它不完整,但我们需要足够的。

Thank's to Todd Fast, for setting up an User32 interface. It's not complete, but enough we need here.

使用此界面,我们可以访问Windows上user32.dll的某些功能。
我们需要以下功能: FindWindowA GetWindowThreadProcessID

With this interface, we get access to some functions of the user32.dll on Windows. We need the following functions: FindWindowA and GetWindowThreadProcessID

Side注意:如果Eclipse告诉你它需要添加未实现的方法,那么只需忽略它并运行代码。

感谢Deject3d的 Kernel32接口。我修改了一下。

Thank's to Deject3d for the Kernel32 interface. I modified it a little bit.

这个接口包含我们用来读写内存的方法。 WriteProcessMemory ReadProcessMemory 。它还包含一个打开进程的方法 OpenProcess

This interface contains the methods we use to read and write to memory. WriteProcessMemory and ReadProcessMemory. It also contains a method to open a process OpenProcess

我们现在创建一个新类,它将包含一些辅助方法,主要函数作为JVM的访问点。

We now create a new class which will contain some helper methods and the main function as access point for the JVM.

public class SolitaireHack {

    public static void main(String... args)
    {

    }
}

让我们填写我们的东西已经知道了,比如我们的偏移量和基地址。

Let's fill in stuff we already know, like our offsets and the base-address.

public class SolitaireHack {

    final static long baseAddress = 0x10002AFA8L;
    final static int[] offsets = new int[]{0x50,0x14};

    public static void main(String... args)
    {

    }
}

接下来我们使用我们的接口来访问我们的Windows特定方法:

Next we use our interfaces to get access to our Windows specific methods:

import com.sun .jna.Native;

import com.sun.jna.Native;

public class SolitaireHack {

    final static long baseAddress = 0x10002AFA8L;
    final static int[] offsets = new int[]{0x50,0x14};

    static Kernel32 kernel32 = (Kernel32) Native.loadLibrary("kernel32",Kernel32.class);
    static User32 user32 = (User32) Native.loadLibrary("user32", User32.class);

    public static void main(String... args)
    {

    }
}

最后但并非最不重要的是,我们创建了一些我们需要的权限常量,以获得读取和写入流程的权限。

Last but not least we create some permission constants we need, to get the permission to read and write to a process.

import com.sun.jna.Native;

public class SolitaireHack {

    final static long baseAddress = 0x10002AFA8L;
    final static int[] offsets = new int[]{0x50,0x14};

    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 void main(String... args)
    {

    }
}

为了获得一个我们可以操作内存的进程,我们需要获取窗口。此窗口可用于获取进程ID 。使用此ID,我们可以打开该过程。

In order to get a process, where we can manipulate the memory, we need to get the window. This window can be used to get the process id. With this id we can open the process.

public static void main(String... args)
{
    int pid = getProcessId("Solitaire");
    Pointer process = openProcess(PROCESS_VM_READ|PROCESS_VM_WRITE|PROCESS_VM_OPERATION, pid);
}

public static int getProcessId(String window) {
     IntByReference pid = new IntByReference(0);
     user32.GetWindowThreadProcessId(user32.FindWindowA(null, window), pid);

     return pid.getValue();
}

public static Pointer openProcess(int permissions, int pid) {
     Pointer process = kernel32.OpenProcess(permissions, true, pid);
     return process;
}

getProcessId 方法我们使用参数,即窗口的标题,来查找窗口句柄。 ( FindWindowA )此窗口句柄用于获取进程ID。 IntByReference是指针的JNA版本,其中将存储进程ID。

In the getProcessId method we use the parameter, which is the title of the window, to find the window handle. (FindWindowA) This window handle is used to get the process id. An IntByReference is the JNA version of an pointer, where the process id will be stored.

如果您获得进程ID,则可以使用它以 openProcess 打开进程。此方法获取权限和pid,以打开进程,并返回指向它的指针。要从进程读取,您需要权限PROCESS_VM_READ并从进程写入,您需要权限PROCESS_VM_WRITE和PROCESS_VM_OPERATION。

If you get the process id, you can use it to open the process with openProcess. This method gets the permissions and the pid, to open the process, and returns a pointer to it. To read from a process you need the permission PROCESS_VM_READ and to write from a process you need the permissions PROCESS_VM_WRITE and PROCESS_VM_OPERATION.

接下来我们需要得到的是实际地址。动态地址。所以我们需要另一种方法:

The next thing we need to get is the actual address. The dynamic-address. So we need another method:

public static void main(String... args)
{
    int pid = getProcessId("Solitaire");
    Pointer process = openProcess(PROCESS_VM_READ|PROCESS_VM_WRITE|PROCESS_VM_OPERATION, pid);

    long dynAddress = findDynAddress(process,offsets,baseAddress);
}

public static long findDynAddress(Pointer process, int[] offsets, long baseAddress)
{

    long pointer = baseAddress;

    int size = 4;
    Memory pTemp = new Memory(size);
    long pointerAddress = 0;

    for(int i = 0; i < offsets.length; i++)
    {
        if(i == 0)
        {
             kernel32.ReadProcessMemory(process, pointer, pTemp, size, null);
        }

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

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


    }

    return pointerAddress;
}

此方法需要进程,偏移量和基址。它将一些临时数据存储在 Memory 对象中,这正是它所说的。记忆。它读出基地址,获取内存中的新地址并添加偏移量。这是针对所有偏移完成的,并在最后一个地址返回,这将是动态地址。

This method needs the process, the offsets and the base-address. It stores some temporary data in a Memory object, which is exactly what it says. A memory. It reads out at the base-address, gets back a new address in the memory and adds the offset. This is done for all offsets and returns in the end the last address, which will be the dynamic-address.

所以现在我们想要读取我们的分数并将其打印出来。我们有动态地址,其中存储了分数,只需要阅读它。得分为4Byte值。 Integer是4Byte数据类型。所以我们可以使用Integer来读出它。

So now we want to read our score and print it out. We have the dynamic-addres where the score is stored and just need to read it out. The score is a 4Byte value. Integer is a 4Byte datatype. So we can use an Integer to read it out.

public static void main(String... args)
{
    int pid = getProcessId("Solitaire");
    Pointer process = openProcess(PROCESS_VM_READ|PROCESS_VM_WRITE|PROCESS_VM_OPERATION, pid);

    long dynAddress = findDynAddress(process,offsets,baseAddress);

    Memory scoreMem = readMemory(process,dynAddress,4);
    int score = scoreMem.getInt(0);
    System.out.println(score);
}

public static Memory readMemory(Pointer process, long address, int bytesToRead) {
    IntByReference read = new IntByReference(0);
    Memory output = new Memory(bytesToRead);

    kernel32.ReadProcessMemory(process, address, output, bytesToRead, read);
    return output;
}

我们为kernel32方法编写了一个包装器 readProcessMemory 。我们知道我们需要读取4Byte,所以bytesToRead将是4.在该方法中,将创建并返回 Memory 对象,其大小为byteToRead并存储数据,包含在我们的地址中。使用 .getInt(0)方法,我们可以在偏移量0处读出内存的Integer值。

We wrote a wrapper for our kernel32 method readProcessMemory. We know we need to read 4Byte so the bytesToRead will be 4. In the method, a Memory object will be created and returned, which will have the size of byteToRead and store the data, contained in our address. With the .getInt(0) method we can read out the Integer value of our memory at the offset 0.

玩一下你的单人纸牌,并获得一些积分。然后运行您的代码并读出值。检查这是否是你的分数。

Play a little bit with your solitaire, and get some points. Then run your code and read out the value. Check if it's your score.

我们的最后一步是操纵我们的分数。我们想成为最好的。所以我们需要将4Byte数据写入内存。

Our last step will be to manipulate our score. We want to be the best. So we need to write 4Byte data to our memory.

byte[] newScore = new byte[]{0x22,0x22,0x22,0x22};

这将是我们的新分数。 newScore [0] 将是最低字节, newScore [3] 将是最高字节。因此,如果您想将分数更改为值20,则 byte [] 将为:

byte [] newScore = new byte [] {0x14,0x00,0x00,0x00};

This will be our new score. newScore[0] will be the lowest byte and newScore[3] will be the highest one. So if you wanted to change your score to the value 20 your byte[] would be:
byte[] newScore = new byte[]{0x14,0x00,0x00,0x00};

让我们把它写在我们的记忆中:

Let's write it in our memory:

public static void main(String... args)
{
    int pid = getProcessId("Solitaire");
    Pointer process = openProcess(PROCESS_VM_READ|PROCESS_VM_WRITE|PROCESS_VM_OPERATION, pid);

    long dynAddress = findDynAddress(process,offsets,baseAddress);

    Memory scoreMem = readMemory(process,dynAddress,4);
    int score = scoreMem.getInt(0);
    System.out.println(score);

    byte[] newScore = new byte[]{0x22,0x22,0x22,0x22};
    writeMemory(process, dynAddress, newScore);
}

public static void writeMemory(Pointer process, long address, byte[] data)
{
    int size = data.length;
    Memory toWrite = new Memory(size);

    for(int i = 0; i < size; i++)
    {
            toWrite.setByte(i, data[i]);
    }

    boolean b = kernel32.WriteProcessMemory(process, address, toWrite, size, null);
}

使用我们的 writeMemory 方法,我们写一个 byte [] 调用数据到我们的地址。我们创建一个新的 Memory 对象,并将大小设置为数组的长度。我们使用正确的偏移量将数据写入内存对象,并将对象写入我们的地址。

With our writeMemory method, we write a byte[] called data to our address. We create a new Memory object and set the size to the length of the array. We write the data to the Memory object with the correct offsets and write the object to our address.

现在你应该有572662306的精彩分数。

Now you should have the fantastic score of 572662306.

如果您不确切知道某些kernel32或user32方法的作用,请查看MSDN或随意询问。

If you don't know exactly, what some kernel32 or user32 methods do, have a look at MSDN or feel free to ask.

已知问题:

如果您没有获得Solitaire的进程ID,请在您的任务管理器并手动写入pid。德国的Solitär不会工作,我想是因为名字中的ä。

If you don't get the process id of Solitaire, just check it in your Task Manager and write in the pid manually. The german Solitär won't work, i think because of the ä in the name.

我希望你喜欢这个教程。大多数部分都是来自其他一些教程,但是把它们放在一起,所以如果有人需要一个起点,这应该会有所帮助。

I hope you liked this tutorial. Most parts are from some other tutorials, but put all together here, so in case someone needs a starting point, this should help.

再次感谢Deject3d和Todd Fast他们的帮助。如果您有问题,请告诉我,我会尽力帮助您。如果缺少某些东西,请随时告诉我或自己添加。

Thanks again to Deject3d and Todd Fast for their help. If you have issues, just tell me and I try to help you out. If something is missing, feel fre to let me know or add it by yourself.

谢谢你,祝你有愉快的一天。

Thank you and have a nice day.

让我们来看看SolitaireHack类的完整代码:

Let's have a look at the full code of the SolitaireHack Class:

import com.sun.jna.Memory;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.IntByReference;

public class SolitaireHack {

    final static long baseAddress = 0x10002AFA8L;
    final static int[] offsets = new int[]{0x50,0x14};

    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 void main(String... args)
    {
        int pid = getProcessId("Solitaire");
        Pointer process = openProcess(PROCESS_VM_READ|PROCESS_VM_WRITE|PROCESS_VM_OPERATION, pid);

        long dynAddress = findDynAddress(process,offsets,baseAddress);

        Memory scoreMem = readMemory(process,dynAddress,4);
        int score = scoreMem.getInt(0);
        System.out.println(score);

        byte[] newScore = new byte[]{0x22,0x22,0x22,0x22};
        writeMemory(process, dynAddress, newScore);
    }

    public static int getProcessId(String window) {
         IntByReference pid = new IntByReference(0);
         user32.GetWindowThreadProcessId(user32.FindWindowA(null, window), pid);

         return pid.getValue();
    }

    public static Pointer openProcess(int permissions, int pid) {
         Pointer process = kernel32.OpenProcess(permissions, true, pid);
         return process;
    }

    public static long findDynAddress(Pointer process, int[] offsets, long baseAddress)
    {

        long pointer = baseAddress;

        int size = 4;
        Memory pTemp = new Memory(size);
        long pointerAddress = 0;

        for(int i = 0; i < offsets.length; i++)
        {
            if(i == 0)
            {
                 kernel32.ReadProcessMemory(process, pointer, pTemp, size, null);
            }

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

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


        }

        return pointerAddress;
    }

    public static Memory readMemory(Pointer process, long address, int bytesToRead) {
        IntByReference read = new IntByReference(0);
        Memory output = new Memory(bytesToRead);

        kernel32.ReadProcessMemory(process, address, output, bytesToRead, read);
        return output;
    }

    public static void writeMemory(Pointer process, long address, byte[] data)
    {
        int size = data.length;
        Memory toWrite = new Memory(size);

        for(int i = 0; i < size; i++)
        {
                toWrite.setByte(i, data[i]);
        }

        boolean b = kernel32.WriteProcessMemory(process, address, toWrite, size, null);
    }
}

这篇关于如何在Windows上使用JNA从Java操作内存的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆