手记

Java逆向基础之动态生成类

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

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

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

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

运行结果

begin!name=gsls200808age=25sex=1end!

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


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

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!");  }}

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

0人推荐
随时随地看视频
慕课网APP