栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

如何在Windows上使用JNA从Java处理内存

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

如何在Windows上使用JNA从Java处理内存

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

我找到了关于pastebin
的教程,其中介绍了如何使用此库。但是,无需阅读即可理解以下内容。

假设您要操纵Windows游戏“单人纸牌”的地址及其值


知道,你做什么

  1. 如果要操纵地址及其值,请 知道该怎么做!
    您需要知道存储在地址中的值的大小。是4Byte还是8Byte或其他。

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

  3. 知道基本地址和动态地址之间的区别:

    • *每次重新启动应用程序时, *动态地址 都会更改(单人纸牌)。
      它们将包含所需的值,但是您每次都需要再次查找地址。因此,您首先需要学习的是如何获取基址。
      通过阅读CheatEngine教程了解这一点。

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

现在,您知道了您需要了解的知识,并开始使用纸牌上的CheatEngine做一些研究。


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

分数的基本地址:

0x10002AFA8

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

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


创建一个新项目

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

User32界面

感谢Todd Fast设置了User32接口。它还不完整,但是我们在这里需要足够的东西。

通过此接口,我们可以访问Windows上的user32.dll的某些功能。我们需要以下功能:

FindWindowA
GetWindowThreadProcessID

旁注:如果Eclipse告诉您它需要添加未实现的方法,请忽略它并以任何方式运行代码。

Kernel32接口

感谢Deject3d提供Kernel32接口。我做了一点修改。

此接口包含我们用于读取和写入内存的方法。

WriteProcessMemory
ReadProcessMemory
。它还包含打开流程的方法
OpenProcess

实际操作

现在,我们创建一个新类,其中将包含一些辅助方法和主要功能,作为JVM的访问点。

public class SolitaireHack {    public static void main(String... args)    {    }}

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

public class SolitaireHack {    final static long baseAddress = 0x10002AFA8L;    final static int[] offsets = new int[]{0x50,0x14};    public static void main(String... args)    {    }}

接下来,我们使用界面访问Windows特定方法:

导入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)    {    }}

最后但并非最不重要的一点是,我们创建了一些我们需要的权限常数,以获取对进程进行读写的权限。

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,我们可以打开该过程。

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。

如果获得了进程ID,则可以使用来打开它

openProcess
。此方法获取权限和pid,以打开进程并返回指向它的指针。要从进程中读取,需要权限PROCESS_VM_READ,而要从进程中写入,则需要权限PROCESS_VM_WRITE和PROCESS_VM_OPERATION。

接下来我们需要获取的是实际地址。动态地址。因此,我们需要另一种方法:

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
对象中存储了一些临时数据,这正是它所说的。记忆。它在基地址处读取,在存储器中获取新地址并添加偏移量。对所有偏移量都执行此操作,最后返回最后一个地址,即动态地址。

因此,现在我们想读取分数并将其打印出来。我们拥有动态分数,分数存储在此,只需要读出即可。分数是一个4字节的值。整数是4Byte数据类型。因此,我们可以使用Integer读取它。

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值。

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

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

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

这将是我们的新成绩。

newScore[0]
将是最低的字节,
newScore[3]
将是最高的字节。因此,如果您想将分数更改为20,
byte[]
则将是:
byte[] newScore = new byte[]{0x14,0x00,0x00,0x00};

让我们将其写在内存中:

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
对象,并将大小设置为数组的长度。我们将数据写入
Memory
具有正确偏移量的对象,然后将对象写入我们的地址。

现在,您应该获得572662306的出色得分。

如果您不完全了解某些kernel32或user32方法的功能,请查看MSDN或随意询问。

已知的问题:

如果您没有获得纸牌的进程ID,只需在任务管理器中检查它并手动写入pid。我认为德语的Solitär无法使用,因为名称中的ä。

希望您喜欢本教程。大部分内容来自其他教程,但都放在这里,所以如果有人需要一个起点,这应该会有所帮助。

再次感谢Deject3d和Todd Fast的帮助。如果您有任何问题,请告诉我,我会尽力帮助您。如果缺少某些内容,请让我自己知道或添加。

谢谢你,祝你有美好的一天。


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

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


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/497525.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号