NacOJerk NacOJerk - 1 month ago 15
Java Question

Getting verify error when working with asm java

So basicly Im trying to add a simple

System.out.println("hey");

at the end of a method. I used the tree API. I do however keep getting this error:


java.lang.VerifyError: Expecting a stackmap frame at branch target 38


This is my code:

public class MethodNodeCustom extends MethodNode {

public MethodNodeCustom(int paramInt, String paramString1, String paramString2, String paramString3, String[] paramArrayOfString) {
this(327680, paramInt, paramString1, paramString2, paramString3, paramArrayOfString);
return;
}

@SuppressWarnings({ "unchecked", "rawtypes" })
public MethodNodeCustom(int paramInt1, int paramInt2, String paramString1, String paramString2, String paramString3,
String[] paramArrayOfString) {
super(paramInt1);
this.access = paramInt2;
this.name = paramString1;
this.desc = paramString2;
this.signature = paramString3;
this.exceptions = new ArrayList((paramArrayOfString == null) ? 0 : paramArrayOfString.length);
int i = ((paramInt2 & 0x400) != 0) ? 1 : 0;
if (i == 0)
this.localVariables = new ArrayList(5);
this.tryCatchBlocks = new ArrayList();
if (paramArrayOfString != null)
this.exceptions.addAll(Arrays.asList(paramArrayOfString));
this.instructions = new InsnList();
}
@Override
public void visitEnd() {
AbstractInsnNode label = instructions.getLast();
instructions.remove(instructions.getLast());
instructions.remove(instructions.getLast());
visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", Type.getDescriptor(PrintStream.class));
visitLdcInsn("Cracked by damm ass pro skills");
visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
visitInsn(Opcodes.RETURN);
instructions.add(label);

super.visitEnd();
}
}


And this is my class node:

public class ClassNodeCustom extends ClassNode {
public ClassNodeCustom() {
super(ASMContentHandler.ASM4);
}

@SuppressWarnings("unchecked")
@Override
public MethodVisitor visitMethod(int paramInt, String paramString1, String paramString2, String paramString3, String[] paramArrayOfString) {
MethodNode localMethodNode = new MethodNodeCustom(paramInt, paramString1, paramString2, paramString3, paramArrayOfString);
this.methods.add(localMethodNode);
return localMethodNode;
}
}


And this is how I "inject" the code (I load it directly from the jar thats why it is using a zipFile)

InputStream in = zipFile.getInputStream(entry);
ClassReader cr = new ClassReader(in);
ClassNodeCustom node = new ClassNodeCustom();
cr.accept(node, 0);
ClassWriter cw = new ClassWriter(0);
node.accept(cw);


And like I said when ever I run it I get the verify error is there any way for me to solve it or any smarter way for me to "inject" that code ?

Answer

If you are adding code at the end of a method, you are adding it after its last instruction which is always a throw or a return statement when compiling Java code. Even when compiling a method without an explicit return statement like

void foo() { }

you are actully compiling

void foo() { return; }

where the final return is implicit. With your additions, you change the method to

void foo() {
  return;
  System.out.println("hey");
}

Such unreachable code is forbidden by javac but perfectly legal in byte code. For unreachable code, it is however required that you are prepending it with a stack map frame that describes the state of the stack and the local variable array at that point. It would be easy enough to add a description of an empty frame at this point but I assume that you want to add the code before the return statement.

To implement this, ASM offers an AdviceAdapter that allows you to add code before return statements. As far as I know, there is nothing similar for the tree API but you can simply look for a return node within any method's instruction list and add the code prior to it.