多读书多实践,勤思考善领悟

Java逆向基础之十四.动态生成类

本文于1991天之前发表,文中内容可能已经过时。

为什么有这个东西,一方面时AOP框架的需要,另一方面是增加软件逆向的难度

动态生成类的技术目前大体上分为两类,一类是通过操作字节码框架如cglib/Javassist去实现,另一类就是JNI方式,调用dll/so库,内存中动态还原。这两种方式都能实现隐藏类

1. 看一个Javassist动态生成类的例子

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package com.vvvtimes;

import java.lang.reflect.Modifier;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;

public class DynamicGenerateClass {

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

// ClassPool:CtClass对象的容器
ClassPool pool = ClassPool.getDefault();

// 通过ClassPool生成一个public新类Employee.java
CtClass ctClass = pool.makeClass("com.vvvtimes.bean.Employee");

// 添加字段
// 首先添加字段private String ename
CtField enameField = new CtField(pool.getCtClass("java.lang.String"), "ename", ctClass);
enameField.setModifiers(Modifier.PRIVATE);
ctClass.addField(enameField);

// 其次添加字段privtae int eage
CtField eageField = new CtField(pool.getCtClass("int"), "eage", ctClass);
eageField.setModifiers(Modifier.PRIVATE);
ctClass.addField(eageField);

// 其次添加字段privtae int eage
CtField esexField = new CtField(pool.getCtClass("int"), "esex", ctClass);
esexField.setModifiers(Modifier.PRIVATE);
ctClass.addField(esexField);

// 为字段ename和eno添加getter和setter方法
ctClass.addMethod(CtNewMethod.getter("getEname", enameField));
ctClass.addMethod(CtNewMethod.setter("setEname", enameField));
ctClass.addMethod(CtNewMethod.getter("getEage", eageField));
ctClass.addMethod(CtNewMethod.setter("setEage", eageField));
ctClass.addMethod(CtNewMethod.getter("getSex", esexField));
ctClass.addMethod(CtNewMethod.setter("setSex", esexField));

// 添加构造函数
CtConstructor ctConstructor = new CtConstructor(new CtClass[] {}, ctClass);
// 为构造函数设置函数体
StringBuffer buffer = new StringBuffer();
buffer.append("{\n").append("ename=\"gsls200808\";\n").append("eage=25;\n").append("esex=1;\n}");
ctConstructor.setBody(buffer.toString());
// 把构造函数添加到新的类中
ctClass.addConstructor(ctConstructor);

// 添加自定义方法
CtMethod ctMethod = new CtMethod(CtClass.voidType, "printInfo", new CtClass[] {}, ctClass);
// 为自定义方法设置修饰符
ctMethod.setModifiers(Modifier.PUBLIC);
// 为自定义方法设置函数体
StringBuffer buffer2 = new StringBuffer();
buffer2.append("{\n").append("System.out.println(\"begin!\");\n")
.append("System.out.println(\"name=\"+ename);\n").append("System.out.println(\"age=\"+eage);\n")
.append("System.out.println(\"sex=\"+esex);\n").append("System.out.println(\"end!\");\n").append("}");
ctMethod.setBody(buffer2.toString());
ctClass.addMethod(ctMethod);

// 为了验证效果,下面使用反射执行方法printInfo
Class<?> clazz = ctClass.toClass();
Object obj = clazz.newInstance();
obj.getClass().getMethod("printInfo", new Class[] {}).invoke(obj, new Object[] {});

}
}

需要引用的第三方jar:javassist-3.20.0-GA.jar

运行结果

1
2
3
4
5
begin!
name=gsls200808
age=25
sex=1
end!

代码的意思相当于在内存中创建了一个名为com.vvvtimes.bean.Employee的类,并通过反射方式调用了printInfo方法进行输出。

这个类的内容大致如下,只不过我们在上面是动态生成的

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
47
48
49
package com.vvvtimes.bean;

import java.io.PrintStream;

public class Employee
{
private String ename = "gsls200808";
private int eage = 25;
private int esex = 1;

public String getEname()
{
return this.ename;
}

public void setEname(String paramString)
{
this.ename = paramString;
}

public int getEage()
{
return this.eage;
}

public void setEage(int paramInt)
{
this.eage = paramInt;
}

public int getSex()
{
return this.esex;
}

public void setSex(int paramInt)
{
this.esex = paramInt;
}

public void printInfo()
{
System.out.println("begin!");
System.out.println("name=" + this.ename);
System.out.println("age=" + this.eage);
System.out.println("sex=" + this.esex);
System.out.println("end!");
}
}

在后续文章中我们会讲如何获取内存中的类