查看当前类的依赖

假如有一个HelloWorld类:

import java.util.Random;

public class HelloWorld {
    public static void main(String[] args) throws InterruptedException {
        Random rand = new Random();
        int val = rand.nextInt(1000);
        Thread.sleep(val);
        System.out.println("Hello World");
    }
}

查看HelloWorld类所依赖的类:

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.ClassRemapper;
import org.objectweb.asm.commons.Remapper;

import java.util.HashSet;
import java.util.Set;

public class HelloWorldAnalysisCore {
    public static void main(String[] args) {
        String relative_path = "sample/HelloWorld.class";
        String filepath = FileUtils.getFilePath(relative_path);
        byte[] bytes = FileUtils.readBytes(filepath);

        //(1)构建ClassReader
        ClassReader cr = new ClassReader(bytes);

        //(2)分析ClassVisitor
        Set<String> dependencies = new HashSet<>();
        EmptyClassVisitor emptyVisitor = new EmptyClassVisitor(Opcodes.ASM9, null);
        ClassVisitor cv = new ClassRemapper(emptyVisitor, new Remapper() {
            @Override
            public String map(String internalName) {
                dependencies.add(internalName);
                return super.map(internalName);
            }
        });

        //(3)结合ClassReader和ClassVisitor
        int parsingOptions = ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES;
        cr.accept(cv, parsingOptions);

        //(4)打印依赖信息
        for (String item : dependencies) {
            System.out.println(item);
        }
    }
}

输出结果:

java/lang/Object
java/lang/String
java/util/Random
sample/HelloWorld
java/lang/InterruptedException
java/lang/System
java/lang/Thread
java/io/PrintStream

代码分析

核心代码段

Set<String> dependencies = new HashSet<>();
EmptyClassVisitor emptyVisitor = new EmptyClassVisitor(Opcodes.ASM9, null);
ClassVisitor cv = new ClassRemapper(emptyVisitor, new Remapper() {
    @Override
    public String map(String internalName) {
        dependencies.add(internalName);
        return super.map(internalName);
    }
});

EmptyClassVisitor

对于EmptyClassVisitor类,它本身并没有做什么有用的操作,只是一个空的实现,有三点情况需要注意:

  • 第一点,可以将EmptyClassVisitor类实例替换成ClassWriter类实例,但是没有很大的必要。原因:ClassWriter类功能更强大,但这些强大的功能,在当前场景下,并不需要使用到。
  • 第二点,不能将EmptyClassVisitor类实例替换成匿名类new ClassVisitor(Opcodes.ASM9) {}。原因:在默认情况下,ClassVisitorvisitField()visitMethod()返回值为null,影响功能发挥。
  • 第三点,不能将EmptyClassVisitor类实例替换成null,影响功能正常发挥。
import org.objectweb.asm.*;

public class EmptyClassVisitor extends ClassVisitor {
    public EmptyClassVisitor(int api, ClassVisitor classVisitor) {
        super(api, classVisitor);
    }

    @Override
    public ModuleVisitor visitModule(String name, int access, String version) {
        ModuleVisitor mv = super.visitModule(name, access, version);
        return new EmptyModuleVisitor(api, mv);
    }

    @Override
    public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
        AnnotationVisitor av = super.visitAnnotation(descriptor, visible);
        return new EmptyAnnotationVisitor(api, av);
    }

    @Override
    public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String descriptor, boolean visible) {
        AnnotationVisitor av = super.visitTypeAnnotation(typeRef, typePath, descriptor, visible);
        return new EmptyAnnotationVisitor(api, av);
    }

    @Override
    public RecordComponentVisitor visitRecordComponent(String name, String descriptor, String signature) {
        RecordComponentVisitor rcv = super.visitRecordComponent(name, descriptor, signature);
        return new EmptyRecordComponentVisitor(api, rcv);
    }

    @Override
    public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
        FieldVisitor fv = super.visitField(access, name, descriptor, signature, value);
        return new EmptyFieldVisitor(api, fv);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
        MethodVisitor mv = super.visitMethod(access, name, descriptor, signature, exceptions);
        return new EmptyMethodVisitor(api, mv);
    }

}

ClassRemapper和Remapper

在下面代码中,我们可以看到ClassRemapperRemapper类,这两个类一般用于“混淆处理.class文件”,两者有各自的作用:

  • ClassRemapper类负责找出所有可以替换的位置(类名、方法名和字段名)。
  • Remapper类负责决定哪些位置需要进行替换。
Set<String> dependencies = new HashSet<>();
EmptyClassVisitor emptyVisitor = new EmptyClassVisitor(Opcodes.ASM9, null);
ClassVisitor cv = new ClassRemapper(emptyVisitor, new Remapper() {
    @Override
    public String map(String internalName) {
        dependencies.add(internalName);
        return super.map(internalName);
    }
});

ClassRemapper类当中,我们重点关注于Remapper.mapType(String)方法,它会再进一步调用Remapper.map(String)方法。在记录类的依赖关系时,正是在Remapper.map(String)方法中进行的。 另外,如果我们有兴趣,可以进一步查看MethodRemapper.visitMethodInsn()方法。

public class ClassRemapper extends ClassVisitor {
    protected final Remapper remapper;

    public ClassRemapper(final ClassVisitor classVisitor, final Remapper remapper) {
        this(Opcodes.ASM9, classVisitor, remapper);
    }

    protected ClassRemapper(final int api, final ClassVisitor classVisitor, final Remapper remapper) {
        super(api, classVisitor);
        this.remapper = remapper;
    }

    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
        this.className = name;
        super.visit(version, access,
                remapper.mapType(name),
                remapper.mapSignature(signature, false),
                remapper.mapType(superName),
                interfaces == null ? null : remapper.mapTypes(interfaces));
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
        String remappedDescriptor = remapper.mapMethodDesc(descriptor);
        MethodVisitor methodVisitor =
                super.visitMethod(access,
                        remapper.mapMethodName(className, name, descriptor),
                        remappedDescriptor,
                        remapper.mapSignature(signature, false),
                        exceptions == null ? null : remapper.mapTypes(exceptions));
        return methodVisitor == null ? null : createMethodRemapper(methodVisitor);
    }

    protected MethodVisitor createMethodRemapper(final MethodVisitor methodVisitor) {
        return new MethodRemapper(api, methodVisitor, remapper);
    }
}