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 ステートメントである最後の命令の後に追加します。次のような明示的な return ステートメントなしでメソッドをコンパイルする場合でも

void foo() { }

あなたは実際にコンパイルしています

void foo() { return; }

ここで、最終的なリターンは暗黙的です。追加により、メソッドを次のように変更します

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

このような到達不可能なコードはjavacでは禁止されていますが、バイト コードでは完全に合法です。ただし、到達不能なコードの場合は、その時点でのスタックとローカル変数配列の状態を記述するスタック マップ フレームを先頭に追加する必要があります。この時点で空のフレームの説明を追加するのは簡単ですが、return ステートメントの前にコードを追加したいと考えています。

これを実装するために、ASM はAdviceAdapterreturn ステートメントの前にコードを追加できる を提供しています。私の知る限り、ツリー API に似たものはありませんが、任意のメソッドの命令リスト内で戻りノードを探し、その前にコードを追加するだけです。

于 2016-10-10T20:18:13.290 に答える