使用ASM进行字节码分析

时间:2016-02-22 12:26:12

标签: java java-bytecode-asm bytecode-manipulation jvm-bytecode

我正在评估使用ASM作为实现某些字节码分析的框架的可能性。到目前为止,我一直在玩一些例子,但有几件事我需要解决: 1)我不知道如何使用MethodVisitor类检测完全签名的方法(完整参数类型名称和正式名称)。

2)如果正在分析的.class文件与java源关联,如何将字节码指令与源中的行号链接

3)如何区分ClassVisitor中的实例字段和静态字段

2 个答案:

答案 0 :(得分:2)

  

1)我没有看到如何使用MethodVisitor类检测完全签名的方法(完整参数类型名称和正式名称)。

你做不到。 ClassVisitor接收类似

的调用
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) 

在返回MethodVistor之前,您需要从您感兴趣的参数中捕获信息。

如果使用调试信息编译代码,则可以使用visitLocalVariable

获取参数和局部变量名称
  

2)如果正在分析的.class文件与java源关联,如何将字节码指令与源中的行号链接

方法中的代码将以visitLabel

的形式提供源信息“instruction”
  

3)如何区分ClassVisitor中的实例字段和静态字段

通过access修饰符。使用Modifier.isStatic(access)

答案 1 :(得分:0)

1)实际上我设法提取详细的方法签名(参数类型和名称以及返回类型)作为MethodVisitor实现的一部分。 备注:仅在编译包含调试信息的类文件时才有效。

@Override
public MethodVisitor visitMethod(int access, String name,
        String desc, String signature, String[] exceptions) {



    try {
        final LinkedList<String> parameters;
        final boolean isStaticMethod;

        Type[] args = Type.getArgumentTypes(desc);
        Type ret = Type.getReturnType(desc);

        parameters = new LinkedList<String>();            
        isStaticMethod = Modifier.isStatic(access);

        return new MethodVisitor(Opcodes.ASM5) {
            // assume static method until we get a first parameter name
            public void visitLocalVariable(String name, String description, String signature, Label start, Label end, int index) {
                if (isStaticMethod && parameters.size() < args.length) {
                    parameters.add(args[index].getClassName()+" " +name);
                } else if (index > 0 && parameters.size() < args.length) {
                    // for non-static the 0th arg is "this" so we need to offset by -1
                    parameters.add(args[index-1].getClassName() +" " +name);
                }
            }

            @Override
            public void visitEnd() {
                 System.out.println("Method: "+ret.getClassName()+" "+name+"("+String.join(", ", parameters)+")");
                super.visitEnd();
            }
        };
    } catch (Exception e) {
        throw e;
    }     

这将为标准main产生以下输出:

  

方法:void main(java.lang.String [] args)

2)参数Label startLabel end包含有关相应源的信息,只要在编译发生时包含源。

3)参见@Peter Lawrey 回复。