我正在尝试学习 java 用于字节码检测的 asm 框架,但无法找到足够的文档或教程

I am trying to learn java asm framework for bytecode instrumentation but not able to find sufficient docs or tutorials on it

我正在尝试学习 java 用于字节码检测的 asm 框架,但找不到足够的文档或教程。

我研究了 ClassReaderClassWriterClassVisitor 以及一些更相似的 API,但不太清楚如何实现这些 API 以及如何编写相应的适配器。

假设我有一个 HelloWorld java class.

public class HelloWorld {

    public static void main(String[] args) {
//some code.....

    }

}

现在我想在字节码中插入一个变量"int i =10;"。请告诉我应该写什么Adapter/program。

提前致谢!

了解如何使用 ASM 的一个好方法是 运行使用 ASMifier 工具。 如果您只是想知道某些语言构造函数(例如变量初始值设定项)如何转换为字节码,创建一个简单的 Java class、编译它、找到它的 .class 文件和运行 javap 或用 IDE 打开它。

以下是向 class 添加附加字段的方法,例如 "int i = 10;"。 假设您正在使用 javaagent 来执行检测: 1) 使用以下作为 java agent

的 premain class
import java.lang.instrument.Instrumentation;

public class SimpleAgent {

    public static void premain(String agentArgs, Instrumentation inst) {

        ClassTransformer transformer = new ClassTransformer();
        inst.addTransformer(transformer);
    }
}   

2)addTransformer调用ClassTransformer的transform方法class定义如下

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;

public class ClassTransformer implements ClassFileTransformer{
    public byte[] transform(ClassLoader    loader,
            String              className,
            Class            classBeingRedefined,
            ProtectionDomain    protectionDomain,
            byte[]              b)
                    throws IllegalClassFormatException {
        try
        {
                ClassReader cr=new ClassReader(b);
                ClassWriter cw = new ClassWriter(cr,ClassWriter.COMPUTE_MAXS);
                AddField cp = new AddField(cw);
                cr.accept(cp,0);
                return cw.toByteArray();
        }
        catch(Exception e)
        {
            System.out.println(e);
        }
        return b;
    }
}

3) 最后AddField 如下是负责向class

添加新字段的ClassVisitor
import static org.objectweb.asm.Opcodes.ASM4;
import org.objectweb.asm.ClassVisitor;


class AddField extends ClassVisitor{

    static String className;
    static String methName, descrip;
    public AddField(ClassVisitor cv) {
        super(ASM4, cv);
    }

    @Override
    public void visit(int version, int access, String name,
            String signature, String superName, String[] interfaces) {
        className = name;
        cv.visit(version, access, name, signature, superName, interfaces);
    }
    public void visitEnd() {
        cv.visitField(0, "i", "I", null , new Integer(10));
        cv.visitEnd();
    }
}

4。 ** 新编辑 ** 用于将变量添加到方法中。该变量必须存储到一个临时变量中,以后可以使用。以下适配器可用于此目的(查看 onMethodEnter):

import static org.objectweb.asm.Opcodes.ASM4;
import static org.objectweb.asm.Opcodes.*;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.commons.AdviceAdapter;

public class MethodAdapter extends ClassVisitor {


    public MethodAdapter(ClassVisitor cv) {
        super(ASM4, cv);
    }

    @Override
    public void visit(int version, int access, String name,
            String signature, String superName, String[] interfaces) {
        cv.visit(version, access, name, signature, superName, interfaces);
    }


    public MethodVisitor visitMethod(int access, String name,
            String desc, String signature, String[] exceptions) {
        MethodVisitor mv;
        mv = cv.visitMethod(access, name, desc, signature, exceptions);
        mv = new AddVariableAdapter(access, name, desc, mv);
        return mv;
    }
    public void visitEnd() {
        cv.visitEnd();
    }


    public class AddVariableAdapter extends AdviceAdapter{
        public AddCallAdapter(int access, String name, String desc,  
                MethodVisitor mv) {  
            super(ASM4, mv, access, name, desc);  
        }  

        protected void onMethodEnter()  {
            mv.visitIntInsn(BIPUSH, 10); // pushes the number 10 on to the stack
            mv.visitVarInsn(ISTORE, 1);  // pops the top of the stack into a local variable indexed by 1
        /*  code to print the local variable
            mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            mv.visitVarInsn(ILOAD, 1);
            mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V");*/
        }
    }
}