42 - ASM之Class Transformation总结

2022-02-22  本文已影响0人  舍是境界

Class Transformation,从Core API的角度来说(第二个层次),我们介绍了asm.jar当中的ClassReader和Type两个类;同时,从应用的角度来说(第一个层次),我们也介绍了Class Transformation的原理和示例。

asm学习层次

Class Transformation的原理

在Class Transformation的过程中,我们主要使用到了ClassReader、ClassVisitor和ClassWriter三个类;其中ClassReader类负责“读”Class,ClassWriter负责“写”Class,而ClassVisitor则负责进行“转换”(Transformation)。

ClassVisitor传递示意图

在Java ASM当中,Class Transformation的本质就是利用了“中间人公(攻)鸡(击)”的方式来实现对已有的Class文件进行修改或转换。

中间人攻击

详细的来说,我们自己定义的ClassVisitor类就是一个“中间人”,那么这个“中间人”可以做什么呢?可以做三种类型的事情:

ASM能够做哪些转换操作

在类层面所做的修改,主要是通过ClassVisitor类来完成。我们将类层面可以修改的信息,分成以下三个方面:

public class HelloWorld extends Object implements Cloneable {
    public int intValue;
    public String strValue;

    public int add(int a, int b) {
        return a + b;
    }

    public int sub(int a, int b) {
        return a - b;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

为了让大家更明确的知道需要修改哪一个visitXxx()方法的参数,我们做了如下总结:

ClassVisitor.visitField(int access, String name, String descriptor, String - signature, Object value)

ClassVisitor.visitMethod(int access, String name, String descriptor, String signature, String[] exceptions)

再有,如何删除一个字段或者方法呢?其实很简单,我们只要让中间的某一个ClassVisitor在遇到该字段或方法时,不向后传递就可以了。在具体的代码实现上,我们只要让visitField()或visitMethod()方法返回一个null值就可以了。


最后,如何添加一个字段或方法呢?我们只要让中间的某一个ClassVisitor向后多传递一个字段和方法就可以了。在具体的代码实现上,我们是在visitEnd()方法完成对字段或方法的添加,而不是在visitField()或visitMethod()当中添加。因为我们要避免“一个类里有重复的字段和方法出现”,在visitField()或visitMethod()方法中,我们要判断该字段或方法是否已经存在;如果该字段或方法不存在,那我们就在visitEnd()方法进行添加;如果该字段或方法存在,那么我们就不需要在visitEnd()方法中添加了。

方法体层面的修改

在方法体层面所做的修改,主要是通过MethodVisitor类来完成。

在方法体层面的修改,更准确的地说,就是对方法体内包含的Instruction进行修改。就像数据库的操作“增删改查”一样,我们也可以对Instruction进行添加、删除、修改和查找。

为了让大家更直观的理解,我们假设有如下代码:

public class HelloWorld {
    public int test(String name, int age) {
        int hashCode = name.hashCode();
        hashCode = hashCode + age * 31;
        return hashCode;
    }
}

其中,test()方法的方法体包含的Instruction内容如下:

public test(Ljava/lang/String;I)I
    ALOAD 1
    INVOKEVIRTUAL java/lang/String.hashCode ()I
    ISTORE 3
    ILOAD 3
    ILOAD 2
    BIPUSH 31
    IMUL
    IADD
    ISTORE 3
    ILOAD 3
    IRETURN
    MAXSTACK = 3
    MAXLOCALS = 4

有的时候,我们想实现某个功能,但是感觉无从下手。这个时候,我们需要解决两个问题。第一个问题,就是要明确需要修改什么?第二个问题,就是“定位”方法,也就是要使用哪个方法进行修改。我们可以结合这两个问题,和下面的示例应用来理解。

由于MethodVisitor类里定义了很多的visitXxxInsn()方法,我们就不详细介绍了。但是,大家可以的看一下 asm4-guide.pdf的一段描述:

Methods can be transformed, i.e. by using a method adapter that forwards the method calls it receives with some modifications:

需要要注意一点:无论是添加instruction,还是删除instruction,还是要替换instruction,都要保持operand stack修改前和修改后是一致的。

小结

本文内容总结如下:

上一篇下一篇

猜你喜欢

热点阅读