Java反序列化(基础)
0x01、基本概念
1. 序列化和反序列化:
Serialization(序列化)是指把Java对象保存为二进制字节码的过程;
Deserialization(反序列化)是把二进制码重新转换成Java对象的过程。
2. 什么情况下需要序列化
a)当你想把内存中的对象保存到数据库中或者一个文件中时;
b)当你想用套接字在网络上传送对象时;
c)当你想通过RMI传输对象的时候;
总之,序列化的作用就是传递和存储!
3. 如何实现序列化
将需要序列化的类实现Serializable接口就可以了,Serializable接口中没有任何方法,可以理解为一个标记,即表明这个类可以被序列化。序列化和反序列化都可以理解为“读”和“写”操作,通过如下两个方法可以将对象进行“序列化”和“反序列化”的操作。
4. 需要的接口和方法:
java.io.Serialize 接口 (可以理解为一个标记,通过此标记可以表明这个类可以被序列化)
writeObjec 方法(对象进行序列化的方法)
readObjec t方法(进行反序列的方法)
serialVersionUID (表明了序列化版本的id,确定了序列化版本的唯一性)
00x2、java.io.Serializable 接口
1. java.io.Serializable包 是一个没有任何方法的接口,可以理解为序列化的标识,一个类若需要序列化,则必须被标识。
2. 按住 ctrl + Serializable可以查看这个接口,发现这个接口没有任何方法
0x03、writeObject() 、readObject()方法
1. writeObejct(): 对象序列化,要调用ObjectOutputStream输出流的wirteObject()方法;通过调用类的构造方法,传递字节输出流,包装一 个文件;
序列化流,将对象中的数据写到字节输出流中,字节输出流写到文件中。
2. readObject(): 对象反序列化,需要调用ObjectInputStream输入流的readObject()方法。
反序列化流,通过Object 的一个对象接收来自反序列化流的数据。
0x04、serialVersionUID
序列化版本ID,具有唯一性,需要序列化的对象的成员属性,表示该对象的序列化版本id,反序列化的接收对象的serialVersionUID必须保持和序列化对象的serialVersionUID一致,这样才能正常反序列化。
0x05、下面的代码表示的是:需要被序列化的 Student 类,和 writeObejct()方法、readObject() 方法
package com.java.serialize; import java.io.*; //可以直接这么写: public class Student implements java.io.Serializable {} public class Student implements Serializable { private static final long serialVersionUID = -2550501340827203332L; public int id; public String name; public Student(int id,String name) { this.id=id; this.name=name; } public String toString() { return "id="+id+" "+"name="+name+" "; } } class test { // 通过writeObject() 方法序列化对象 public static void writeObj() throws IOException { // 对象序列化,输出流ObjectOutputStream,通过写对象流,传递字节输出流,包装到 test.txt 文件中 FileOutputStream file = new FileOutputStream("D:\\test.txt"); ObjectOutputStream oos = new ObjectOutputStream(file); // 调用Student类的构造方法,通过writeObject()方法序列化流,将对象中的数据写到字节输出流中,字节输出流写到文件中 Student s = new Student(1,"john"); oos.writeObject(s); //打印输出语句,关闭流 System.out.println("Serialize success!"); oos.close(); } // 通过readObject() 方法序反列化对象 public static void readObject() throws IOException, ClassNotFoundException { //对象反序列化,输出流ObjectInputStream, 传递字节输入流,包装一个文件 FileInputStream file = new FileInputStream("D:\\test.txt"); ObjectInputStream ois = new ObjectInputStream(file); // readObject()方法反序列化流,从文件中读取对象中的数据,通过Object类的一个对象,接收来自字节输入流中的传递的字节数组 Object o = ois.readObject(); // 打印序列化后接受的数据,关闭流 System.out.println(o); ois.close(); } public static void main(String[] args) throws IOException, ClassNotFoundException { writeObj(); //readObject(); } }
在上述代码块中的main方法里调用 writeObject() 方法(即对象序列化)后的结果:
在D盘可以看到文件test.txt,打开后是一对乱码,原因是,java序列化后的保存的形式是二进制的,又通过字节数组打印出来。
在上述代码块中的main方法里调用 readObject() 方法(即对象反序列化)后的结果:
0x06、反序列化漏洞
话题太高端,简单的来说,在Server服务端的代码,程序员在写代码时没有进行严格的过滤,写类时调用了java.io.Serializable接口,此时,客户端Client的攻击者可以通过序列化,将自己的恶意代码上通过传参到类序列化出来,Server接收到来自Client后的类,进行了反序列化,此时,攻击者上传的恶意代码被反序列后开始执行。一般这个漏洞出现在框架中,比如我们最常用的,写jsp一句话木马的命令: 如果把这一句作为参数,直接传递给 Runtime.getRuntime().exec(String cmd),就执行了。
Over!