java序列化与反序列化详解_java序列化和反序列化-程序员宅基地

技术标签: java  web安全  安全  Web安全  

一:什么是反序列化

序列化:

序列化就是将 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)方法进行序列化和反序列化。

4.1 示例代码

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;
    }
}

1.被transient 修饰的变量不会被序列化,同时若自定义了readObject方法,反序列化时则会执行 自定义的方法

image.png

2.不实现Serializable接口

image.png

3.让序列化的文件的serialVersionUID 和本地class 的不一致

1.首先定义serialVersionUID 然后按住 Ctrl + Shift + A 弹出的dialog 输入serialVersionUID 然后让java计算出独一无二的值

image.png

2.序列化person类,保存到本地,用SerializationDumper 查看序列化后的文件
java -jar SerializationDumper-v1.13.jar -r person.ser

image.png
serialVersionUID - 0x0e 76 fa 9f 59 73 be c6 是16进制转换为二进制,就是生成的值
image.png

3.使用010editor 修改serialVersionUID值

image.png
再次解析下修改后的数据,发现确实改变了
image.png
把序列化的代码注释,再次运行代码,执行反序列化,发现报错了
image.png
报错信息如下,确实符合 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会根据类的成员变量自动生成一个版本号

4. 反序列化文件的格式

原始的16进制格式

aced 0005

在这里插入图片描述

base64编码的

rO0ABXNyABZTZXJpYWxpemEuTGVhcm4uUGVyc29uey5vlQ4y/oYCAAJJAANhZ2VMAARuYW1ldAAS
TGphdmEvbGFuZy9TdHJpbmc7eHAAAAASdAAGUHl0aDBu
在这里插入图片描述

因此在平时我们渗透过程中如果在流量中看到aced0005开头 或rO0AB 开头的流量就需要注意了,很可能这里是存在反序列化的

4.2 注意事项

  1. 序列化类必须要实现Serializable接口
  2. 序列化类中对象属性要求实现Serializable接口
  3. serialVersionUID 序列化版本ID,保证序列化的类和反序列化的类是同一个类
  4. transient(瞬间的)修饰属性,这个属性就不能序列化了
  5. 静态属性也不能被序列化, 静态属性的值不会被序列化,因为它不属于任何一个对象,而是属于类
  6. 如果可以序列化,就会出现下面的情况,当我们序列化某个类的一个对象到某个文件后,这个文件中的对象的那个被static修饰的属性值会固定下来,当另外一个普通的的对象修改了该static属性后,我们再去反序列化那个文件中的对象,就会得到和后面的对象不同的static属性值,
  7. 用Java序列化的二进制字节数据只能由Java反序列化,不能被其他语言反序列化。如果要进行前后端或者不同语言之间的交互一般需要将对象转变成Json/Xml通用格式的数据,再恢复原来的对象。

4.3 transient真的不能被序列化么?

image.pngimage.png
执行结果
image.png
在 Java 中有两种实现序列化的方式,Serializable 和 Externalizable,可能大部分人只知道 Serializable 而不知道 Externalizable。
这两种序列化方式的区别是:实现了 Serializable 接口是自动序列化的,实现 Externalizable 则需要手动序列化,通过 writeExternal 和 readExternal 方法手动进行,
因为我只手动序列化了 saraly属性,所以其他值为初始化值

4.4 transient 关键字总结

  1. transient修饰的变量不能被序列化;
  2. transient只作用于实现 Serializable 接口;
  3. transient只能用来修饰普通成员变量字段;
  4. 不管有没有 transient 修饰,静态变量都不能被序列化;

五、参考资料

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/pyth0zn/article/details/130454935

智能推荐

18个顶级人工智能平台-程序员宅基地

文章浏览阅读1w次,点赞2次,收藏27次。来源:机器人小妹  很多时候企业拥有重复,乏味且困难的工作流程,这些流程往往会减慢生产速度并增加运营成本。为了降低生产成本,企业别无选择,只能自动化某些功能以降低生产成本。  通过数字化..._人工智能平台

electron热加载_electron-reloader-程序员宅基地

文章浏览阅读2.2k次。热加载能够在每次保存修改的代码后自动刷新 electron 应用界面,而不必每次去手动操作重新运行,这极大的提升了开发效率。安装 electron 热加载插件热加载虽然很方便,但是不是每个 electron 项目必须的,所以想要舒服的开发 electron 就只能给 electron 项目单独的安装热加载插件[electron-reloader]:// 在项目的根目录下安装 electron-reloader,国内建议使用 cnpm 代替 npmnpm install electron-relo._electron-reloader

android 11.0 去掉recovery模式UI页面的选项_android recovery 删除 部分菜单-程序员宅基地

文章浏览阅读942次。在11.0 进行定制化开发,会根据需要去掉recovery模式的一些选项 就是在device.cpp去掉一些选项就可以了。_android recovery 删除 部分菜单

mnn linux编译_mnn 编译linux-程序员宅基地

文章浏览阅读3.7k次。https://www.yuque.com/mnn/cn/cvrt_linux_mac基础依赖这些依赖是无关编译选项的基础编译依赖• cmake(3.10 以上)• protobuf (3.0 以上)• 指protobuf库以及protobuf编译器。版本号使用 protoc --version 打印出来。• 在某些Linux发行版上这两个包是分开发布的,需要手动安装• Ubuntu需要分别安装 libprotobuf-dev 以及 protobuf-compiler 两个包•..._mnn 编译linux

利用CSS3制作淡入淡出动画效果_css3入场效果淡入淡出-程序员宅基地

文章浏览阅读1.8k次。CSS3新增动画属性“@-webkit-keyframes”,从字面就可以看出其含义——关键帧,这与Flash中的含义一致。利用CSS3制作动画效果其原理与Flash一样,我们需要定义关键帧处的状态效果,由CSS3来驱动产生动画效果。下面讲解一下如何利用CSS3制作淡入淡出的动画效果。具体实例可参考刚进入本站时的淡入效果。1. 定义动画,名称为fadeIn@-webkit-keyf_css3入场效果淡入淡出

计算机软件又必须包括什么,计算机系统应包括硬件和软件两个子系统,硬件和软件又必须依次分别包括______?...-程序员宅基地

文章浏览阅读2.8k次。计算机系统应包括硬件和软件两个子系统,硬件和软件又必须依次分别包括中央处理器和系统软件。按人的要求接收和存储信息,自动进行数据处理和计算,并输出结果信息的机器系统。计算机是脑力的延伸和扩充,是近代科学的重大成就之一。计算机系统由硬件(子)系统和软件(子)系统组成。前者是借助电、磁、光、机械等原理构成的各种物理部件的有机组合,是系统赖以工作的实体。后者是各种程序和文件,用于指挥全系统按指定的要求进行..._计算机系统包括硬件系统和软件系统 软件又必须包括

随便推点

进程调度(一)——FIFO算法_进程调度fifo算法代码-程序员宅基地

文章浏览阅读7.9k次,点赞3次,收藏22次。一 定义这是最早出现的置换算法。该算法总是淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面予以淘汰。该算法实现简单,只需把一个进程已调入内存的页面,按先后次序链接成一个队列,并设置一个指针,称为替换指针,使它总是指向最老的页面。但该算法与进程实际运行的规律不相适应,因为在进程中,有些页面经常被访问,比如,含有全局变量、常用函数、例程等的页面,FIFO 算法并不能保证这些页面不被淘汰。这里,我_进程调度fifo算法代码

mysql rownum写法_mysql应用之类似oracle rownum写法-程序员宅基地

文章浏览阅读133次。rownum是oracle才有的写法,rownum在oracle中可以用于取第一条数据,或者批量写数据时限定批量写的数量等mysql取第一条数据写法SELECT * FROM t order by id LIMIT 1;oracle取第一条数据写法SELECT * FROM t where rownum =1 order by id;ok,上面是mysql和oracle取第一条数据的写法对比,不过..._mysql 替换@rownum的写法

eclipse安装教程_ecjelm-程序员宅基地

文章浏览阅读790次,点赞3次,收藏4次。官网下载下载链接:http://www.eclipse.org/downloads/点击Download下载完成后双击运行我选择第2个,看自己需要(我选择企业级应用,如果只是单纯学习java选第一个就行)进入下一步后选择jre和安装路径修改jvm/jre的时候也可以选择本地的(点后面的文件夹进去),但是我们没有11版本的,所以还是用他的吧选择接受安装中安装过程中如果有其他界面弹出就点accept就行..._ecjelm

Linux常用网络命令_ifconfig 删除vlan-程序员宅基地

文章浏览阅读245次。原文链接:https://linux.cn/article-7801-1.htmlifconfigping <IP地址>:发送ICMP echo消息到某个主机traceroute <IP地址>:用于跟踪IP包的路由路由:netstat -r: 打印路由表route add :添加静态路由路径routed:控制动态路由的BSD守护程序。运行RIP路由协议gat..._ifconfig 删除vlan

redux_redux redis-程序员宅基地

文章浏览阅读224次。reduxredux里要求把数据都放在公共的存储区域叫store里面,组件中尽量少放数据,假如绿色的组件要给很多灰色的组件传值,绿色的组件只需要改变store里面对应的数据就行了,接着灰色的组件会自动感知到store里的数据发生了改变,store只要有变化,灰色的组件就会自动从store里重新取数据,这样绿色组件的数据就很方便的传到其它灰色组件里了。redux就是把公用的数据放在公共的区域去存..._redux redis

linux 解压zip大文件(解决乱码问题)_linux 7za解压中文乱码-程序员宅基地

文章浏览阅读2.2k次,点赞3次,收藏6次。unzip版本不支持4G以上的压缩包所以要使用p7zip:Linux一个高压缩率软件wget http://sourceforge.net/projects/p7zip/files/p7zip/9.20.1/p7zip_9.20.1_src_all.tar.bz2tar jxvf p7zip_9.20.1_src_all.tar.bz2cd p7zip_9.20.1make && make install 如果安装失败,看一下报错是不是因为没有下载gcc 和 gcc ++(p7_linux 7za解压中文乱码