Java 反序列化
反射
大白话说Java反射:入门、使用、原理 - 陈树义 - 博客园
知道类名,用正射:
1
| Apple apple = new Apple();
|
Java 中一切皆对象,类是Class
对象,方法是Method
对象,构造方法是Constructor
对象,字段是Field
对象。
不知道类名,只能把类名先存变量里面,再一句一句跟代码说要对这个类干什么,这是反射:
1
| Class clz = Class.forName("com.zhenai.api.Apple");
|
对于名字叫 xxx 的类,获取 xxx 类的Class
对象(请注意这里不等于 xxx 对象),就是根据 xxx 这个名字,把 xxx 的档案调出来。
1
| Constructor appleConstructor = clz.getConstructor();
|
对于这个对象,获取 xxx 的构造方法/construct/无参函数的对象也调出来,也就是Constructor
对象。就是根据 xxx 的档案,把 xxx 的 DNA 也调出来。
1
| Object appleObj = appleConstructor.newInstance();
|
用Constructor
对象新建一个实例。
根据 xxx 的 DNA,新建一个 xxx。
如果要调用方法:
1
| Method setPriceMethod = clz.getMethod("setPrice", int.class);
|
用Class
对象获取Method
对象,这个Method
对象是一个名字叫setPrice
,参数类型是int
的方法 。
在 xxx 的档案里找一个叫setPrice
的技能档案,把这个技能教给 xxx。
1
| setPriceMethod.invoke(appleObj, 14);
|
用Method
对象调用 invoke 方法,传入对象名字和参数。。
让 xxx 以指定的方式使用技能。
CC1链——全网最菜的分析思路 - FreeBuf网络安全行业门户
反序列化和 readObject
序列化是把对象变成字节流,反序列化是把字节流还原成对象。
定义类 Student.java
1 2 3 4 5 6 7 8 9 10 11 12 13
| import java.io.Serializable;
public class Student implements Serializable { public String name; public int age;
public Student(String name, int age) { this.name = name; this.age = age; } }
|
implements Serializable
必须写上这个接口这个类才可以被序列化。
主程序 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
| import java.io.*;
public class Main { public static void main(String[] args) throws Exception { Student stu = new Student("小明", 20);
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("student.ser")); oos.writeObject(stu); oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("student.ser")); Student stu2 = (Student) ois.readObject(); ois.close();
System.out.println("姓名:" + stu2.name); System.out.println("年龄:" + stu2.age); } }
|
这段代码演示了序列化和反序列化的过程。
反序列化不仅可以把字节流还原成对象,如果类里面有readObject
这个方法,那么反序列化的时候会调用这个方法。如果该方法中有恶意代码,也会执行。所以自定义了 readObject()
方法的类就是我们寻找的入口点。
如果现在有一个程序Demo.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import java.io.*;
public class Demo implements Serializable { private String data;
public Demo(String data) { this.data = data; }
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { System.out.println("readObject 被调用了!"); in.defaultReadObject(); System.out.println("data的值是:" + data); } }
|
可以看到开发者自己写了一个readObject
,那么我们这里可以写一个调用反序列化的主程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class Main { public static void main(String[] args) throws Exception { Demo obj = new Demo("Hello");
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("demo.ser")); oos.writeObject(obj); oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("demo.ser")); Demo newObj = (Demo) ois.readObject(); ois.close(); } }
|
输出:
1 2
| readObject 被调用了! data的值是:Hello
|
readObject 是起点,能执行命令的类是终点,从起点到终点,前一个触发了后一个,所以是一条链。
通过反射来执行 Runtime 命令:
1 2 3 4
| Class clazz = Runtime.class; Method getRuntime = clazz.getDeclaredMethod("getRuntime", null); Runtime runtime = (Runtime)getRuntime.invoke(null, null); runtime.exec("open -a /System/Applications/Calculator.app/Contents/MacOS/Calculator");
|
获取类,获取方法,获取实例,执行命令
CommonsCollections1 链
使用InvokerTransformer 对象调用 transform()方法,会以反射的方式 执行任意方法。
1 2 3 4 5 6
| InvokerTransformer invokerTransformer = new InvokerTransformer( "exec", new Class[]{String.class} , new Object[]{"open -a /System/Applications/Calculator.app/Contents/MacOS/Calculator"}); Runtime runtime = Runtime.getRuntime(); invokerTransformer.transform(runtime);
|
这里看不出反射是因为反射被封装在transform()方法内部。