F4DE F4DE
首页
技术
笔记
随笔
关于
友链
归档

F4DE

Web Security
首页
技术
笔记
随笔
关于
友链
归档
  • Java相关

    • Stream
    • javassist
      • start
      • API
      • usage
        • 读取类、方法、成员变量信息
        • 修改类的方法
        • 动态生成类
  • 笔记
  • Java相关
F4DE
2021-03-10

javassist

关于 Javassist 的学习笔记。

# start

javassist 是一个分析、编辑、创建Java字节码的类库,与 ASM 相比提供了更加方便的API,无需再去关注底层的栈操作和字节码。

依赖导入:

    <dependencies>
        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.27.0-GA</version>
        </dependency>
    </dependencies>

# API

类 描述
ClassPool ClassPool是一个存储CtClass的容器,如果调用get方法会搜索并创建一个表示该类的CtClass对象
CtClass CtClass表示的是从ClassPool获取的类对象,可对该类就行读写编辑等操作
CtMethod 可读写的类方法对象
CtConstructor 可读写的类构造方法对象
CtField 可读写的类成员变量对象

javassist 提供的API和反射非常类似,基本可以对应反射中的Class、Method、Constructor、Field。

此外 javassist 还提供了一些内置的标识符来表示一些特定的含义:

表达式 描述
$0, $1, $2, ... this和方法参数
$args Object[]类型的参数数组
$$ 所有的参数,如m($$)等价于m($1,$2,...)
$cflow(...) cflow变量
$r 返回类型,用于类型转换
$w 包装类型,用于类型转换
$_ 方法返回值
$sig 方法签名,返回java.lang.Class[]数组类型
$type 返回值类型,java.lang.Class类型
$class 当前类,java.lang.Class类型

# usage

相比于基于访问者模式的 ASM 框架来说,使用 javassist 来操作字节码是非常简单的。

# 读取类、方法、成员变量信息

通过 ClassPool 来获取 CtClass对象之后,基本就和反射一样来读取类的信息。

package src;

public class TestClass {
    private String name;

    public TestClass() {
    }

    public TestClass(String name) {
        this.name = name;
    }

    public String getId() {
        return name;
    }

    public void setId(String name) {
        this.name = name;
    }

    public void hello() {
        System.out.println("Hello " + this.name);
    }
}
package src;


import javassist.*;

import java.util.Arrays;

public class Main {
    public static void main(String[] args) throws Exception {
        ClassPool classPool = ClassPool.getDefault();
        CtClass ctClass = classPool.getCtClass(TestClass.class.getName());
        // 获取成员变量
        CtField[] fields = ctClass.getDeclaredFields();
        // 获取方法
        CtMethod[] methods = ctClass.getDeclaredMethods();
        // 获取构造方法
        CtConstructor[] constructors = ctClass.getDeclaredConstructors();
        // 输出信息
        System.out.println("------fields------");
        Arrays.stream(fields).forEach(System.out::println);
        System.out.println("------methods------");
        Arrays.stream(methods).forEach(System.out::println);
        System.out.println("------constructors------");
        Arrays.stream(constructors).forEach(System.out::println);
    }
}
// output

------fields------
src.TestClass.name:Ljava/lang/String;
------methods------
javassist.CtMethod@fb809fd2[public getId ()Ljava/lang/String;]
javassist.CtMethod@db3679d4[public setId (Ljava/lang/String;)V]
javassist.CtMethod@30063153[public hello ()V]
------constructors------
javassist.CtConstructor@46d56d67[public TestClass ()V]
javassist.CtConstructor@d8355a8[public TestClass (Ljava/lang/String;)V]

# 修改类的方法

通过调用 CtMethods 的一些方法就可以进行方法逻辑的修改:

  • setModifiers:修改方法的修饰符
  • insertBefore:在方法执行前插入代码
  • insertAfter:方法执行后插入代码
  • setBody:修改整段方法的代码
  • ······
package src;


import javassist.*;

import java.io.File;
import java.io.FileOutputStream;

public class Main {
    public static void main(String[] args) throws Exception {
        ClassPool classPool = ClassPool.getDefault();
        CtClass ctClass = classPool.getCtClass(TestClass.class.getName());
        // 获取 hello 方法
        CtMethod hello = ctClass.getDeclaredMethod("hello", null);
        // 修改方法的修饰符
        hello.setModifiers(Modifier.PRIVATE);
        // 实现简单AOP
        hello.insertBefore("System.out.println(\"func start\");");
        hello.insertAfter("System.out.println(\"func over\");");
        // 写入class文件
        byte[] bytecode = ctClass.toBytecode();
        File file = new File("target/classes/src/TestClass.class");
        FileOutputStream fos = new FileOutputStream(file);
        fos.write(bytecode);
        fos.flush();
        fos.close();
    }
}

修改后的字节码:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package src;

public class TestClass {
    private String name;

    public TestClass() {
    }

    public TestClass(String name) {
        this.name = name;
    }

    public String getId() {
        return this.name;
    }

    public void setId(String name) {
        this.name = name;
    }

    private void hello() {
        System.out.println("func start");
        System.out.println("Hello " + this.name);
        Object var2 = null;
        System.out.println("func over");
    }
}

修改成员变量信息方法上大体差不多。

# 动态生成类

使用CtClass的makeClass方法就可以动态生成一个类。

package src;


import javassist.*;

import java.io.File;
import java.io.FileOutputStream;

public class Main {
    public static void main(String[] args) {
        ClassPool classPool = ClassPool.getDefault();
        CtClass ctClass = classPool.makeClass("src.ClassMade");
        try {
            // 加入静态代码块
            ctClass.makeClassInitializer().insertBefore("java.lang.System.out.println(\"static code block\");");

            // 创建成员变量
            CtField id = CtField.make("private static int id = 3;", ctClass);
            ctClass.addField(id);

            // 创建方法
            CtMethod main = CtMethod.make(
                    "public static void main(String[] args) {java.lang.System.out.println(id);}",
                    ctClass
            );
            ctClass.addMethod(main);

            // 写入class文件
            byte[] bytecode = ctClass.toBytecode();
            File file = new File("target/classes/src/ClassMade.class");
            if (!(file.exists())) {
                file.createNewFile();
            }
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(bytecode);
            fos.flush();
            fos.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

如果要使用这个类的话,需要向 JVM 中动态注册,这个就是类加载中的内容了。

Stream

← Stream

最近更新
01
在Shiro中使用无CommonsCollections依赖的CommonsBeanUtils利用链
04-19
02
CommonsBeanUtils
04-19
03
基于Tomcat全局存储进行回显
04-16
更多文章>
Theme by Vdoing | Copyright © 2020-2021
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×