Java .Class文件更改字符串

6
我正在尝试修改一个Minecraft模组(Gravisuite),每当我按F键时,它会显示“Gravitation Engine OFF/ON”,但我想要更改这个字符串。我开始使用十六进制编辑器将“Gravitation Engine OFF”替换为“Gravitation Engine Turned OFF”,但是文件后来变得无效了 :/ 我尝试使用像jbe、cjbe和rej之类的工具,发现该字符串在常量池中,但只能让我删除它...有没有办法在不破坏编译后的Java类的情况下更改字符串?谢谢。

我会先查找.class文件的二进制格式规范,并了解字符串是如何表示的,可能在实际字符串之前有一个大小字段,也许...? - Adam
1
尝试“Grav引擎关闭/开启”=相同字节数(覆盖模式)。 - Joop Eggen
@user265889 我们的任何答案解决了你的问题吗? - Adam
5个回答

3

我对同一个类进行了两次编译,只是稍微修改了一下,第一次使用的是"foo",第二次使用的是"foo-bar"

public class HelloWorld {
   public static final String HELLO = "foo-bar";
}

使用“foo”
000000b0  74 01 00 **03** 66 6f 6f 00  21 00 02 00 03 00 00 00  |t...foo.!.......|
000000c0  01 00 19 00 04 00 05 00  01 00 06 00 00 00 02 00  |................|
000000d0  07 00 01 00 01 00 08 00  09 00 01 00 0a 00 00 00  |................|
000000e0  1d 00 01 00 01 00 00 00  05 2a b7 00 01 b1 00 00  |.........*......|
000000f0  00 01 00 0b 00 00 00 06  00 01 00 00 00 01 00 01  |................|
00000100  00 0c 00 00 00 02 00 0d                           |........|

使用 "foo-bar"

000000b0  74 01 00 **07** 66 6f 6f 2d  62 61 72 00 21 00 02 00  |t...foo-bar.!...|
000000c0  03 00 00 00 01 00 19 00  04 00 05 00 01 00 06 00  |................|
000000d0  00 00 02 00 07 00 01 00  01 00 08 00 09 00 01 00  |................|
000000e0  0a 00 00 00 1d 00 01 00  01 00 00 00 05 2a b7 00  |.............*..|
000000f0  01 b1 00 00 00 01 00 0b  00 00 00 06 00 01 00 00  |................|
00000100  00 01 00 01 00 0c 00 00  00 02 00 0d              |............|

似乎长度也被编码在结构中。请注意3和7... 这个结构有更多信息。如果一个字符串有300个字符,前两个字节就是01 2c。因此,“Gravitation Engine Turned OFF”有29个字符,请确保在该字符串之前的字节改为1D,当前应该是19(“Gravitation Engine OFF/ON”25个字符)。

2
您可以查看Apache BCEL(字节码工程库),它包含一个非常强大的类,名为BCELifier。这是一个类,可以接受一个输入类,并在执行时创建一个类,该类编译并执行后将创建输入类。
什么?
是的。所以想象一下您有一个包含一些字符串的类,像这样:
public class ClassContainingStrings
{
    private String someString = "Some string";
    public void call()
    {
        System.out.println("Printed string");
        System.out.println(someString);
    }
}

现在,您可以编译此代码,以获取ClassContainingStrings.class文件。该文件可被输入到BCELifier中,如下所示:

import java.io.FileOutputStream;

import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.util.BCELifier;


public class ChangeStringInClassFile
{
    public static void main(String[] args) throws Exception
    {
        String classFileName = "ClassContainingStrings.class";
        JavaClass c = new ClassParser(classFileName).parse();
        BCELifier b = new BCELifier(c, 
            new FileOutputStream("ClassContainingStringsCreator.java"));
        b.start();
    }
}

它将创建一个名为ClassContainingStringsCreator.java的文件。对于给定的示例,它将如下所示:
import org.apache.bcel.generic.*;
import org.apache.bcel.classfile.*;
import org.apache.bcel.*;
import java.io.*;

public class ClassContainingStringsCreator implements Constants {
  private InstructionFactory _factory;
  private ConstantPoolGen    _cp;
  private ClassGen           _cg;

  public ClassContainingStringsCreator() {
    _cg = new ClassGen("ClassContainingStrings", "java.lang.Object", "ClassContainingStrings.java", ACC_PUBLIC | ACC_SUPER, new String[] {  });

    _cp = _cg.getConstantPool();
    _factory = new InstructionFactory(_cg, _cp);
  }

  public void create(OutputStream out) throws IOException {
    createFields();
    createMethod_0();
    createMethod_1();
    _cg.getJavaClass().dump(out);
  }

  private void createFields() {
    FieldGen field;

    field = new FieldGen(ACC_PRIVATE, Type.STRING, "someString", _cp);
    _cg.addField(field.getField());
  }

  private void createMethod_0() {
    InstructionList il = new InstructionList();
    MethodGen method = new MethodGen(ACC_PUBLIC, Type.VOID, Type.NO_ARGS, new String[] {  }, "<init>", "ClassContainingStrings", il, _cp);

    InstructionHandle ih_0 = il.append(_factory.createLoad(Type.OBJECT, 0));
    il.append(_factory.createInvoke("java.lang.Object", "<init>", Type.VOID, Type.NO_ARGS, Constants.INVOKESPECIAL));
    InstructionHandle ih_4 = il.append(_factory.createLoad(Type.OBJECT, 0));
    il.append(new PUSH(_cp, "Some string"));
    il.append(_factory.createFieldAccess("ClassContainingStrings", "someString", Type.STRING, Constants.PUTFIELD));
    InstructionHandle ih_10 = il.append(_factory.createReturn(Type.VOID));
    method.setMaxStack();
    method.setMaxLocals();
    _cg.addMethod(method.getMethod());
    il.dispose();
  }

  private void createMethod_1() {
    InstructionList il = new InstructionList();
    MethodGen method = new MethodGen(ACC_PUBLIC, Type.VOID, Type.NO_ARGS, new String[] {  }, "call", "ClassContainingStrings", il, _cp);

    InstructionHandle ih_0 = il.append(_factory.createFieldAccess("java.lang.System", "out", new ObjectType("java.io.PrintStream"), Constants.GETSTATIC));
    il.append(new PUSH(_cp, "Printed string"));
    il.append(_factory.createInvoke("java.io.PrintStream", "println", Type.VOID, new Type[] { Type.STRING }, Constants.INVOKEVIRTUAL));
    InstructionHandle ih_8 = il.append(_factory.createFieldAccess("java.lang.System", "out", new ObjectType("java.io.PrintStream"), Constants.GETSTATIC));
    il.append(_factory.createLoad(Type.OBJECT, 0));
    il.append(_factory.createFieldAccess("ClassContainingStrings", "someString", Type.STRING, Constants.GETFIELD));
    il.append(_factory.createInvoke("java.io.PrintStream", "println", Type.VOID, new Type[] { Type.STRING }, Constants.INVOKEVIRTUAL));
    InstructionHandle ih_18 = il.append(_factory.createReturn(Type.VOID));
    method.setMaxStack();
    method.setMaxLocals();
    _cg.addMethod(method.getMethod());
    il.dispose();
  }

  public static void main(String[] args) throws Exception {
    ClassContainingStringsCreator creator = new ClassContainingStringsCreator();
    creator.create(new FileOutputStream("ClassContainingStrings.class"));
  }
}

(是的,它看起来很丑,但那并不太重要)。重要的是,可以在原始类中找到字符串“Some string”和“Printed string”。您现在可以更改这些字符串,然后编译并执行此创建者类。

它将创建一个新的ClassContainingStrings.class,并带有修改后的字符串。


0

一个jar文件是类的zip文件,我猜你已经弄清楚了。你最好使用带有反编译插件的Java IDE(我很确定Intellij已经内置了这个功能)。一旦你反编译了它,就可以更改生成的源代码并重新编译它。

这不是简单的Java东西,但也不是太复杂。如果你以前做过一些Java项目开发,那么这并不难。


0

这些文件有校验和:

Archive:  dvt-utils.jar
Length   Method    Size  Ratio   Date   Time   CRC-32    Name
--------  ------  ------- -----   ----   ----   ------    ----
332  Defl:N      226  32%  11.05.31 19:41  a745ad09  META-INF/MANIFEST.MF

0

在一个 .jar 文件中有一个用于字符串的在线编辑器,点击这里。它的源代码在这里。最近我成功地使用了它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接