Question

I am trying to generate a method named hello that returns the value 2 using dynamic bytecode generation. This is my current code. To generate the method.

    dout.writeShort(Modifier.PUBLIC);//class modifier
    dout.writeShort(classConstant("test"));//class name
    dout.writeShort(classConstant(Object.class.getName()));//superclass
    dout.writeShort(0);//interface count
    dout.writeShort(0);//field count
    dout.writeShort(1);//method count
    dout.writeShort(Modifier.PUBLIC|Modifier.STATIC);//modifiers
    dout.writeShort(utfConstant("test"));//name
    dout.writeShort(utfConstant(methodDescriptor(int.class, new Class[]{})));//descriptor
    dout.writeShort(1);//attribute count
    dout.writeShort(utfConstant("Code"));//attribute name
    dout.writeInt(34);//attribute length
    dout.writeShort(1);//max stack
    dout.writeShort(0);//max locals
    dout.writeInt(2);//code length
    dout.writeByte(0x05);//iconst_2 opcode
    dout.writeByte(0xAC);//ireturn opcode
    dout.writeShort(0);//exception count
    dout.writeShort(0);//attribute count
    dout.writeShort(0);//class attributes

The problem is that when i run this code, i get this exception

Exception in thread "main" java.lang.ClassFormatError: Invalid method Code length 0 in class file test
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:791)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:634)
    at Bytecode.BytecodeTest$BytecodeClassLoader.buildClass(BytecodeTest.java:229)
    at Bytecode.BytecodeTest.makeClass(BytecodeTest.java:42)
    at Bytecode.BytecodeTest.buildClass(BytecodeTest.java:27)
    at Bytecode.BytecodeTest.main(BytecodeTest.java:19)

The weird thing is i am making the code length greater than 0 i am making it 2. I went back through the oracle specification but it still looks right. I have a feeling that i am writing some of the data as the wrong type, but i still cant find a problem.

Was it helpful?

Solution 2

Well, one thing that strikes me is the attribute length: by my count, it should be 14 (not 34). You also seem to be missing the class attribute count.

It would probably help you to define a couple helper methods for writing attributes, to ensure that you are computing and writing the length correctly, e.g., something like this:

private int writeAttribute(final String attributeName) {
    dout.putShort(utfConstant(attributeName));
    dout.putInt(0);
    return dout.position();
}

private void endAttribute(final int attributeStart) {
    dout.putInt(attributeStart- 4, dout.position() - attributeStart);
}

private void writeCode() {
    final int codeAttributeStart = writeAttribute("Code");

    dout.writeShort(1);//max stack
    dout.writeShort(0);//max locals
    dout.writeInt(2);//code length
    dout.writeByte(0x05);//iconst_2 opcode
    dout.writeByte(0xAC);//ireturn opcode
    dout.writeShort(0);//exception count
    dout.writeShort(0);//attribute count

    endAttribute(codeAttributeStart);
}

Also, make sure the classfile minor/major version you're writing out matches the specification you're following--the format does change from time to time :).

OTHER TIPS

An undocumented feature of the Hotspot verifier is that for versions <= 45.2, it uses shorter field lengths for some of the fields in the code attribute. That's why changing the version to 49 fixed everything.

If you use Krakatau, it will automatically take care of this, but I haven't seen any other tools that handle this case.

Luckily, the first stable public version of Java was 45.3, so you are unlikely to see legitimate code like this in the wild. But it is a neat trick for foiling reverse engineers.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top