javassist 使用笔记

javassist

Javassist 是一个开源的分析、编辑和创建Java字节码的类库。其主要的优点,在于简单,而且快速。直接使用 java 编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。

  • ClassPool:一个基于 Hashtable 实现的 CtClass 对象容器,其中键是类名称,值是表示该类的 CtClass 对象。
  • CtClass:CtClass 表示类,一个 CtClass (编译时类)对象可以处理一个 class 文件,这些 CtClass 对象可以从 ClassPool 获得。
  • CtMethods:表示类中的方法。
  • CtFields :表示类中的字段。

ClassPool

获取 classpool 对象

// 获取 ClassPool 对象,使用系统默认类路径
ClassPool pool = new ClassPool(true);
// 效果与 new ClassPool(true) 一致
ClassPool pool1 = ClassPool.getDefault();

获取类

// 通过类名获取 CtClass,未找到会抛出异常
CtClass ctClass = pool.get("org.test.demo.DemoService");
// 通过类名获取 CtClass,未找到返回 null,不会抛出异常
CtClass ctClass1 = pool.getOrNull("org.test.demo.DemoService");

创建类

// 复制一个类,创建一个新类
CtClass ctClass2 = pool.getAndRename("org.test.demo.DemoService", "org.test.demo.DemoCopyService");
// 通过类名,创建一个新类
CtClass ctClass3 = pool.makeClass("org.test.demo.NewDemoService");
// 通过文件流,创建一个新类,注意文件必须是编译后的 class 文件,不是源代码文件。
CtClass ctClass4 = pool.makeClass(new FileInputStream(new File("./customize/DemoBeforeHandler.class")));

添加类搜索路径

// 将类搜索路径插入到搜索路径之前
pool.insertClassPath(new ClassClassPath(this.getClass()));
// 将类搜索路径添加到搜索路径之后
pool.appendClassPath(new ClassClassPath(this.getClass()));
// 将一个目录作为类搜索路径
pool.insertClassPath("/usr/local/javalib");

CtClass

public static void main(String[] args) throws Exception {

    ClassPool pool = ClassPool.getDefault();
    CtClass ctClass = pool.get("javassist.test02.Person");
    //类名
    String name = ctClass.getName();
    //包名
    String packageName = ctClass.getPackageName();
    //父类
    CtClass superclass = ctClass.getSuperclass();
    //接口
    CtClass[] interfaces = ctClass.getInterfaces();

    System.out.println(name);
    System.out.println(packageName);
    System.out.println(superclass.getName());
    System.out.println(interfaces[0].getName());
}

CtMethod

// 在方法体前插入代码块
ctMethod.insertBefore("");
// 在方法体后插入代码块
ctMethod.insertAfter("");
// 在某行 字节码 后插入代码块
ctMethod.insertAt(10, "");
// 添加参数
ctMethod.addParameter(CtClass);
// 设置方法名
ctMethod.setName("newName");
// 设置方法体
ctMethod.setBody("System.out.println(123);");

创建新类和调用

package javassist.test;

import javassist.*;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

/**
 * Created by ssr on 2020/9/15.
 */
public class JavassistTest01 {
    public static void main(String[] args) throws Exception{

        // 获取 ClassPool 对象,使用系统默认类路径
        ClassPool classPool = ClassPool.getDefault();
        // 创建一个空类
        CtClass ctClass = classPool.makeClass("Test");

        // 新增 String 类型字段 name
        CtField name = new CtField(classPool.get(String.class.getName()), "name", ctClass);
        // 新增 int 类型字段 age
        CtField age = new CtField(classPool.get(int.class.getName()), "age", ctClass);

        // 设置修饰符 private
        name.setModifiers(Modifier.PRIVATE);
        age.setModifiers(Modifier.PRIVATE);

        //添加到类里
        ctClass.addField(name);
        ctClass.addField(age);

        // 无参构造方法
        CtConstructor ctConstructor = new CtConstructor(new CtClass[]{},ctClass);
        // 有参构造方法
        CtConstructor ctConstructor1 = new CtConstructor(new CtClass[]{classPool.get(String.class.getName()), CtClass.intType}, ctClass);

        // 方法体
        ctConstructor.setBody("{name="test";age=12;}");
        // 方法体  $0 代表this  $1 $2 方法参数 name age
        ctConstructor1.setBody("{$0.name = $1;$0.age = $2;}");

        ctClass.addConstructor(ctConstructor);
        ctClass.addConstructor(ctConstructor1);

        //创建 getter  setter 方法
        CtMethod setName = CtNewMethod.setter("setName", name);
        CtMethod getName = CtNewMethod.getter("getName", name);

        CtMethod getAge = CtNewMethod.getter("getAge", age);
        CtMethod setAge = CtNewMethod.setter("setAge", age);

        ctClass.addMethod(setName);
        ctClass.addMethod(getName);

        ctClass.addMethod(getAge);
        ctClass.addMethod(setAge);

        //新增方法
        CtMethod printName = new CtMethod(new CtClass(String.class.getName()) {
            @Override
            public String toString() {
                return super.toString();
            }
        }, "printInfo", new CtClass[]{}, ctClass);

        // 设置方法修饰符 public
        printName.setModifiers(Modifier.PUBLIC);
        // 设置方法体
        printName.setBody("{return "my name is "+name+"," + "age is "+age;}");
        ctClass.addMethod(printName);

        // 写入文件
        ctClass.writeFile("D:\漏洞分析\Commons Collections 3.1\src\main\java\javassist\test");

        //将创建的ctClass加载至当前线程的上下文类加载器中
        Class clz = ctClass.toClass();

        //反射调用
        Constructor declaredConstructor = clz.getDeclaredConstructor(String.class, int.class);
        Object obj = declaredConstructor.newInstance("liangzi", 100);

        Method printInfo = clz.getMethod("printInfo");
        System.out.println(printInfo.invoke(obj));


    }
}

修改类

  • 原始类
package javassist.test03;

public class Test {
    private void info(String name){
        System.out.println(name);
    }

}
  • 修改
package javassist.test03;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;

import java.lang.reflect.Method;

public class JavassistTest01 {

    public static void main(String[] args) throws Exception{

        ClassPool pool = ClassPool.getDefault();
        // 通过类名获取 CtClass,未找到会抛出异常
        CtClass ctClass = pool.get("javassist.test03.Test");
        // 获取类中的info方法
        CtMethod info = ctClass.getDeclaredMethod("info");
        // 修改info方法修饰符为 public
        info.setModifiers(Modifier.PUBLIC);
        // 方法开头添加语句
        info.insertBefore("{System.out.println("000");}");
        // 方法结尾添加语句
        info.insertAfter("{System.out.println("111");}");

        Class cls = ctClass.toClass();
        Object o = cls.newInstance();
        Method info1 = cls.getMethod("info", String.class);
        info1.invoke(o,"bbb");
    }
}

原文地址:https://www.cnblogs.com/depycode/p/13676295.html