赞
踩
序列化就是将 java对象 转化为字节序列的过程。
序列化是指把一个Java对象变成二进制内容,本质上就是一个byte[]数组。 为什么要把Java对象序列化呢?因为序列化后可以把byte[]保存到文件中,或者把byte[]通过网络传输到远程,这样,就相当于把Java对象存储到文件或者通过网络传输出去了。
注意:序列化是为了在传递和保存对象时,为了保证对象的完整性和可传递性。将对象转为有序的字节流,以便在网上传输或者保存在本地文件中。
反序列化就是将 字节序列恢复为java对象的过程。
有序列化,就有反序列化,即把一个二进制内容(也就是byte[]数组)变回Java对象。有了反序列化,保存到文件中的byte[]数组又可以“变回”Java对象,或者从网络上读取byte[]并把它“变回”Java对象。
两个进程在远程通信时,可以发送多种数据,包括文本、图片、音频、视频等,这些数据都是以二进制序列的形式在网络上传输。
java是面向对象的开发方式,一切都是java对象,想要在网络中传输java对象,可以使用序列化和反序列化去实现,发送发需要将java对象转换为字节序列,然后在网络上传送,接收方收到字符序列后,会通过反序列化将字节序列恢复成java对象。
1.JDK类库提供的序列化API:
java.io.ObjectOutputStream
表示对象输出流,其中writeObject(Object obj)方法可以将给定参数的obj对象进行序列化,将转换的一连串的字节序列写到指定的目标输出流中。
java.io.ObjectInputStream
该类表示对象输入流,该类下的readObject(Object obj)方法会从源输入流中读取字节序列,并将它反序列化为一个java对象并返回
实现序列化的类对象必须实现了Serializable类或Externalizable类才能被序列化,否则会抛出异常
方法一:若student类实现了serializable接口,则可以通过objectOutputstream和objectinputstream默认的序列化和反序列化方式,对非transient的实例变量进行序列化和反序列化。
方法二:若student类实现了serializable接口,并且定义了writeObject(objectOutputStream out)和readObject(objectinputStream in)方法
则可以直接调用student类的两种方法进行序列化和反序列化
方法三:若student类实现了Externalizable接口,则必须实现readExternal(Objectinput in)和writeExternal(Objectoutput out)方法进行序列化和反序列化。
package Serializa.Learn;
import java.io.*;
public class Person implements Serializable {
private static String secret;
private String name;
private int age;
transient private double saraly;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSaraly() {
return saraly;
}
public void setSaraly(double saraly) {
this.saraly = saraly;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
", saraly='" + saraly + '\'' +
", secret='" + secret + '\'' +
'}';
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
Person p1 = new Person();
p1.setAge(18);
p1.setName("lxl");
p1.setSaraly(5000000);
Person.secret = "aaa";
System.out.println(p1);
serizliza(p1);
// Person.secret="bbbb";
Person p2 = (Person) deserizliza("person.ser");
System.out.println("反序列化" + p2);
}
private static void serizliza(Object obj) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"));
oos.writeObject(obj);
}
private static Object deserizliza(String file) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
Object obj = ois.readObject();
return obj;
}
}
java -jar SerializationDumper-v1.13.jar -r person.ser
serialVersionUID - 0x0e 76 fa 9f 59 73 be c6 是16进制转换为二进制,就是生成的值
再次解析下修改后的数据,发现确实改变了
把序列化的代码注释,再次运行代码,执行反序列化,发现报错了
报错信息如下,确实符合 java 反序列的规范,
Exception in thread "main" java.io.InvalidClassException: Serializa.Learn.Person; local class incompatible: stream classdesc serialVersionUID = 1042295926090350279, local class serialVersionUID = 1042295926090350278
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:2001)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1848)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2158)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1665)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:501)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:459)
at Serializa.Learn.Person.deserizliza(Person.java:77)
at Serializa.Learn.Person.main(Person.java:61)
所以这也就是为什么我们在利用反序列化漏洞的时候,明明有这个链,但是不能成功,因为版本不一致。所以serializaUID 不一致的原因,
PS:serialVersionUID 的初衷是为了在反序列化时确保类的版本号与序列化时的版本号一致,避免出现版本不一致的问题,serialVersionUID 是根据类的成员变量自动生成的。如果没有手动指定 serialVersionUID,Java会根据类的成员变量自动生成一个版本号
原始的16进制格式
aced 0005
base64编码的
rO0ABXNyABZTZXJpYWxpemEuTGVhcm4uUGVyc29uey5vlQ4y/oYCAAJJAANhZ2VMAARuYW1ldAAS
TGphdmEvbGFuZy9TdHJpbmc7eHAAAAASdAAGUHl0aDBu
因此在平时我们渗透过程中如果在流量中看到aced0005开头 或rO0AB 开头的流量就需要注意了,很可能这里是存在反序列化的
执行结果
在 Java 中有两种实现序列化的方式,Serializable 和 Externalizable,可能大部分人只知道 Serializable 而不知道 Externalizable。
这两种序列化方式的区别是:实现了 Serializable 接口是自动序列化的,实现 Externalizable 则需要手动序列化,通过 writeExternal 和 readExternal 方法手动进行,
因为我只手动序列化了 saraly属性,所以其他值为初始化值
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。