• ASM使用小抄


    0x00

    参考:ASM教程

    0x01 Core Api

    生成新的类

    核心接口是ClassVisitor,其中方法调用的顺序为

    visit
    [visitSource][visitModule][visitNestHost][visitPermittedSubclass][visitOuterClass]
    (
     visitAnnotation |
     visitTypeAnnotation |
     visitAttribute
    )*
    (
     visitNestMember |
     visitInnerClass |
     visitRecordComponent |
     visitField |
     visitMethod
    )* 
    visitEnd
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    ClassWrite继承于ClassVisitor,ClassWrite最重要的是在构造方法时提供的参数
    在创建ClassWriter对象的时候,要指定一个flags参数,它可以选择的值有三个
    第一个,可以选取的值是0。ASM不会自动计算max stacks和max locals,也不会自动计算stack map frames。
    第二个,可以选取的值是ClassWriter.COMPUTE_MAXS。ASM会自动计算max stacks和max locals,但不会自动计算stack map frames。
    第三个,可以选取的值是ClassWriter.COMPUTE_FRAMES(推荐使用)。ASM会自动计算max stacks和max locals,也会自动计算stack map frames。

    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
    
    • 1

    生成接口

    public interface HelloWorld {
    }
    
    • 1
    • 2
    // (1) 创建ClassWriter对象
    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
    
    // (2) 调用visitXxx()方法
    cw.visit(
            V1_8,                                        // version
            ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE,   // access
            "sample/HelloWorld",                         // name
            null,                                        // signature
            "java/lang/Object",                          // superName
            null                                         // interfaces
    );
    
    cw.visitEnd();
    
    // (3) 调用toByteArray()方法
    return cw.toByteArray();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    生成接口+字段+方法

    public interface HelloWorld extends Cloneable {
        int LESS = -1;
        int EQUAL = 0;
        int GREATER = 1;
        int compareTo(Object o);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    // (1) 创建ClassWriter对象
    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
    
    // (2) 调用visitXxx()方法
    cw.visit(V1_8, ACC_PUBLIC + ACC_ABSTRACT + ACC_INTERFACE, "sample/HelloWorld",
            null, "java/lang/Object", new String[]{"java/lang/Cloneable"});
    
    {
        FieldVisitor fv1 = cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "LESS", "I", null, -1);
        fv1.visitEnd();
    }
    
    {
        FieldVisitor fv2 = cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "EQUAL", "I", null, 0);
        fv2.visitEnd();
    }
    
    {
        FieldVisitor fv3 = cw.visitField(ACC_PUBLIC + ACC_FINAL + ACC_STATIC, "GREATER", "I", null, 1);
        fv3.visitEnd();
    }
    
    {
        MethodVisitor mv1 = cw.visitMethod(ACC_PUBLIC + ACC_ABSTRACT, "compareTo", "(Ljava/lang/Object;)I", null, null);
        mv1.visitEnd();
    }
    
    
    cw.visitEnd();
    
    // (3) 调用toByteArray()方法
    return cw.toByteArray();
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    生成类

    public class HelloWorld {
    }
    
    • 1
    • 2
    // (1) 创建ClassWriter对象
    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
    
    // (2) 调用visitXxx()方法
    cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, "sample/HelloWorld",
            null, "java/lang/Object", null);
    
    // 创建默认的构造方法
    MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null);
    mv.visitCode();
    // 构造方法中调用超类的构造方法
    mv.visitVarInsn(ALOAD, 0);
    mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false);
    mv.visitInsn(RETURN);
    mv.visitMaxs(1, 1);
    mv.visitEnd();
    
    cw.visitEnd();
    
    // (3) 调用toByteArray()方法
    return cw.toByteArray();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    生成含有注解的字段

    // 希望生成HelloWorld接口
    public interface HelloWorld {
        @MyTag(name = "tomcat", age = 10)
        int intValue = 100;
    }
    
    public @interface MyTag {
        String name();
        int age();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    // (1) 创建ClassWriter对象
    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
    
    // (2) 调用visitXxx()方法
    cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, "sample/HelloWorld", null, "java/lang/Object", null);
    
    {
        FieldVisitor fv1 = cw.visitField(ACC_PUBLIC | ACC_FINAL | ACC_STATIC, "intValue", "I", null, 100);
    
        {
            AnnotationVisitor anno = fv1.visitAnnotation("Lsample/MyTag;", false);
            anno.visit("name", "tomcat");
            anno.visit("age", 10);
            anno.visitEnd();
        }
    
        fv1.visitEnd();
    }
    
    cw.visitEnd();
    
    // (3) 调用toByteArray()方法
    return cw.toByteArray();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    MethodVisitor是另一个重要的接口,其提供了众多的方法用于设置方法体中指令
    其定义方法的调用顺序如下

    (visitParameter)*
    [visitAnnotationDefault]
    (visitAnnotation | visitAnnotableParameterCount | visitParameterAnnotation | visitTypeAnnotation | visitAttribute)*
    [
        visitCode
        (
            visitFrame |
            visitXxxInsn |
            visitLabel |
            visitInsnAnnotation |
            visitTryCatchBlock |
            visitTryCatchAnnotation |
            visitLocalVariable |
            visitLocalVariableAnnotation |
            visitLineNumber
        )*
        visitMaxs
    ]
    visitEnd
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    我们可以把这些visitXxx()方法分成三组:

    • 第一组,在visitCode()方法之前的方法。这一组的方法,主要负责parameter、annotation和attributes等内容,这些内容并不是方法当中“必不可少”的一部分;在当前课程当中,我们暂时不去考虑这些内容,可以忽略这一组方法。
    • 第二组,在visitCode()方法和visitMaxs()方法之间的方法。这一组的方法,主要负责当前方法的“方法体”内的opcode内容。其中,visitCode()方法,标志着方法体的开始,而visitMaxs()方法,标志着方法体的结束。
    • 第三组,是visitEnd()方法。这个visitEnd()方法,是最后一个进行调用的方法。

    对这些visitXxx()方法进行精简之后,内容如下:

    [
        visitCode
        (
            visitFrame |
            visitXxxInsn |
            visitLabel |
            visitTryCatchBlock
        )*
        visitMaxs
    ]
    visitEnd
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这些方法的调用顺序,可以记忆如下:

    • 第一步,调用visitCode()方法,调用一次。
    • 第二步,调用visitXxxInsn()方法,可以调用多次。对这些方法的调用,就是在构建方法的“方法体”。
    • 第三步,调用visitMaxs()方法,调用一次。
    • 第四步,调用visitEnd()方法,调用一次。

    遇到的坑,修改类成员变量

    在这里插入图片描述

  • 相关阅读:
    LeetCode-2485-找出中枢整数
    数组与链表
    【ASM】字节码操作 转换已有的类 记录方法运行时间
    CentOS7.8安装配置OpenLDAP
    嵌入式开发环境之uboot
    MIT 6.858 计算机系统安全讲义 2014 秋季(二)
    网络安全深入学习第八课——反向代理(工具:frp)
    VS Code 自动选择Python3 venv
    【云原生】K8S二进制搭建一
    NanoPC-T4 RK3399:uboot cmd与boot加载
  • 原文地址:https://blog.csdn.net/u014679440/article/details/127725930