Как изменить байт-код Java с помощью ASM 4.0

Я новичок в asm framework. Я работаю вокруг этой структуры ASM в течение недели. Я видел учебники в сети относительно разбора класса и генерации a .файл класса с нуля. Но я не могу следить за тем, как изменить существующий класс в 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()
{
    System.out.println("Show Two Method");
}

public static void main(String[] args) {

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

}

}

каким должен быть код ASM для его изменения?

я использовал инструмент ASMifier для создания кода. Но я не знаю, куда его применить.

пожалуйста, помогите мне. +

1 ответов


ваши требования немного недооценены. Ниже приведен пример программы, которая использует API посетителя ASM для преобразования класса, предположительно имеющего структуру вашего вопроса, в результирующий класс. Я добавил удобный метод, берущий массив байтов и возвращающий массив байтов. Такой способ может быть использован в обоих случаях, статическое преобразование применяется к классу файлов на диске, а также в качестве инструментария агента.

при объединении ClassWriter С ClassVisitor передано a ClassReader как показано ниже, он автоматически реплицирует каждую функцию исходного класса, поэтому вам нужно переопределить только эти методы, где вы хотите применить изменения.

здесь visitMethod переопределяется для перехвата при встрече main метод для его изменения и visitEnd переопределяется, чтобы добавить совершенно новый showTwo метод. The 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);
    }
  }
}