Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java .Class file change string

Tags:

java

I'm trying to modify a minecraft mod (gravisuite) that puts "Gravitation Engine OFF/ON" whenever I press F, however I want to change this string, I started with replacing "Gravitation Engine OFF" with "Gravitation Engine Turned OFF" by using a hex editor but the file was no longer valid afterwards :/ I tried to use tools like jbe and cjbe and rej and that string is in the constant pool but it will only let me delete it...

Is there any way to change a string in a compiled java class without destroying it?

Thanks

like image 664
user265889 Avatar asked Jun 24 '16 13:06

user265889


2 Answers

I compiled the same class twice with a minor tweak, firstly with "foo" and then with "foo-bar"

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

With "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                           |........|

With "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              |............|

It seems that the length is also encoded in the structure. Note the 3 and the 7... There is more information on this structure

And with a String of 300 characters the preceding two bytes were 01 2c.

So given "Gravitation Engine Turned OFF" is 29 characters long, I'd make sure you change the byte immediately before the string to 1D, it should currently be 19 (25 characters for "Gravitation Engine OFF/ON")

like image 188
Adam Avatar answered Nov 15 '22 05:11

Adam


You could have a look at the Apache BCEL (ByteCode Engineering Library). It contains a remarkably powerful class called BCELifier. It is a class that can take an input class, and, when executed, creates a class that, when compiled and executed, creates the input class.

What?

Yes. So imagine you have a class containing some strings, like this:

public class ClassContainingStrings
{
    private String someString = "Some string";
    public void call()
    {
        System.out.println("Printed string");
        System.out.println(someString);
    }
}

Now, you can compile this, to obtain the ClassContainingStrings.class file. This file can be fed into the BCELifier, like this:

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

It will create a file called ClassContainingStringsCreator.java. For the given example, this will look like this:

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

(yes, it looks horrible, but that should not matter too much). The important thing is that the strings from the original class, namely the string "Some string" and "Printed string" can be found in there. Now, you can change these strings, and then compile and execute this creator class.

It will create a new ClassContainingStrings.class, with the modified strings.

like image 40
Marco13 Avatar answered Nov 15 '22 03:11

Marco13