Java安全之Javassist动态编程

Javassist 介绍

​ Javaassist是处理Java字节码的类库,Java字节码以二进制的形式存储在class文件中,每个class文件中包含一个java类或者接口。其主要的优点,在于简单,而且快速。直接使用java编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。而个人感觉在安全中最重要的就是在使用Javassist时我们可以像写Java代码一样直接插入Java代码片段,让我们不再需要关注Java底层的字节码的和栈操作,仅需要学会如何使用Javassist的API即可实现字节码编辑,类似于可以达到任意代码执行的效果。

Javassist使用

ClassPool

​ 一个基于HashMap实现的CtClass对象容器,其中键是类名称,值是表示该类的CtClass对象。默认的ClassPool使用与底层JVM相同的类路径,因此在某些情况下,可能需要向ClassPool添加类路径或类字节。

常用方法

ClassPool getDefault() 返回默认的类池
ClassPath insertClassPath(String pathname) 在搜索路径的开头插入目录或jar(或zip)文件。
ClassPath insertClassPath(ClassPath cp) ClassPath在搜索路径的开头插入一个对象。
java.lang.ClassLoader getClassLoader() 获取类加载器
CtClass get(java.lang.String classname) 从源中读取类文件,并返回对CtClass 表示该类文件的对象的引用。
ClassPath appendClassPath(ClassPath cp) 将ClassPath对象附加到搜索路径的末尾。
CtClass makeClass(java.lang.String classname) 创建一个新的public类

代码实例

LAmtiD.png

运行完成后就会生成一个新的类,而且会生成一个新的目录。

CtClass

​ 表示一个类,一个CtClass(编译时类)对象可以处理一个class文件,这些CtClass对象可以从ClassPool获得,从上面的测试也能大概了解一些关于CtClass的相关知识。

Void setSuperclass(CtClass clazz) 更改超类,除非此对象表示接口
java.lang.Class<?> toClass(java.lang.invoke.MethodHandles.Lookup lookup) 将此类转换为java.lang.Class对象。
byte[] toBytecode() 将该类转换为类文件
void writeFile() 将由此CtClass 对象表示的类文件写入当前目录。
void writeFile(java.lang.String directoryName) 将由此CtClass 对象表示的类文件写入本地磁盘
CtConstructor makeClassInitializer() 制作一个空的类初始化程序(静态构造函数)

CtMethod

表示类中的方法。超类为CtBehavior,很多有用的方法都在CtBehavior

Void insertBefore (java.lang.String src) 在正文的开头插入字节码.
Void insertAfter (java.lang.String src) 在正文的末尾插入字节码。
void setBody (CtMethod src, ClassMap map) 从另一个方法复制方法体。

CtConstructor

CtConstructor的实例表示一个构造函数。它可能代表一个静态构造函数。

void setBody(java.lang.String src) 设置构造函数主体。
void setBody(CtConstructor src, ClassMap map) 从另一个构造函数复制一个构造函数主体。
void toMethod(java.lang.String name, CtClass declaring) 复制此构造函数并将其转换为方法

CtField

CtFields :表示类中的字段

动态生成类

步骤如下

1.获取默认类池

2.新建一个类

3.构造方法,添加属性,添加接口

4.反射调用.

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import javassist.*;

import java.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;

public class JavassitTest {
public static void main (String[] args) throws CannotCompileException, IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {


//获取默认类池
ClassPool cp = ClassPool.getDefault();
//新建一个类
CtClass CC = cp.makeClass("com.java.calcDemo");
CtMethod Cm = CtNewMethod.make("public void calcDemo(){java.lang.Runtime.getRuntime().exec(\"open -a Calculator\");}",CC);
// CC.writeFile("./");
CC.addMethod(Cm);

byte[] bytes = CC.toBytecode();
File classPath = new File(new File(System.getProperty("user.dir"), "/com/java/"), "ClassDemo.class");
FileOutputStream fos = new FileOutputStream(classPath);
fos.write(bytes);
fos.close();


ClassLoader loder = new Loader(cp);
Class<?> clazz = loder.loadClass("com.java.calcDemo");
clazz.getDeclaredMethod("calcDemo").invoke(clazz.newInstance());








// byte[] b1 = new byte[]{-54, -2, -70, -66, 0, 0, 0, 52, 0, 25, 1, 0, 17, 99, 111, 109, 47, 106, 97, 118, 97, 47, 99, 97, 108, 99, 68, 101, 109, 111, 7, 0, 1, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 7, 0, 3, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, 105, 108, 101, 1, 0, 13, 99, 97, 108, 99, 68, 101, 109, 111, 46, 106, 97, 118, 97, 1, 0, 8, 99, 97, 108, 99, 68, 101, 109, 111, 1, 0, 3, 40, 41, 86, 1, 0, 17, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 82, 117, 110, 116, 105, 109, 101, 7, 0, 9, 1, 0, 10, 103, 101, 116, 82, 117, 110, 116, 105, 109, 101, 1, 0, 21, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 82, 117, 110, 116, 105, 109, 101, 59, 12, 0, 11, 0, 12, 10, 0, 10, 0, 13, 1, 0, 18, 111, 112, 101, 110, 32, 45, 97, 32, 67, 97, 108, 99, 117, 108, 97, 116, 111, 114, 8, 0, 15, 1, 0, 4, 101, 120, 101, 99, 1, 0, 39, 40, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 80, 114, 111, 99, 101, 115, 115, 59, 12, 0, 17, 0, 18, 10, 0, 10, 0, 19, 1, 0, 4, 67, 111, 100, 101, 1, 0, 6, 60, 105, 110, 105, 116, 62, 12, 0, 22, 0, 8, 10, 0, 4, 0, 23, 0, 33, 0, 2, 0, 4, 0, 0, 0, 0, 0, 2, 0, 1, 0, 7, 0, 8, 0, 1, 0, 21, 0, 0, 0, 22, 0, 2, 0, 1, 0, 0, 0, 10, -72, 0, 14, 18, 16, -74, 0, 20, 87, -79, 0, 0, 0, 0, 0, 1, 0, 22, 0, 8, 0, 1, 0, 21, 0, 0, 0, 17, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 24, -79, 0, 0, 0, 0, 0, 1, 0, 5, 0, 0, 0, 2, 0, 6};
// String b2 = new String(b1);
// System.out.println("args = " + Arrays.deepToString(new byte[][]{bytes}));
// System.out.println("args = " + Arrays.deepToString(new String[]{b2}));

}
}

LAmsdf.png

动态获取类方法

获取默认类池

1
ClassPool cp = ClassPool.getDefault();

获取目标类

1
CtClass CC = cp.get("getDemo.Demo01");

获取类的方法

1
CtMethod CM = CC.getDeclaredMethod("hello");

插入任意代码

1
2
3
 byte[] b1 = new byte[]{123, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101, 46, 103, 101, 116, 82, 117, 110, 116, 105, 109, 101, 40, 41, 46, 101, 120, 101, 99, 40, 34, 111, 112, 101, 110, 32, 45, 97, 32, 67, 97, 108, 99, 117, 108, 97, 116, 111, 114, 34, 41, 59, 125};
String b2 = new String(b1);
CM.insertAfter(b2);

转换为class对象

1
Class c = CC.toClass();

反射调用对象

1
Demo01 demo02 = (Demo01)c.newInstance();

执行方法

1
demo02.hello();
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
30
31
32
33
34
package getDemo;

import java.io.*;
import java.*;
import java.util.Arrays;

import javassist.*;

public class getMethodDemo {
public static void main (String[] args) throws NotFoundException, CannotCompileException, IllegalAccessException, InstantiationException {
getMethodDemo a = new getMethodDemo();
ClassPool cp = ClassPool.getDefault();
CtClass CC = cp.get("getDemo.Demo01");
CtMethod CM = CC.getDeclaredMethod("hello");
//插入任意代码
//{java.lang.Runtime.getRuntime().exec(\"open -a Calculator\");}"
byte[] b1 = new byte[]{123, 106, 97, 118, 97, 46, 108, 97, 110, 103, 46, 82, 117, 110, 116, 105, 109, 101, 46, 103, 101, 116, 82, 117, 110, 116, 105, 109, 101, 40, 41, 46, 101, 120, 101, 99, 40, 34, 111, 112, 101, 110, 32, 45, 97, 32, 67, 97, 108, 99, 117, 108, 97, 116, 111, 114, 34, 41, 59, 125};
String b2 = new String(b1);
CM.insertAfter(b2);

// System.out.println("args = " + Arrays.deepToString(new String[]{b2}));

Class c = CC.toClass();
Demo01 demo02 = (Demo01)c.newInstance();
demo02.hello();

// System.out.println("args = " + Arrays.deepToString(new CtClass[]{CC}));




}

}

LAmToT.png