10-java安全基础——javassist字节码编程
Posted songly_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了10-java安全基础——javassist字节码编程相关的知识,希望对你有一定的参考价值。
javassist是一个开源的分析、编辑和创建Java字节码的类库,通过javassist提供的API可以在java程序运行时编辑一个类的字节码信息,改变该类的结构信息。除了Javassist,常见的字节码编程工具有ASM和byte-buddy,这两个工具相对来说更加偏向于底层,需要了解关于jvm的指令。
使用javassist可以不需要了解jvm指令,只需使用javassist类库提供的API接口就可以实现字节码编程。
javassist字节码编程常用的类:
ClassPool:ClassPool 类可以控制的类的字节码,例如创建一个类或加载一个类,与 JVM 类装载器类似
CtClass: CtClass提供了类的操作,如在类中动态添加新字段、方法和构造函数、以及改变类、父类和接口的方法
CtField:类的属性,通过它可以给类创建新的属性,还可以修改已有的属性的类型,访问修饰符等
CtMethod:表示类中的方法,通过它可以给类创建新的方法,还可以修改返回类型,访问修饰符等, 甚至还可以修改方法体内容代码
CtConstructor:用于访问类的构造,与CtMethod类的作用类似
在maven项目中引入javassist类库:
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.25.0-GA</version>
</dependency>
来看一段代码:
public static void main(String[] args) throws NotFoundException {
//创建classPool类对象池
ClassPool classPool = ClassPool.getDefault();
//通过classPool类对象池创建一个Student类
CtClass ctClass1 = classPool.makeClass("com.test.Student");
//通过classPool类对象池加载Student类
CtClass ctClass2 = classPool.getCtClass("com.test.Student");
}
ClassPool对象是代表class文件的CtClass对象的容器,可以控制字节码的修改,CtClass表示一个类的class文件的句柄:
当要创建一个类时,需要通过ClassPool对象的makeClass方法来创建
当要修改一个类时,可以通过getCtClass方法来加载一个已知的类
无论是创建还是修改一个类,都会返回操作该类的句柄CtClass对象,然后可以通过CtClass对象进一步操作该类,例如添加成员方法,成员属性等等。
1. 使用CtMethod类添加main方法
创建一个Student类并添加一个main方法
public class JavassistTest1 {
//1.添加main方法
public static void main(String[] args) throws Exception {
//创建classPool类对象池
ClassPool classPool = ClassPool.getDefault();
//通过classPool类对象池创建一个Student类
CtClass student_ctClass = classPool.makeClass("target.com.test.javassist.Student");
//添加方法:参数1:方法的返回值类型你,参数2:方法名,参数3:方法的参数类型,参数4:方法所属的类
CtMethod mainMethod = new CtMethod(CtClass.voidType , "main" , new CtClass[]{classPool.get(String[].class.getName())},student_ctClass);
//设置main方法的访问修饰符
mainMethod.setModifiers(Modifier.PUBLIC + Modifier.STATIC);
//设置方法体
mainMethod.setBody("{System.out.println(\\"hello world\\");}");
//添加方法
student_ctClass.addMethod(mainMethod);
//输出类的内容
student_ctClass.writeFile();
//实例化Student对象
Class aClass = student_ctClass.toClass();
Object obj = aClass.newInstance();
//反射调用Student对象的main方法
Method main_method = aClass.getDeclaredMethod("main" , String[].class);
main_method.invoke(obj, (Object) new String[1]);
}
}
CtMethod 类是用于添加方法的,例如添加main方法主要包括:方法的属性、类型、名称、参数,方法体。new CtMethod操作主要是创建方法的声明,setModifiers方法用于设置方法的修饰符, setBody方法用于设置方法体,addMethod方法用于把main方法添加到Student类的CtClass对象中。然后调用CtClass对象的writeFile方法在磁盘上生成Student类的class文件。
程序执行结果:
通过javassist创建一个Student类会生成Student类的class文件,通过反射可以调用Student类的class对象的main方法输出hello wrold。
还有一种方法可以直接创建方法,通过CtNewMethod类的静态方法make创建方法,make方法的参数1直接传入方法的完整结构信息,例如下面这段代码:
CtMethod ctMethod1 = CtNewMethod.make("public int print_stu(){ return 100;}", student_ctClass);
student_ctClass.addMethod(ctMethod1);
传入的参数类型是对象类型时需要注意以下几点:
例如传入的是String类型,那么就要使用classPool.get(String[].class.getName()) 这种方式传入
当传入多个参数,且参数类型相同,例如传入多个Integer类型的参数,需要使用new CtClass[] {classPool.get(Integer[].class.getName())} 这种方式传入
当传入多个参数,且参数类型不相同,例如传入char类型的参数和long类型的参数就要使用 new CtClass[] {CtClass.charType , CtClass.longType} 这种方式
如果方法接收的参数是一个对象类型,例如接收的是一个Student对象类型的参数,那么就可以通过classPool对象的getCtClass方法来获取Student类的CtClass对象的方式传入该参数。
classPool.getCtClass("target.com.test.javassist.Student");
2. 添加构造方法
来看一个创建Student类并添加一个构造方法的示例程序
package com.test;
import javassist.*;
/**
* @auther songly_
* @data 2021/7/28 13:47
*/
public class JavassistTest3 {
public static void main(String[] args) throws Exception {
//创建classPool类对象池
ClassPool classPool = ClassPool.getDefault();
//通过classPool类对象池创建一个Student类
CtClass student_ctClass = classPool.makeClass("target.com.test.javassist.Student");
//添加一个int类型的classid变量
CtField ctField = new CtField(CtClass.intType,"classid",student_ctClass);
//设置变量的访问权限
ctField.setModifiers(Modifier.PRIVATE);
//把变量添加到Student类的ctClass对象
student_ctClass.addField(ctField);
//添加一个String类型的name变量
CtField ctField2 = new CtField(classPool.getCtClass("java.lang.String"), "name" , student_ctClass);
ctField2.setModifiers(Modifier.PUBLIC);
student_ctClass.addField(ctField2);
//添加有参构造
CtConstructor ctConstructor1 = new CtConstructor(new CtClass[]{ CtClass.intType, classPool.getCtClass("java.lang.String")},student_ctClass);
ctConstructor1.setModifiers(Modifier.PUBLIC);
ctConstructor1.setBody("{$0.classid = $1;$0.name = $2;}");
student_ctClass.addConstructor(ctConstructor1);
//输出类的内容
student_ctClass.writeFile();
//实例化Student对象,会调用Student对象的构造方法
Class aClass = student_ctClass.toClass();
}
}
程序执行结果:
CtConstructor的参数1为创建的构造方法的参数,如果是创建无参构造的话,那么第一个参数可以设置为null ,如果是有参构造,参数1为CtClass[]数组,表示接收多个参数
public CtConstructor(CtConstructor src, CtClass declaring, ClassMap map) throws CannotCompileException {
this((MethodInfo)null, declaring);
this.copy(src, true, map);
}
3. 添加成员属性
创建一个Student类并添加两个成员属性,如下所示:
package com.test;
import javassist.*;
/**
* @auther songly_
* @data 2021/7/28 13:47
*/
public class JavassistTest3 {
public static void main(String[] args) throws Exception {
//创建classPool类对象池
ClassPool classPool = ClassPool.getDefault();
//通过classPool类对象池创建一个Student类
CtClass student_ctClass = classPool.makeClass("target.com.test.javassist.Student");
//添加一个int类型的classid变量
CtField ctField = new CtField(CtClass.intType,"classid",student_ctClass);
//设置变量的访问权限
ctField.setModifiers(Modifier.PRIVATE);
//把变量添加到Student类的ctClass对象
student_ctClass.addField(ctField);
//添加一个String类型的name变量
CtField ctField2 = new CtField(classPool.getCtClass("java.lang.String"), "name" , student_ctClass);
ctField2.setModifiers(Modifier.PUBLIC);
student_ctClass.addField(ctField2);
//输出类的内容
student_ctClass.writeFile();
//实例化Student对象,会调用Student对象的构造方法
Class aClass = student_ctClass.toClass();
Object obj = aClass.newInstance();
}
}
javassist字节码编程可以通过几行代码创造一个类并生成class文件, 并且还可以通过反射调用该类的方法和成员属性,甚至还可以修改某一个类的方法或成员属性等等,是一门非常强大的技术。
以上是关于10-java安全基础——javassist字节码编程的主要内容,如果未能解决你的问题,请参考以下文章