Java 使用 ASM 的字节码检测,MethodVisitor 为空
Java bytecode instrumentation using ASM, MethodVisitor is null
因此,使用下面编写的代码,我的输出是:
Starting application with the Agent
Visiting class: HelloWorld
Class Major Version: 51
Super class: java/lang/Object
Source: HelloWorld.java
Method: <init> desc = ()V cv = com.amir.agent.instrumentor.amirClassVisitor@79f1d448 and mv = null
Method: main desc = ([Ljava/lang/String;)V cv = com.amir.agent.instrumentor.amirClassVisitor@79f1d448 and mv = null
Method: foo desc = ()V cv = com.amir.agent.instrumentor.amirClassVisitor@79f1d448 and mv = null
Method ends here
Done instrumenting: HelloWorld
这让我很困惑。为什么我的 methodVisitor
会是 null
?当 classVisitor
为 null
时,ASM 源代码似乎只有 return null
用于 methodVisitor
,这在我的情况下并非如此。
package com.amir.agent.instrumentor;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
public class amirClassVisitor {
private byte[] outData = null;
public amirClassVisitor() {
}
public void performInstrumentation(final String className,
final byte[] classAsBytes) {
final ClassVisitor cl = new ClassVisitor(Opcodes.ASM4) {
@Override
public void visit(final int version,
final int access,
final String name,
final String signature,
final String superName,
final String[] interfaces) {
System.out.println("Visiting class: "+name);
System.out.println("Class Major Version: "+version);
System.out.println("Super class: " + superName);
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public void visitOuterClass(final String owner,
final String name,
final String desc) {
System.out.println("Outer class: "+owner);
super.visitOuterClass(owner, name, desc);
}
@Override
public AnnotationVisitor visitAnnotation(final String desc,
final boolean visible) {
System.out.println("Annotation: "+desc);
return super.visitAnnotation(desc, visible);
}
@Override
public void visitAttribute(final Attribute attr) {
System.out.println("Class Attribute: " + attr.type);
super.visitAttribute(attr);
}
@Override
public void visitInnerClass(final String name,
final String outerName,
final String innerName,
final int access) {
System.out.println("Inner Class: " + innerName + " defined in " + outerName);
super.visitInnerClass(name, outerName, innerName, access);
}
@Override
public FieldVisitor visitField(final int access,
final String name,
final String desc,
final String signature,
final Object value) {
System.out.println("Field: "+name+" "+desc+" value:"+value);
return super.visitField(access, name, desc, signature, value);
}
@Override
public void visitEnd() {
System.out.println("Method ends here");
super.visitEnd();
}
@Override
public MethodVisitor visitMethod(final int access,
final String name,
final String desc,
final String signature,
final String[] exceptions) {
final MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
System.out.println("Method: " +name+ " desc = " +desc+ " cv = " +this+ " and mv = " +mv);
return mv;
}
@Override
public void visitSource(final String source,
final String debug) {
System.out.println("Source: "+source);
super.visitSource(source, debug);
}
};
final ClassReader classReader = new ClassReader(classAsBytes);
classReader.accept(cl, 0);
System.out.println("Done instrumenting: " +className);
}
public byte[] result() {
return outData;
}
}
编辑:
我这样称呼这段代码:
public class ClassLoadInterceptor implements ClassFileTransformer {
@SuppressWarnings("unchecked")
public byte[] transform(final java.lang.ClassLoader loader,
final java.lang.String className,
final java.lang.Class classBeingRedefined,
final java.security.ProtectionDomain protectionDomain,
final byte[] classfileBuffer) throws IllegalClassFormatException {
if (!(className.startsWith("java") || className.startsWith("sun") || className.startsWith("com/workday/agent"))) {
WorkdayClassVisitor v = new WorkdayClassVisitor();
v.performInstrumentation(className, classfileBuffer);
System.out.println("\t Instrumenting : " +className);
byte[] instrumented_class = v.result();
writeOutClassFile("debug", className + ".class", classfileBuffer);
writeOutClassFile("debug", className + "_instrumented" + ".class", instrumented_class);
return instrumented_class;
}
return classfileBuffer;
}
你打印出来的不是“my methodVisitor
”,而是 super
调用 visitMethod
的值 return,换句话说,默认值 return编辑者 ClassVisitor.visitMethod
:
Returns:
an object to visit the byte code of the method, or null
if this class visitor is not interested in visiting the code of this method.
由于抽象 class ClassVisitor
不执行任何操作,它没有兴趣访问该方法,因此 null
是完美的 return 值。
因此,由您来实例化一个新的 MethodVisitor
来实现您想要的行为。或者,由于您似乎打算检测 class,让您的 class 访问者委托给 ClassWriter
which you pass to the super class constructor。这样您将继承可以自定义的复制行为。然后,super.visitMethod
将 return 一个非 null
方法访问者,它将复制该方法。
因此,使用下面编写的代码,我的输出是:
Starting application with the Agent
Visiting class: HelloWorld
Class Major Version: 51
Super class: java/lang/Object
Source: HelloWorld.java
Method: <init> desc = ()V cv = com.amir.agent.instrumentor.amirClassVisitor@79f1d448 and mv = null
Method: main desc = ([Ljava/lang/String;)V cv = com.amir.agent.instrumentor.amirClassVisitor@79f1d448 and mv = null
Method: foo desc = ()V cv = com.amir.agent.instrumentor.amirClassVisitor@79f1d448 and mv = null
Method ends here
Done instrumenting: HelloWorld
这让我很困惑。为什么我的 methodVisitor
会是 null
?当 classVisitor
为 null
时,ASM 源代码似乎只有 return null
用于 methodVisitor
,这在我的情况下并非如此。
package com.amir.agent.instrumentor;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
public class amirClassVisitor {
private byte[] outData = null;
public amirClassVisitor() {
}
public void performInstrumentation(final String className,
final byte[] classAsBytes) {
final ClassVisitor cl = new ClassVisitor(Opcodes.ASM4) {
@Override
public void visit(final int version,
final int access,
final String name,
final String signature,
final String superName,
final String[] interfaces) {
System.out.println("Visiting class: "+name);
System.out.println("Class Major Version: "+version);
System.out.println("Super class: " + superName);
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
public void visitOuterClass(final String owner,
final String name,
final String desc) {
System.out.println("Outer class: "+owner);
super.visitOuterClass(owner, name, desc);
}
@Override
public AnnotationVisitor visitAnnotation(final String desc,
final boolean visible) {
System.out.println("Annotation: "+desc);
return super.visitAnnotation(desc, visible);
}
@Override
public void visitAttribute(final Attribute attr) {
System.out.println("Class Attribute: " + attr.type);
super.visitAttribute(attr);
}
@Override
public void visitInnerClass(final String name,
final String outerName,
final String innerName,
final int access) {
System.out.println("Inner Class: " + innerName + " defined in " + outerName);
super.visitInnerClass(name, outerName, innerName, access);
}
@Override
public FieldVisitor visitField(final int access,
final String name,
final String desc,
final String signature,
final Object value) {
System.out.println("Field: "+name+" "+desc+" value:"+value);
return super.visitField(access, name, desc, signature, value);
}
@Override
public void visitEnd() {
System.out.println("Method ends here");
super.visitEnd();
}
@Override
public MethodVisitor visitMethod(final int access,
final String name,
final String desc,
final String signature,
final String[] exceptions) {
final MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
System.out.println("Method: " +name+ " desc = " +desc+ " cv = " +this+ " and mv = " +mv);
return mv;
}
@Override
public void visitSource(final String source,
final String debug) {
System.out.println("Source: "+source);
super.visitSource(source, debug);
}
};
final ClassReader classReader = new ClassReader(classAsBytes);
classReader.accept(cl, 0);
System.out.println("Done instrumenting: " +className);
}
public byte[] result() {
return outData;
}
}
编辑:
我这样称呼这段代码:
public class ClassLoadInterceptor implements ClassFileTransformer {
@SuppressWarnings("unchecked")
public byte[] transform(final java.lang.ClassLoader loader,
final java.lang.String className,
final java.lang.Class classBeingRedefined,
final java.security.ProtectionDomain protectionDomain,
final byte[] classfileBuffer) throws IllegalClassFormatException {
if (!(className.startsWith("java") || className.startsWith("sun") || className.startsWith("com/workday/agent"))) {
WorkdayClassVisitor v = new WorkdayClassVisitor();
v.performInstrumentation(className, classfileBuffer);
System.out.println("\t Instrumenting : " +className);
byte[] instrumented_class = v.result();
writeOutClassFile("debug", className + ".class", classfileBuffer);
writeOutClassFile("debug", className + "_instrumented" + ".class", instrumented_class);
return instrumented_class;
}
return classfileBuffer;
}
你打印出来的不是“my methodVisitor
”,而是 super
调用 visitMethod
的值 return,换句话说,默认值 return编辑者 ClassVisitor.visitMethod
:
Returns:
an object to visit the byte code of the method, ornull
if this class visitor is not interested in visiting the code of this method.
由于抽象 class ClassVisitor
不执行任何操作,它没有兴趣访问该方法,因此 null
是完美的 return 值。
因此,由您来实例化一个新的 MethodVisitor
来实现您想要的行为。或者,由于您似乎打算检测 class,让您的 class 访问者委托给 ClassWriter
which you pass to the super class constructor。这样您将继承可以自定义的复制行为。然后,super.visitMethod
将 return 一个非 null
方法访问者,它将复制该方法。