如何使用ASM 4.0修改Java字节码

Nar*_*ana 7 java bytecode java-bytecode-asm

我是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();

}

}
Run Code Online (Sandbox Code Playgroud)

上述类应修改为:

public class ClassName {


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


public void showTwo()
{
    System.out.println("Show Two Method");
}

public static void main(String[] args) {

    ClassName c=new ClassName();
    c.showOne();
    c.showTwo();

}

}
Run Code Online (Sandbox Code Playgroud)

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

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

请帮我.+

Hol*_*ger 8

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

将a ClassWriterClassVisitor传递给a 组合时ClassReader,它将自动复制源类的每个功能,因此您只需要覆盖要应用更改的这些方法.

在这里,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);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 没有 `main` 方法,因为您没有在问题中指定 `main` 方法应该做什么。我在回答的开头已经解决了这个问题。我提供了一个接受字节数组并提供字节数组的 `static` 方法。你从哪里得到这个数组以及你将如何处理结果,这取决于你真正想要做什么。我读不懂你的心思。 (3认同)
  • 示例代码展示了“如何组合 ClassVisitor ClassReader 和 ClassWriter”。如果您需要ASM4,只需将示例中的`Opcodes.ASM5`替换为`Opcodes.ASM4`,据我所知,没有区别。我不知道我应该如何解释“它的流程”。您知道 [访问者模式的工作原理](http://en.wikipedia.org/wiki/Visitor_pattern) 吗? (2认同)