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
There are checksums for the files:
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
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")
A jar file is a zip file of classes, I guess you've figured that out already. Your best best is to load up a a java IDE with a decompiler addon (pretty sure Intellij has this built in). Once you've decompiled you can change the generated source and recompile it.
This isn't trivial java stuff, but it's not so complicated either. If you've done some java project development before it's not so hard.
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.