0x0、基本概念 1、什么是序列化和反序列化
Serialization(序列化)是指把Java对象保存为二进制字节码的过程;反序列化deserialization是把二进制码重新转换成Java对象的过程。
2、什么情况下需要序列化
a)当你想把的内存中的对象保存到一个文件中或者数据库中时候;
b)当你想用套接字在网络上传送对象的时候;
c)当你想通过RMI传输对象的时候;
总之,序列化的用途就是传递和存储。
3、如何实现序列化
将需要序列化的类实现Serializable接口就可以了,Serializable接口中没有任何方法,可以理解为一个标记,即表明这个类可以被序列化。
序列化与反序列化都可以理解为“写”和“读”操作 ,通过如下这两个方法可以将对象实例进行“序列化”与“反序列化”操作。
/** * 写入对象内容 */ private void writeObject(java.io.ObjectOutputStream out) /** * 读取对象内容 */ private void readObject(java.io.ObjectInputStream in)
4、一些注意点
当然,并不是一个实现了序列化接口的类的所有字段及属性,都是可以序列化的:
如果该类有父类,则分两种情况来考虑:
1.如果该父类已经实现了可序列化接口,则其父类的相应字段及属性的处理和该类相同;
2.如果该类的父类没有实现可序列化接口,则该类的父类所有的字段属性将不会序列化,并且反序列化时会调用父类的默认构造函数来初始化父类的属性,而子类却不调用默认构造函数,而是直接从流中恢复属性的值。
如果该类的某个属性标识为static类型的,则该属性不能序列化。
如果该类的某个属性采用transient关键字标识,则该属性不能序列化。
a)当一个父类实现序列化,子类自动实现序列化,不需要显式实现Serializable接口;
b)当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化;
0x1、简单实例 以下代码是一个简单的序列化和反序列化操作的demo。
package 反序列化; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class deserTest implements Serializable { //创建一个简单的可被序列化的类,它的实例化后的对象就是可以被序列化的 private static final long serialVersionUID = 1L; private int n; public deserTest(int n) { this.n=n; } @Override public String toString() { return "deserTest [n=" + n + ", getClass()=" + getClass() + ", hashCode()=" + hashCode() + ", toString()=" + super.toString() + "]"; } public static void main(String[] args) { deserTest x = new deserTest(5); operation.ser(x); operation.deser(); x.toString(); } } class operation{ public static void ser(Object obj) { //序列化操作,写操作 try { ObjectOutputStream ooStream = new ObjectOutputStream(new FileOutputStream("object.obj")); //ObjectOutputStream能把Object输出成Byte流 ooStream.writeObject(obj); ooStream.flush(); ooStream.close(); } catch (FileNotFoundException e) { e.printStackTrace(); }catch (IOException e) { // TODO: handle exception e.printStackTrace(); } } public static void deser() { //反序列化,读取操作 try { ObjectInputStream iiStream = new ObjectInputStream(new FileInputStream("object.obj")); Object xObject = iiStream.readObject(); System.out.println(xObject); iiStream.close(); } catch (FileNotFoundException e) { // TODO: handle exception e.printStackTrace(); }catch (IOException e) { // TODO: handle exception e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
0x2、自定义反序列化的行为–弹个计算器 自定义序列化和反序列化过程,就是重写writeObject和readObject方法。
对以上代码进行改造,加入readObject方法的重写,再重写函数中加入自己的代码逻辑。
package 反序列化; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class deserTest2 implements Serializable { //创建一个简单的可被序列化的类,它的实例化后的对象就是可以被序列化的 private static final long serialVersionUID = 1L; private int n; public deserTest2(int n) { this.n=n; } @Override public String toString() { return "deserTest2 [n=" + n + ", getClass()=" + getClass() + ", hashCode()=" + hashCode() + ", toString()=" + super.toString() + "]"; } private void readObject(java.io.ObjectInputStream in) throws IOException,ClassNotFoundException{ in.defaultReadObject(); Runtime.getRuntime().exec("open /System/Applications/Calculator.app"); System.out.println("test"); } public static void main(String[] args) { deserTest2 x = new deserTest2(5); operation1.ser(x); operation1.deser(); x.toString(); } } class operation1{ public static void ser(Object obj) { //序列化操作,写操作 try { ObjectOutputStream ooStream = new ObjectOutputStream(new FileOutputStream("object.obj")); //ObjectOutputStream能把Object输出成Byte流 ooStream.writeObject(obj); ooStream.flush(); ooStream.close(); } catch (FileNotFoundException e) { e.printStackTrace(); }catch (IOException e) { // TODO: handle exception e.printStackTrace(); } } public static void deser() { //反序列化,读取操作 try { ObjectInputStream iiStream = new ObjectInputStream(new FileInputStream("object.obj")); Object xObject = iiStream.readObject(); System.out.println(xObject); iiStream.close(); } catch (FileNotFoundException e) { // TODO: handle exception e.printStackTrace(); }catch (IOException e) { // TODO: handle exception e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
总结:
这是一个极端的例子,在真实场景中,不会有人真的这样直接写一句执行命令的代码在readObject()中。但是一般会有其他的代码逻辑,如果它的代码逻辑里,如果有某一个分支有像method.invoke()这种可以调用其他函数的代码,那么就可以跳转过去,再从这个“其他函数”的角度去看有没有执行代码或调用其他函数的可能。这个“其他函数”也就是所谓的gadget了。
参考:
http://www.cnblogs.com/xdp-gacl/p/3777987.html
http://www.importnew.com/20125.html
http://beautyboss.farbox.com/post/study/shen-ru-xue-xi-javaxu-lie-hua