为什么有这个东西,一方面时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 pool = ClassPool.getDefault();
CtClass ctClass = pool.makeClass("com.vvvtimes.bean.Employee");
CtField enameField = new CtField(pool.getCtClass("java.lang.String"), "ename", ctClass); enameField.setModifiers(Modifier.PRIVATE); ctClass.addField(enameField);
CtField eageField = new CtField(pool.getCtClass("int"), "eage", ctClass); eageField.setModifiers(Modifier.PRIVATE); ctClass.addField(eageField);
CtField esexField = new CtField(pool.getCtClass("int"), "esex", ctClass); esexField.setModifiers(Modifier.PRIVATE); ctClass.addField(esexField);
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);
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!"); } }
|
在后续文章中我们会讲如何获取内存中的类