2

所以基本上我试图System.out.println("hey"); 在方法的末尾添加一个简单的。我使用了树 API。但是,我确实不断收到此错误:

java.lang.VerifyError:在分支目标 38 处期望堆栈图帧

这是我的代码:

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

这是我的班级节点:

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

这就是我“注入”代码的方式(我直接从 jar 加载它,这就是它使用 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);

就像我说的那样,当我运行它时,我会收到验证错误,有什么方法可以解决它,或者有什么更聪明的方法可以让我“注入”该代码?

4

1 回答 1

6

如果您在方法的末尾添加代码,则您是在其最后一条指令之后添加它,在编译 Java 代码时,该指令始终是 goto、switch、throw 或 return 语句。即使在编译没有明确返回语句的方法时,如

void foo() { }

你实际上正在编译

void foo() { return; }

最终回报是隐含的。随着您的添加,您将方法更改为

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

javac禁止这种无法访问的代码,但在字节码中是完全合法的。但是,对于无法访问的代码,您需要在其前面添加一个堆栈映射框架,该框架描述堆栈的状态和该点的局部变量数组。此时添加空框架的描述很容易,但我假设您想在 return 语句之前添加代码。

为了实现这一点,ASM 提供了一个AdviceAdapter允许您在返回语句之前添加代码的方法。据我所知,树 API 没有类似的东西,但您可以简单地在任何方法的指令列表中查找返回节点并在其之前添加代码。

于 2016-10-10T20:18:13.290 回答