JAVA反序列化的简单探究

x33g5p2x  于2021-11-20 转载在 Java  
字(3.8k)|赞(0)|评价(0)|浏览(448)

JAVA反序列化的简单探究

本文主要是探究,在反序列化过程中是怎么调用到readObject、readResolve、readExternal方法的问题

新建一个需要被序列化的类ObjectA,写入readResolve和readObject方法:

package com.yy.serialize.readResolve;

import java.io.IOException;
import java.io.Serializable;

public class ObjectA implements Serializable {
    private ObjectA() {
    }

    private static final ObjectA objectA = new ObjectA();

    public static ObjectA getInstance() {
        return objectA;
    }
    private Object readResolve() {
        System.out.println("执行了readResolve方法");
        return objectA;
    }

    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
        //执行默认的readObject()方法
        in.defaultReadObject();
        System.out.println("执行了readObject方法");
    }
}

另一个类ObjectB则写入了readExternal方法:

package com.yy.serialize.readResolve;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

public class ObjectB implements Externalizable {
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        System.out.println("执行了writeExternal");
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        System.out.println("执行了readExternal");
    }
}

测试类:

package com.yy.serialize.readResolve;

import java.io.*;

public class SerializeDemo {
    public static void main(String[] args) throws Exception {
        ObjectA objectA = ObjectA.getInstance();
        ObjectB objectB = new ObjectB();

        // 序列化
        FileOutputStream fos = new FileOutputStream("a.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(objectA);
        oos.flush();

        // 反序列化
        FileInputStream fis = new FileInputStream("a.txt");
        ObjectInputStream ois = new ObjectInputStream(fis);
        ois.readObject();

    }
}

运行测试类生成a.txt文件后,可以使用SerializationDumper查看字节流的数据:

这里有两个关键的标识

TC_OBJECT:标记后面的数据为Object对象

TC_CLASSDESC:类描述符标识,表示一个类中的所有信息

TC_CLASSDESC - 0x72
      className
        Length - 36 - 0x00 24
        Value - com.yy.serialize.readResolve.ObjectA - 0x636f6d2e79792e73657269616c697a652e726561645265736f6c76652e4f626a65637441
      serialVersionUID - 0xee 44 c6 e6 0d b3 64 b5
      newHandle 0x00 7e 00 00
      classDescFlags - 0x02 - SC_SERIALIZABLE
      fieldCount - 0 - 0x00 00
      classAnnotations
        TC_ENDBLOCKDATA - 0x78
      superClassDesc
        TC_NULL - 0x70

调用分析

ois.readObject下个断点

进入ObjectInputStream#readObject方法,看到其调用了readObject0

跟进readObject0,在此函数中,根据了tc值来进行switch,此时的tc值为TC_OBJECT,也就是0x73十进制数115

case TC_OBJECT中,调用了readOrdinaryObject

跟进readOrdinaryObject中,发现调用了readClassDesc方法,并把值赋给了desc

跟进readClassDesc,此方法用来分发处理字节流中TC_CLASSDESC的方法,用switch来选择需要处理的方法

tc的值就是TC_CLASSDESC的值0x72,转成10进制就是114,然后进入switch判断后转到case TC_CLASSDESC:

跟进readNonProxyDesc方法,调用了resolveClass方法后,把值赋给了cl

继续跟进resolveClass后,发现使用了Class.forName来创建了ObjectA对象,然后把创建的对象进行return返回

回到readNonProxyDesc,此时cl值变成了ObjectA对象,然后把cl对象传入了initNonProxy,并且赋值给了desc

最后readNonProxyDesc方法返回了desc的值

返回的desc的值赋给了调用处readClassDesc的descriptor,然后又进行了返回

最后回到了readOrdinaryObject方法中

在这里先总结下这几个方法分别做了什么操作

总结下上面的流程顺序为:

readOrdinaryObject -》 readClassDesc -》 readNonProxyDesc -》 readResolve方法

readClassDesc :分发用于处理字节流中TC_CLASSDESC的方法,用switch来选择需要处理的方法

readNonProxyDesc :真正用来处理字节流中的TC_CLASSDESC方法,会调用resolveClass进行创建反序列化的对象

resolveClass:是用Class.forName来创建ObjectA对象的地方,在这里可以做一个检查校验,用于反序列化拦截。weblogic中补丁拦截就是在此进行的

到这里三个方法都讲了用途后,还剩最后一个readOrdinaryObject 了

readOrdinaryObject

这里的readOrdinaryObject就是真正操作调用序列化类中,readObject、readResolve、readExternal方法的地方

接着上面debug,拿到了desc的值后往下走,做了一个判断desc.isExternalizable,如果序列化的接口是Externalizable类型,就进入readExternalData,否则进入readSerialData

此处的ObjectA对象接口类型是Serializable,所以进入了readSerialData方法

最后readSerialData方法中用了反射进行调用反序列化对象的readObject方法

回到readOrdinaryObject,接下来就是调用readResolve方法的地方了

用if进行判断,为true则用反射调用反序列化对象的readResolve方法

引用一张xz上师傅文章的流程图:

https://xz.aliyun.com/t/8443#toc-2

总结

方法调用的流程顺序:

readOrdinaryObject -》 readClassDesc -》 readNonProxyDesc -》 readResolve方法

readClassDesc :分发用于处理字节流中TC_CLASSDESC的方法,用switch来选择需要处理的方法

readNonProxyDesc :真正用来处理字节流中的TC_CLASSDESC方法,会调用resolveClass进行创建反序列化的对象

resolveClass:是用Class.forName来创建ObjectA对象的地方,在这里可以做一个检查校验,用于反序列化拦截。weblogic中补丁拦截就是在此进行的

readOrdinaryObject:用于调用序列化类中,readObject、readResolve、readExternal的方法

相关文章

微信公众号

最新文章

更多