修改jar中的class:杀戮尖塔修改教程 | 您所在的位置:网站首页 › 杀戮尖塔mod制作教程下载安装 › 修改jar中的class:杀戮尖塔修改教程 |
缘起
作为一只程序猿,游戏自然是标配。自从入了杀戮尖塔的坑,几年来陆陆续续玩了几百小时。然而steam上的成就至今没刷完,发牌员和各路小怪次次都在针对我。在第 n n {n^n} nn次死于通往进阶20的三层小怪之手后,我感受到了出离的愤怒。 于是我决定,用技术怼它。 思路杀戮尖塔是用java开发,其主程序是一个.jar文件。现在要修改铁甲战士的基础卡:打击。 打开游戏目录,可以看到一个desktop-1.0.jar。这就是游戏的主程序。 首先使用Java Decompiler对其进行反编译。打开jd-gui,将desktop-1.0.jar拖进来: desktop-1.0\com\megacrit\cardcrawl\cards\red\ 这是卡片目录,red即红色,也就是铁甲战士的红色卡。 第一个文件叫Anger.class,也就是愤怒。我们向下找,可以到找到文件Strike_Red.class,从名称看,这就是要找的打击了。 这行代码设置了打击的基础伤害是6。我们的目标是将其修改为60。 然而,Java Decompiler只能查看class文件反编译后的结果,并不能对其进行修改。 并且由于该class文件import了众多库,无法直接使用javac命令来对单个文件进行编译。于是就需要借助jclasslib。 jclasslib byecode viewer分析class文件使用winrar对游戏目录下的desktop-1.0.jar进行解压。 打开,其下的0号属性是Code(很重要),这就是构造函数的代码。点击Code,可以在右侧面板查看其具体的实现: 同样,右侧上方的Generic info中包含了一个重要信息: Attribute name index: cp_info #58其中#58指的是常量池的58号索引位置。其名称叫Attribute name index。 右侧下方是构造函数的虚拟机代码。里面的指令借助栈和变量表及常量表进行了各种操作。我们需要关心的就是哪一行为变量赋值了6。 很明显地,可以看到如下代码: 27 aload_0 28 bipush 6 30 putfield #11关键是中间的28 bipush 6。该行代码的含义是: 该指令从code的第28号索引开始。指令为入栈一个int。入栈的int值为6。同时要明确: code是以byte为单位。每个指令占一个字节,比如这里的bipush。参考下一行代码,可知该行代码占2个字节,即28和29。将code看做一个byte数组,28号索引即code[28],其内容为bipush指令。code[29]就是入栈的int值6。 于是,只要我们能拿到code[29],并将其修改为60即可。 注意这里入栈的int值只占1个字节,且为带符号数,因此其值不应超过127。 byecode viewer只能进行查看,不能进行修改。 修改要修改class文件有两种方式: 借助二进制文件修改工具。创建一个java工程,使用第三方库。对于方式1,看似简单,实际需要直接分析代码的十六进制值,且要考虑到文件的字节对齐和加密等情况。 对于方式2,需借助第三方库。而第三方库必须集成到java中使用。故而需要创建一个java的工程。 这里使用方式2。故而先打开Intellij IDEA,新建一个JBoss工程。 依赖库安装java常用于修改class文件内容的库有2个:jclasslib和javassist。 jclasslib:可通过虚拟机汇编方式查看class文件内容,且提供的jar能够对class文件进行修改。javassist:常用于动态编程。两个库的安装方式如下。本篇采用jclasslib进行修改。 安装jclasslib 下载并安装打开jclasslib的官方github进行下载: https://github.com/ingokegel/jclasslib/releases 这里选择exe安装版本。下载后安装。 安装后,lib目录下为所有必要的jar包,修改class时需要将该文件夹导入到工程中作为依赖。 导入依赖到工程中打开: File→Project Structure... 左侧选择Modules,右侧选择Dependencies标签,点最右侧**+**号,选择JARs or directories…,在弹出的窗口中选择jclasslib安装目录下的lib文件夹。 打开javassist的官方github进行下载: http://www.javassist.org/ 下载后是个zip压缩包,解压并复制到指定目录下。 导入依赖到工程中打开: File→Project Structure... 左侧选择Modules,右侧选择Dependencies标签,点最右侧 + 号,选择JARs or directories…,在弹出的窗口中选择javassist目录下的javassist.jar文件。 修改class 准备工作 在桌面上创建一个Test文件夹,并将Strike_Red.class复制到这里。在Test文件夹下创建一个Change文件夹,用于存放修改后生成的Strike_Red.class文件。 编程进行修改和保存实现代码: import org.gjt.jclasslib.io.ClassFileWriter; import org.gjt.jclasslib.structures.AttributeInfo; import org.gjt.jclasslib.structures.ClassFile; import org.gjt.jclasslib.structures.MethodInfo; import org.gjt.jclasslib.structures.attributes.CodeAttribute; import java.io.DataInput; import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; public class Main { public static void main(String[] args) throws Exception { String filePath = "C:\\Users\\Administrator\\Desktop\\Test\\Strike_Red.class"; FileInputStream fis = new FileInputStream(filePath); DataInput di = new DataInputStream(fis); ClassFile cf = new ClassFile(); cf.read(di); // 载入类文件数据 // 获取构造函数 MethodInfo mi = cf.getMethod("", "()V"); // 方法的所有属性 AttributeInfo[] attributes = mi.getAttributes(); // 0号属性为Code CodeAttribute codeAttribute = (CodeAttribute)attributes[0]; byte[] code = codeAttribute.getCode(); // 获取Code的二进制内容 code[29] = 60; // 修改29号索引内容 codeAttribute.setCode(code); // 重设方法的Code内容 fis.close(); // 保存修改后的ClassFile File f = new File("C:\\Users\\Administrator\\Desktop\\Test\\Change\\Strike_Red.class"); ClassFileWriter.writeToFile(f, cf); } }整体思路为: 创建一个ClassFile,将class文件载入进来。获取构造函数,存放到一个MethodInfo对象中。这里用到的参数为构造函数的Name和Descriptor,前面的分析已经得知这两个值。获取构造函数所有的属性。0号属性即为Code,前面已经分析过。将获取到的0号属性转换为CodeAttribute。获取Code的二进制内容,是一个byte数组。要修改的内容位于29号索引,将其修改为60。这里若修改为>127的值IDEA会提示错误。将修改后的Code设置回构造函数的属性中。于是构造函数内容被修改,也就是当前ClassFile被修改。将修改后的ClassFile保存在Test/Change下。运行后,即会在Test/Change下生成Strike_Red.class文件。 将这个生成的文件拖入到Byecode viewer中,查看构造函数的Code,可以看到baseDamage已经修改为60: 现在需要将Strike_Red.class替换回desktop-1.0.jar。 首先将Java Decompiler和Byecode viewer都关闭,以防止对正在打开的文件进行修改。 然后用winrar打开desktop-1.0.jar(是打开不是解压),找到Strike_Red.class所在的路径: com\megacrit\cardcrawl\cards\red 将上一步生成的修改后的Strike_Red.class直接拖进来,替换。 这样修改就完成了。运行游戏看一下: |
CopyRight 2018-2019 实验室设备网 版权所有 |