@Author: Patrilic @Time: 2020-3-18 11:26:55
Java中只需要实现 java.io.Serializable
或者 java.io.Externalizable
接口即可执行序列化操作
构造一个调用类,其中,Exployee类实现了 java.io.Serializable
接口 :
package com.patrilic.testSer; public class Employee implements java.io.Serializable { public String name; public String identify; public void mailCheck() { System.out.println("This is the "+this.identify+" of our company"); } }
序列化操作, 在调用Employee类时,所有的数据都会被序列化
package com.patrilic.testSer; import com.patrilic.testSer.Employee; //import com.sun.xml.internal.bind.v2.runtime.unmarshaller.XsiNilLoader.Array; import java.io.*; public class Step1 { public static void main(String [] args) { Employee e = new Employee(); e.name = "员工甲"; e.identify = "General staff"; try { // 打开一个文件输入流 FileOutputStream fileOut = new FileOutputStream("/tmp/test.db"); // 建立对象输入流 ObjectOutputStream out = new ObjectOutputStream(fileOut); //输出反序列化对象 out.writeObject(e); out.close(); fileOut.close(); System.out.printf("Serialized data is saved in /tmp/test.db"); }catch(IOException i) { i.printStackTrace(); } } }
反序列化操作
package com.patrilic.testSer; import java.io.*; public class Step2 { public static void main(String [] args) { Employee e = null; try { // 打开一个文件输入流 FileInputStream fileIn = new FileInputStream("/tmp/test.db"); // 建立对象输入流 ObjectInputStream in = new ObjectInputStream(fileIn); // 读取对象 e = (Employee) in.readObject(); in.close(); fileIn.close(); }catch(IOException i) { i.printStackTrace(); return; }catch(ClassNotFoundException c) { System.out.println("Employee class not found"); c.printStackTrace(); return; } System.out.println("Deserialized Employee..."); System.out.println("Name: " + e.name); System.out.println("This is the "+e.identify+" of our company"); } }
Java反序列化中,会调用被反序列化的readObject方法,如果readObject方法是恶意的,那么就会引发漏洞
package com.patrilic.testSer; import java.io.*; public class test{ public static void main(String args[]) throws Exception{ UnsafeClass Unsafe = new UnsafeClass(); Unsafe.name = "hacked by ph0rse"; FileOutputStream fos = new FileOutputStream("object"); ObjectOutputStream os = new ObjectOutputStream(fos); //writeObject()方法将Unsafe对象写入object文件 os.writeObject(Unsafe); os.close(); //从文件中反序列化obj对象 FileInputStream fis = new FileInputStream("object"); ObjectInputStream ois = new ObjectInputStream(fis); //恢复对象 UnsafeClass objectFromDisk = (UnsafeClass)ois.readObject(); System.out.println(objectFromDisk.name); ois.close(); } } class UnsafeClass implements Serializable{ public String name; //重写readObject()方法 private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{ //执行默认的readObject()方法 in.defaultReadObject(); //执行命令 System.out.println("my First!"); Runtime.getRuntime().exec("open -a Calculator.app"); } }
这里进行反序列化操作时,使用 ObjectInputStream
读取序列化文件,然后调用了 readObject()
, 当然就会成功的执行命令
并且确实是先执行了readObject类的操作,然后再进行 System.out.println(name)
序列化对象: java.io.ObjectOutputStream
-> writeObject()
反序列化对象: java.io.ObjectInputStream
-> readObject()
也就是说在序列化类时,就会自动去
java.io.Serializable接口是空接口,仅仅用于表示这个类可序列化
package com.patrilic.demo; import java.io.*; import java.util.Arrays; public class DeserializationTest implements Serializable { private String username; private String email; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public static void main(String[] args) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { // 创建DeserializationTest类,并类设置属性值 DeserializationTest t = new DeserializationTest(); t.setUsername("Patrilic"); t.setEmail("admin@patrilic.top"); // 创建Java对象序列化输出流对象 ObjectOutputStream out = new ObjectOutputStream(baos); // 序列化DeserializationTest类 out.writeObject(t); out.flush(); out.close(); // 打印DeserializationTest类序列化以后的字节数组,我们可以将其存储到文件中或者通过Socket发送到远程服务地址 System.out.println("DeserializationTest类序列化后的字节数组:" + Arrays.toString(baos.toByteArray())); // 利用DeserializationTest类生成的二进制数组创建二进制输入流对象用于反序列化操作 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); // 通过反序列化输入流(bais),创建Java对象输入流(ObjectInputStream)对象 ObjectInputStream in = new ObjectInputStream(bais); // 反序列化输入流数据为DeserializationTest对象 DeserializationTest test = (DeserializationTest) in.readObject(); System.out.println("用户名:" + test.getUsername() + ",邮箱:" + test.getEmail()); // 关闭ObjectInputStream输入流 in.close(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
java.io.Externalizable继承Serializable接口,定义了两个方法:
package com.patrilic.demo; import java.io.*; import java.util.Arrays; public class ExternalizableTest implements java.io.Externalizable { private String username; private String email; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeObject(username); out.writeObject(email); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { this.username = (String) in.readObject(); this.email = (String) in.readObject(); } public static void main(String[] args) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { // 创建DeserializationTest类,并类设置属性值 ExternalizableTest t = new ExternalizableTest(); t.setUsername("Patrilic"); t.setEmail("admin@patrilic.top"); // 创建Java对象序列化输出流对象 ObjectOutputStream out = new ObjectOutputStream(baos); // 序列化DeserializationTest类 out.writeObject(t); out.flush(); out.close(); // 打印ExternalizableTest类序列化以后的字节数组,我们可以将其存储到文件中或者通过Socket发送到远程服务地址 System.out.println("ExternalizableTest类序列化后的字节数组:" + Arrays.toString(baos.toByteArray())); // 利用ExternalizableTest类生成的二进制数组创建二进制输入流对象用于反序列化操作 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); // 通过反序列化输入流(bais),创建Java对象输入流(ObjectInputStream)对象 ObjectInputStream in = new ObjectInputStream(bais); // 反序列化输入流数据为DeserializationTest对象 ExternalizableTest test = (ExternalizableTest) in.readObject(); System.out.println("用户名:" + test.getUsername() + ",邮箱:" + test.getEmail()); // 关闭ObjectInputStream输入流 in.close(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
值得注意的是两个方法的重写: