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

时间:2015-02-18 09:44:19

标签: java bytecode instrumentation java-bytecode-asm

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

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

假设我有一个HelloWorld java类。

public class HelloWorld {

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

    }

}

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

提前致谢!

2 个答案:

答案 0 :(得分:4)

以下是一种向类添加其他字段的方法,例如" int i = 10;"。 假设您使用javaagent执行检测: 1)使用以下作为java代理的premain类

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方法,该方法定义如下

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如下是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。 ** NEW EDIT **用于将变量添加到方法中。变量必须存储到临时变量中,然后才能使用。以下适配器可用于此目的(查看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");*/
        }
    }
}

答案 1 :(得分:1)

了解如何使用ASM的一个好方法是运行ASMifier工具。 如果您只想知道某些语言构造函数(如变量初始值设定项)是如何转换为字节码的,那么创建一个简单的Java类,编译它,找到它的.class文件并在其上运行javap可能会有所帮助。或者用IDE打开它。