0x01
开始学习JAVA反序列化,参考 《安全漫谈》和feng师傅的文章一步一步来,希望 能赶在这个学期学完java最基础的东西, 笔记做到这里方便自己查阅,也是事先实操了一下 ,才写的笔记
概念:
JAVA 序列化 就是把一个 JAVA Object 变成一个二进制字节数组 即 byte[]
JAVA 反序列化 就是把一个 二进制数组 byte[] 变回Java 对象 ,即JAva Object
序列化/反序列化具备条件
如果一个类 实现序列化或反序列化操作, 必须要实现 Serializable
接口或者Externalizable
接口。 最好还要有一个 serialVersionUID 属性
这属性的介绍:
每个可序列化的类在序列化时都会关联一个版本号 , 这个版本号就是 serialVersionUID 属性 .
在反序列化过程中会根据这个版本号来判断序列化对象的发送者和接收着是否有与该序列化/反序列化过程兼容的类 .( 简单的说就是序列化过程和反序列化过程都需要被序列化的类 , 通过 serialVersionUID 属性来判断这两个类的版本是否相同 , 是否是同一个类 ) . 如果不相同 , 则会抛出 InvalidClassException 异常
serialVersionUID 属性必须通过 static final long 修饰符来修饰 .
如果可序列化的类未声明 serialVersionUID 属性 , 则 Java 序列化时会根据类的各种信息来计算默认的 serialVersionUID 值 . 但是 Oracle 官方文档强烈建议所有可序列化的类都显示声明 serialVersionUID 值 .
Serializable
只是一个空接口,没有任何操作
只是一个标识接口,意味着实现了这个接口的类 可以进行序列化和反序列化操作。
Externalizable
继承的 Serializable
接口的接口
Externalizable 接口使用较为麻烦,一般会使用Serializable
ObjectOutputStream
官方文档:
关键是 writeObject 方法 ,可以将对象写入数据流中(序列化)
需要注意的是
对象的默认序列化机制会写入对象的类,类签名以及所有非瞬态和非静态字段的值。 对其他对象的引用(瞬态或静态字段除外)也会导致这些对象被写入。
需要注意的是,static 字段是不会被序列化的,只会保留原有的值(个人理解
关于类签名:
在开发 JNI( Java Native Interface , Java 本地接口 ) 时需要调用 Java 层的方法或创建引用 , 此时就会用到 Java 签名机制 . 比如基本数据类型的签名如下所示:
https://www.jianshu.com/p/a1438b476e82
ObjectInputStream
而这个函数,则是 php中的 unserialize , 反序列化。
代码实现:
写三个类进行 实现操作,直接借用feng师傅的代码了,因为我已经测试过了
序列化操作
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;public class Serialize {public static void main(String[] args) throws IOException {flag flag = new flag();flag.setTrueFlag("flag{hello,world}");flag.setTestName("feng");FileOutputStream fout = new FileOutputStream("2.txt");ObjectOutputStream oout = new ObjectOutputStream(fout);oout.writeObject(flag);fout.close();oout.close();}
}
反序列化操作
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;public class unserialize {public class TestUnser2 {public static void main(String[] args) throws IOException, ClassNotFoundException {FileInputStream fin = new FileInputStream("2.txt");ObjectInputStream oin = new ObjectInputStream(fin);flag flag = (flag) oin.readObject();fin.close();oin.close();String trueFlag = flag.getFlag("feng");System.out.println(trueFlag);System.out.println(flag.serialVersionUID);System.out.println(flag.testName);}}
}
flag
package com.summer.unserialize;import java.io.Serializable;public class Flag implements Serializable {static final long serialVersionUID = 1L;private String trueFlag;public static String testName = "test";public void setTestName(String testName){Flag.testName = testName;}public void setTrueFlag(String trueFlag){this.trueFlag = trueFlag;}public String getFlag(String name){System.out.println(name + " get the flag");return this.trueFlag;}
}
代码的具体流程 是 把值写进去,然后序列化成2.txt ,代码实现很简单,但是要注意的地方是这个静态方法 static。
public static String testName = "test";
序列化的时候 传参:
flag.setTestName("feng");System.out.println(flag.testName);
然后我们进行反序列读取看看
看到这里 最后一行的输出值是test ,也就是说 静态的字段不会被序列化,因为反序列化读取后 testName的值还是 test
implement方法
跟着feng师傅思路回到Serializable
接口的文档注释里的这个:
* Classes that require special handling during the serialization and* deserialization process must implement special methods with these exact* signatures:** <PRE>* private void writeObject(java.io.ObjectOutputStream out)* throws IOException* private void readObject(java.io.ObjectInputStream in)* throws IOException, ClassNotFoundException;* private void readObjectNoData()* throws ObjectStreamException;* </PRE>
如果需要特殊的处理,类需要自己 implement 这些方法。
readObject
总结一下就是,readObject支持我们重写该方法,在该方法中添加新的功能,但是重写的话唯一的限制 就要添加原有的功能,即 defaultReadObject() 方法。
例子:
在flag类中添加
private void readObject(java.io.ObjectInputStream stream)throws IOException, ClassNotFoundException{stream.defaultReadObject();Runtime.getRuntime().exec("calc");}
然后序列化,再进行反序列化,在反序列化中,会调用
flag flag = (flag) oin.readObject();
而flag中的readObject(),进行重写了,也就实现了反序列化弹出计算器
writeObject
看看 writeObject
的用处。把 Flag
类里面写这个:
private void readObject(java.io.ObjectInputStream stream)throws IOException, ClassNotFoundException{stream.defaultReadObject();Runtime.getRuntime().exec("calc");System.out.println(stream.readObject());}private void writeObject(java.io.ObjectOutputStream stream)throws IOException{stream.defaultWriteObject();stream.writeObject("This is feng");}
和feng师傅有一样的疑惑,为什么把flag 对象写进了流,writeObject
方法里又调用了stream.writeObject("This is feng");
写了个对象进去,这不就相当于写了两个对象吗?
那readObject
里面新加的System.out.println(stream.readObject());
,又能干什么?
工具查看
STREAM_MAGIC - 0xac ed
STREAM_VERSION - 0x00 05
ContentsTC_OBJECT - 0x73TC_CLASSDESC - 0x72classNameLength - 27 - 0x00 1bValue - com.summer.unserialize.Flag - 0x636f6d2e73756d6d65722e756e73657269616c697a652e466c6167serialVersionUID - 0x00 00 00 00 00 00 00 01newHandle 0x00 7e 00 00classDescFlags - 0x03 - SC_WRITE_METHOD | SC_SERIALIZABLEfieldCount - 1 - 0x00 01Fields0:Object - L - 0x4cfieldNameLength - 8 - 0x00 08Value - trueFlag - 0x74727565466c6167className1TC_STRING - 0x74newHandle 0x00 7e 00 01Length - 18 - 0x00 12Value - Ljava/lang/String; - 0x4c6a6176612f6c616e672f537472696e673bclassAnnotationsTC_ENDBLOCKDATA - 0x78superClassDescTC_NULL - 0x70newHandle 0x00 7e 00 02classdatacom.summer.unserialize.FlagvaluestrueFlag(object)TC_STRING - 0x74newHandle 0x00 7e 00 03Length - 17 - 0x00 11Value - flag{hello,world} - 0x666c61677b68656c6c6f2c776f726c647dobjectAnnotationTC_STRING - 0x74newHandle 0x00 7e 00 04Length - 12 - 0x00 0cValue - This is feng - 0x546869732069732066656e67TC_ENDBLOCKDATA - 0x78
看到objectAnnotation
objectAnnotationTC_STRING - 0x74newHandle 0x00 7e 00 04Length - 12 - 0x00 0cValue - This is feng - 0x546869732069732066656e67TC_ENDBLOCKDATA - 0x78
即
stream.writeObject("This is feng");
反序列化结果
总结
因此可以看出,在自定义的writeobject中 调用传入的ObjectOutputStream
的writeObject
方法写入的对象,会写入到objectAnnotation
中。
而自定义的readObject 方法中,调入传入的ObjectInputStream
的readObject
方法读入的对象,则是objectAnnotation
中存储的对象。
rethink
才刚刚入门 感觉还是好难,JAVA漫谈的URLDNS 也跟不上。都是涉及底层的代码。以后再说把呜呜呜
参考
Java 序列化和反序列化 学习笔记_bfengj的博客-CSDN博客
https://www.liaoxuefeng.com/wiki/1252599548343744/1298366845681698
https://blog.csdn.net/dan15188387481/article/details/49977421
https://www.lagou.com/lgeduarticle/94091.html
https://docs.oracle.com/javase/8/docs/platform/serialization/spec/protocol.html
https://www.anquanke.com/post/id/238480