【问题标题】:How to modify a Java bytecode using ASM 4.0如何使用 ASM 4.0 修改 Java 字节码
【发布时间】:2014-08-29 05:22:55
【问题描述】:

我是 ASM 框架的新手。我已经围绕这个 ASM 框架工作了一周。我在网上看到有关解析类和从头开始生成 .class 文件的教程。 但是我无法理解如何修改 ASM 中的现有类。

我无法跟踪ClassVisitor、ClassWriter 和ClassReader 之间的执行流程。

请给我以下代码的 ASM 示例来解决我的问题。

public class ClassName {
  public void showOne() {
    System.out.println("Show One Method");
  }

  public static void main(String[] args) {
    ClassName c = new ClassName();
    c.showOne();
  }
}

上面的类应该修改为:

public class ClassName {
  public void showOne() {
    System.out.println("Show One Method");
  }

  public void showTwo() { // <- Newly added method
    System.out.println("Show Two Method");
  }

  public static void main(String[] args) {
    ClassName c = new ClassName();
    c.showOne();
    c.showTwo(); // <- Newly inserted method call
  }
}

修改它的 ASM 代码应该是什么?

我使用 ASMifier 工具生成代码。但我不知道在哪里应用它。

【问题讨论】:

  • @MartinFrank 但我想创建一个新的 mwthod 并调用它。那里指定的那个是基础级别的!
  • Np。也发生在我身上。

标签: java bytecode java-bytecode-asm


【解决方案1】:

您的要求有点不明确。下面是一个示例程序,它使用 ASM 的访问者 API 将假定具有问题结构的类转换为结果类。我添加了一个方便的方法,它接受一个字节数组并返回一个字节数组。这种方法可以用于两种情况,一种应用于磁盘上的类文件的静态转换,也可以用于 Instrumentation 代理。

当将ClassWriter 与传递给ClassReader 的ClassVisitor 组合在一起时,如下所示,它将自动复制源类的所有功能,因此您只需在要应用更改的地方覆盖这些方法。

这里,visitMethod 被覆盖以在遇到 main 方法对其进行修改时进行拦截,visitEnd 被覆盖以附加全新的 showTwo 方法。 MainTransformer 将拦截 RETURN 指令(您的示例中应该只有一个)以在其前面插入对 showTwo 的调用。

import org.objectweb.asm.*;
import org.objectweb.asm.commons.GeneratorAdapter;

public class MyTransformer extends ClassVisitor {

  public static byte[] transform(byte[] b) {
    final ClassReader classReader = new ClassReader(b);
    final ClassWriter cw = new ClassWriter(classReader,
      ClassWriter.COMPUTE_FRAMES|ClassWriter.COMPUTE_MAXS);
    classReader.accept(new MyTransformer(cw), ClassReader.EXPAND_FRAMES);
    return cw.toByteArray();
  }

  public MyTransformer(ClassVisitor cv) {
    super(Opcodes.ASM5, cv);
  }
  @Override
  public MethodVisitor visitMethod(int access, String name, String desc,
      String signature, String[] exceptions) {

    MethodVisitor v=super.visitMethod(access, name, desc, signature, exceptions);
    if(name.equals("main") && desc.equals("([Ljava/lang/String;)V"))
      v=new MainTransformer(v, access, name, desc, signature, exceptions);
    return v;
  }
  @Override
  public void visitEnd() {
    appendShowTwo();
    super.visitEnd();
  }
  private void appendShowTwo() {
    final MethodVisitor defVisitor = super.visitMethod(
      Opcodes.ACC_PUBLIC, "showTwo", "()V", null, null);
    defVisitor.visitCode();
    defVisitor.visitFieldInsn(Opcodes.GETSTATIC,
      "java/lang/System", "out", "Ljava/io/PrintStream;");
    defVisitor.visitLdcInsn("Show Two Method");
    defVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
      "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
    defVisitor.visitInsn(Opcodes.RETURN);
    defVisitor.visitMaxs(0, 0);
    defVisitor.visitEnd();
  }
  class MainTransformer extends GeneratorAdapter
  {
    MainTransformer(MethodVisitor delegate, int access, String name, String desc,
        String signature, String[] exceptions) {
      super(Opcodes.ASM5, delegate, access, name, desc);
    }
    @Override
    public void visitInsn(int opcode) {
      if(opcode==Opcodes.RETURN) {
        // before return insert c.showTwo();
        super.visitVarInsn(Opcodes.ALOAD, 1); // variable c
        super.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
            "ClassName", "showTwo", "()V", false);
      }
      super.visitInsn(opcode);
    }
  }
}

【讨论】:

  • 没有main 方法,因为您没有在问题中指定main 方法应该做什么。我已经在回答的开头提到了这个问题。我提供了一个接受字节数组并提供字节数组的static 方法。您从哪里获得该数组以及您将如何处理结果,这取决于您真正想要做什么。我无法读懂你的想法。
  • 所以reading a file into a byte array 和writing a byte array into a file 并不神奇。这适用于类文件,就像其他所有文件一样。你在什么时候遇到了困难?
  • 示例代码展示了“如何结合 ClassVisitor ClassReader 和 ClassWriter”。如果您需要ASM4,只需将示例中的Opcodes.ASM5 替换为Opcodes.ASM4,据我所知没有区别。我不知道我应该如何解释“它的流动”。你知道how the visitor pattern works吗?
  • 当然。您必须覆盖 visitMethodInsn 并检查调用的方法是否是您的 "sum" 方法。如果是,请调用super.visitLdcInsn(20); 以替换它,否则调用 super.visitMethodInsn`。
  • @Narayana:cmets 并不是要问几十个新问题。如果您有新问题,open a new question。
猜你喜欢
  • 2020-09-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多