安全矩阵

 找回密码
 立即注册
搜索
查看: 2483|回复: 0

Java 反序列化之readObejct分析

[复制链接]

855

主题

862

帖子

2940

积分

金牌会员

Rank: 6Rank: 6

积分
2940
发表于 2021-8-2 09:24:13 | 显示全部楼层 |阅读模式
原文链接:Java 反序列化之readObejct分析
前言
之前介绍 java反序列化的时候已经对readObject有了一个大概的了解,知道Java在将字节数据反序列化为Java对象的时候会调用流对象的readObject。
本文较长,可以直接拉到尾部,查看readObject的执行流程图
readObject 分析
例子 :Java 反序列化-开篇中第一个代码
  1. import java.io.*;

  2. public class test{
  3.     public static void main(String args[]) throws Exception{
  4.         //定义myObj对象
  5.         MyObject myObj = new MyObject();
  6.         myObj.name = "hi";
  7.         //创建一个包含对象进行反序列化信息的”object”数据文件
  8.         FileOutputStream fos = new FileOutputStream("object");
  9.         ObjectOutputStream os = new ObjectOutputStream(fos);
  10.         //writeObject()方法将myObj对象写入object文件
  11.         os.writeObject(myObj);
  12.         os.close();
  13.         //从文件中反序列化obj对象
  14.         FileInputStream fis = new FileInputStream("object");
  15.         ObjectInputStream ois = new ObjectInputStream(fis);
  16.         //恢复对象
  17.         MyObject objectFromDisk = (MyObject)ois.readObject();
  18.         System.out.println(objectFromDisk.name);
  19.         ois.close();
  20.     }
  21. }

  22. class MyObject implements Serializable {
  23.     public String name;
  24.     //重写readObject()方法
  25.     private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{
  26.         //执行默认的readObject()方法
  27.         in.defaultReadObject();
  28.         //执行打开计算器程序命令
  29.         Runtime.getRuntime().exec("open -a Calculator");
  30.     }
  31. }
复制代码

ObjectInputStream.readObject()跟进代码
  1. public final Object readObject()
  2.     throws IOException, ClassNotFoundException {
  3.     return readObject(Object.class);
  4. }
复制代码


这里直接 返回了 readObject(Object.class) 继续跟进

  1. private final Object readObject(Class<?> type)
  2.         throws IOException, ClassNotFoundException
  3.     {
  4.         if (enableOverride) {
  5.             return readObjectOverride();
  6.         }

  7.         if (! (type == Object.class || type == String.class))
  8.             throw new AssertionError("internal error");

  9.         // if nested read, passHandle contains handle of enclosing object
  10.         int outerHandle = passHandle;
  11.         try {
  12.             Object obj = readObject0(type, false);
  13.             handles.markDependency(outerHandle, passHandle);
  14.             ClassNotFoundException ex = handles.lookupException(passHandle);
  15.             if (ex != null) {
  16.                 throw ex;
  17.             }
  18.             if (depth == 0) {
  19.                 vlist.doCallbacks();
  20.             }
  21.             return obj;
  22.         } finally {
  23.             passHandle = outerHandle;
  24.             if (closed && depth == 0) {
  25.                 clear();
  26.             }
  27.         }
  28.     }
复制代码

开头先判断 enableOverride ,如果为true 调用readObjectOverride() 而不是readObject()
  1. public ObjectInputStream(InputStream in) throws IOException {
  2.     ...
  3.     enableOverride = false;
  4.     ...
  5. }
复制代码


只有在构造函数实例化时不带参数,才能使enableOverride 为 true
  1. protected ObjectInputStream() throws IOException, SecurityException {
  2.     ...
  3.     enableOverride = true;
  4. }
复制代码


当 enableOverride 为true时,跟进readObjectOverride 函数
  1. protected Object readObjectOverride()
  2.     throws IOException, ClassNotFoundException
  3. {
  4.     return null;
  5. }
复制代码

我们的实例化方法ObjectInputStream 是有参数的,所以不会进入readObjectOverride,继续往下分析Object obj = readObject0(type, false);从readObject0跟进去
  1. private Object readObject0(Class<?> type, boolean unshared) throws IOException {
  2.     boolean oldMode = bin.getBlockDataMode();
  3.     if (oldMode) {
  4.         int remain = bin.currentBlockRemaining();
  5.         if (remain > 0) {
  6.             throw new OptionalDataException(remain);
  7.         } else if (defaultDataEnd) {
  8.             /*
  9.              * Fix for 4360508: stream is currently at the end of a field
  10.              * value block written via default serialization; since there
  11.              * is no terminating TC_ENDBLOCKDATA tag, simulate
  12.              * end-of-custom-data behavior explicitly.
  13.              */
  14.             throw new OptionalDataException(true);
  15.         }
  16.         bin.setBlockDataMode(false);
  17.     }

  18.     byte tc;
  19.     while ((tc = bin.peekByte()) == TC_RESET) {
  20.         bin.readByte();
  21.         handleReset();
  22.     }

  23.     depth++;
  24.     totalObjectRefs++;
  25.     try {
  26.         switch (tc) {
  27.             case TC_NULL:
  28.                 return readNull();

  29.             case TC_REFERENCE:
  30.                 // check the type of the existing object
  31.                 return type.cast(readHandle(unshared));

  32.             case TC_CLASS:
  33.                 if (type == String.class) {
  34.                     throw new ClassCastException("Cannot cast a class to java.lang.String");
  35.                 }
  36.                 return readClass(unshared);

  37.             case TC_CLASSDESC:
  38.             case TC_PROXYCLASSDESC:
  39.                 if (type == String.class) {
  40.                     throw new ClassCastException("Cannot cast a class to java.lang.String");
  41.                 }
  42.                 return readClassDesc(unshared);

  43.             case TC_STRING:
  44.             case TC_LONGSTRING:
  45.                 return checkResolve(readString(unshared));

  46.             case TC_ARRAY:
  47.                 if (type == String.class) {
  48.                     throw new ClassCastException("Cannot cast an array to java.lang.String");
  49.                 }
  50.                 return checkResolve(readArray(unshared));

  51.             case TC_ENUM:
  52.                 if (type == String.class) {
  53.                     throw new ClassCastException("Cannot cast an enum to java.lang.String");
  54.                 }
  55.                 return checkResolve(readEnum(unshared));

  56.             case TC_OBJECT:
  57.                 if (type == String.class) {
  58.                     throw new ClassCastException("Cannot cast an object to java.lang.String");
  59.                 }
  60.                 return checkResolve(readOrdinaryObject(unshared));

  61.             case TC_EXCEPTION:
  62.                 if (type == String.class) {
  63.                     throw new ClassCastException("Cannot cast an exception to java.lang.String");
  64.                 }
  65.                 IOException ex = readFatalException();
  66.                 throw new WriteAbortedException("writing aborted", ex);

  67.             case TC_BLOCKDATA:
  68.             case TC_BLOCKDATALONG:
  69.                 if (oldMode) {
  70.                     bin.setBlockDataMode(true);
  71.                     bin.peek();             // force header read
  72.                     throw new OptionalDataException(
  73.                         bin.currentBlockRemaining());
  74.                 } else {
  75.                     throw new StreamCorruptedException(
  76.                         "unexpected block data");
  77.                 }

  78.             case TC_ENDBLOCKDATA:
  79.                 if (oldMode) {
  80.                     throw new OptionalDataException(true);
  81.                 } else {
  82.                     throw new StreamCorruptedException(
  83.                         "unexpected end of block data");
  84.                 }

  85.             default:
  86.                 throw new StreamCorruptedException(
  87.                     String.format("invalid type code: %02X", tc));
  88.         }
  89.     } finally {
  90.         depth--;
  91.         bin.setBlockDataMode(oldMode);
  92.     }
  93. }
复制代码

这个readObject0是底层readObject 的实现,开头的bin没有在函数中生成,跟一下bin
  1. public ObjectInputStream(InputStream in) throws IOException {
  2.     ...
  3.     bin = new BlockDataInputStream(in);
  4.     ...
  5.     bin.setBlockDataMode(true);
  6. }
复制代码

bin是在ObjectInputStream初始化的时候生成的,现在还不知道bin的用处,继续跟BlockDataInputStream
BlockDataInputStream是ObjectInputStream 块数据输入流,用来完成对序列化Stream的读取,其分为两种模式:Default mode 和Block mode ,默认关闭Block mode
回到上层代码,如果采用块数据模式,bin.getBlockDataMode()就为true 否则为false
如果是块数据模式,就检测当前数据块中剩余未消耗的字节数,如果不是就跑出错误,然后就就转到defalutDataEnd,重新设置块数据模式为false
  1. byte tc;
  2. while ((tc = bin.peekByte()) == TC_RESET) {
  3.     bin.readByte();
  4.     handleReset();
  5. }
复制代码


继续分析readObject0中这一部分代码,tc 会获取流的下一个字节值
继续分析遇到switch判断,通过调试或者分析生成的序列化文件object可以知道tc会走到TC_OBJECT这个分支
  1. case TC_OBJECT:
  2.     if (type == String.class) {
  3.         throw new ClassCastException("Cannot cast an object to java.lang.String");
  4.     }
  5.     return checkResolve(readOrdinaryObject(unshared));
复制代码

跟进readOrdinaryObject(unshared)
  1. private Object readOrdinaryObject(boolean unshared)
  2.     throws IOException
  3. {
  4.     if (bin.readByte() != TC_OBJECT) {
  5.         throw new InternalError();
  6.     }

  7.     ObjectStreamClass desc = readClassDesc(false);
  8.     desc.checkDeserialize();

  9.     Class<?> cl = desc.forClass();
  10.     if (cl == String.class || cl == Class.class
  11.             || cl == ObjectStreamClass.class) {
  12.         throw new InvalidClassException("invalid class descriptor");
  13.     }

  14.     Object obj;
  15.     try {
  16.         obj = desc.isInstantiable() ? desc.newInstance() : null;
  17.     } catch (Exception ex) {
  18.         throw (IOException) new InvalidClassException(
  19.             desc.forClass().getName(),
  20.             "unable to create instance").initCause(ex);
  21.     }

  22.     passHandle = handles.assign(unshared ? unsharedMarker : obj);
  23.     ClassNotFoundException resolveEx = desc.getResolveException();
  24.     if (resolveEx != null) {
  25.         handles.markException(passHandle, resolveEx);
  26.     }

  27.     if (desc.isExternalizable()) {
  28.         readExternalData((Externalizable) obj, desc);
  29.     } else {
  30.         readSerialData(obj, desc);
  31.     }

  32.     handles.finish(passHandle);

  33.     if (obj != null &&
  34.         handles.lookupException(passHandle) == null &&
  35.         desc.hasReadResolveMethod())
  36.     {
  37.         Object rep = desc.invokeReadResolve(obj);
  38.         if (unshared && rep.getClass().isArray()) {
  39.             rep = cloneArray(rep);
  40.         }
  41.         if (rep != obj) {
  42.             // Filter the replacement object
  43.             if (rep != null) {
  44.                 if (rep.getClass().isArray()) {
  45.                     filterCheck(rep.getClass(), Array.getLength(rep));
  46.                 } else {
  47.                     filterCheck(rep.getClass(), -1);
  48.                 }
  49.             }
  50.             handles.setObject(passHandle, obj = rep);
  51.         }
  52.     }

  53.     return obj;
  54. }
复制代码


开头先做了简单的判断,之后调用方法readClassDesc
  1. private ObjectStreamClass readClassDesc(boolean unshared)
  2.         throws IOException
  3. {
  4.         byte tc = bin.peekByte();
  5.         ObjectStreamClass descriptor;
  6.         switch (tc) {
  7.             case TC_NULL:
  8.                 descriptor = (ObjectStreamClass) readNull();
  9.                 break;
  10.             case TC_REFERENCE:
  11.                 descriptor = (ObjectStreamClass) readHandle(unshared);
  12.                 // Should only reference initialized class descriptors
  13.                 descriptor.checkInitialized();
  14.                 break;
  15.             case TC_PROXYCLASSDESC:
  16.                 descriptor = readProxyDesc(unshared);
  17.                 break;
  18.             case TC_CLASSDESC:
  19.                 descriptor = readNonProxyDesc(unshared);
  20.                 break;
  21.             default:
  22.                 throw new StreamCorruptedException(
  23.                     String.format("invalid type code: %02X", tc));
  24.         }
  25.         if (descriptor != null) {
  26.             validateDescriptor(descriptor);
  27.         }
  28.         return descriptor;
  29.     }
复制代码

读取并返回类描述符,在文件格式分析那篇文章中,分析了object的文件格式,读到的第一个Byte是 0x72,用来描述类的结构,类名,成员类型等
跟进去descriptor = readNonProxyDesc(unshared)
  1. private ObjectStreamClass readNonProxyDesc(boolean unshared)
  2.     throws IOException
  3. {
  4.     if (bin.readByte() != TC_CLASSDESC) {
  5.         throw new InternalError();
  6.     }

  7.     ObjectStreamClass desc = new ObjectStreamClass();
  8.     int descHandle = handles.assign(unshared ? unsharedMarker : desc);
  9.     passHandle = NULL_HANDLE;

  10.     ObjectStreamClass readDesc = null;
  11.     try {
  12.         readDesc = readClassDescriptor();
  13.     } catch (ClassNotFoundException ex) {
  14.         throw (IOException) new InvalidClassException(
  15.             "failed to read class descriptor").initCause(ex);
  16.     }

  17.     Class<?> cl = null;
  18.     ClassNotFoundException resolveEx = null;
  19.     bin.setBlockDataMode(true);
  20.     final boolean checksRequired = isCustomSubclass();
  21.     try {
  22.         if ((cl = resolveClass(readDesc)) == null) {
  23.             resolveEx = new ClassNotFoundException("null class");
  24.         } else if (checksRequired) {
  25.             ReflectUtil.checkPackageAccess(cl);
  26.         }
  27.     } catch (ClassNotFoundException ex) {
  28.         resolveEx = ex;
  29.     }

  30.     // Call filterCheck on the class before reading anything else
  31.     filterCheck(cl, -1);

  32.     skipCustomData();

  33.     try {
  34.         totalObjectRefs++;
  35.         depth++;
  36.         desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false));
  37.     } finally {
  38.         depth--;
  39.     }

  40.     handles.finish(descHandle);
  41.     passHandle = descHandle;

  42.     return desc;
  43. }
复制代码


读入并返回非动态代理类的类描述符,首先初始化ObjectStreamClass给desc,然后通过readClassDescriptor()去初始化ObjectStreamClass,在通过readNonProxy(this)来读取非代理类描述符信息


  1. void readNonProxy(ObjectInputStream in)
  2.     throws IOException, ClassNotFoundException
  3. {
  4.     name = in.readUTF();
  5.     suid = Long.valueOf(in.readLong());
  6.     isProxy = false;

  7.     byte flags = in.readByte();
  8.     hasWriteObjectData =
  9.         ((flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0);
  10.     hasBlockExternalData =
  11.         ((flags & ObjectStreamConstants.SC_BLOCK_DATA) != 0);
  12.     externalizable =
  13.         ((flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0);
  14.     boolean sflag =
  15.         ((flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0);
  16.     if (externalizable && sflag) {
  17.         throw new InvalidClassException(
  18.             name, "serializable and externalizable flags conflict");
  19.     }
  20.     serializable = externalizable || sflag;
  21.     isEnum = ((flags & ObjectStreamConstants.SC_ENUM) != 0);
  22.     if (isEnum && suid.longValue() != 0L) {
  23.         throw new InvalidClassException(name,
  24.             "enum descriptor has non-zero serialVersionUID: " + suid);
  25.     }

  26.     int numFields = in.readShort();
  27.     if (isEnum && numFields != 0) {
  28.         throw new InvalidClassException(name,
  29.             "enum descriptor has non-zero field count: " + numFields);
  30.     }
  31.     fields = (numFields > 0) ?
  32.         new ObjectStreamField[numFields] : NO_FIELDS;
  33.     for (int i = 0; i < numFields; i++) {
  34.         char tcode = (char) in.readByte();
  35.         String fname = in.readUTF();
  36.         String signature = ((tcode == 'L') || (tcode == '[')) ?
  37.             in.readTypeString() : new String(new char[] { tcode });
  38.         try {
  39.             fields[i] = new ObjectStreamField(fname, signature, false);
  40.         } catch (RuntimeException e) {
  41.             throw (IOException) new InvalidClassException(name,
  42.                 "invalid descriptor for field " + fname).initCause(e);
  43.         }
  44.     }
  45.     computeFieldOffsets();
  46. }
复制代码

从这里就是对输入流进行细致的判断读取了,
name 就是 class descriptor的class name
suid 就是 serialVersionUID 用来验证该类是否被修改过的验证码,至于serialversionUID是怎么生成的 可以在我java 反序列化writeObject分析中查看
readNonProxy()初始化类名,suid之后,readClassDescriptor将得到的类描述符返回后,继续往下分析
  1. private ObjectStreamClass readNonProxyDesc(boolean unshared)
  2.     throws IOException
  3.     ...

  4.     Class<?> cl = null;
  5.     ClassNotFoundException resolveEx = null;
  6.     bin.setBlockDataMode(true);
  7.     final boolean checksRequired = isCustomSubclass();
  8.     try {
  9.         if ((cl = resolveClass(readDesc)) == null) {
  10.             resolveEx = new ClassNotFoundException("null class");
  11.         } else if (checksRequired) {
  12.             ReflectUtil.checkPackageAccess(cl);
  13.         }
  14.     } catch (ClassNotFoundException ex) {
  15.         resolveEx = ex;
  16.     }

  17.     // Call filterCheck on the class before reading anything else
  18.     filterCheck(cl, -1);

  19.     skipCustomData();

  20.     try {
  21.         totalObjectRefs++;
  22.         depth++;
  23.         desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false));
  24.     } finally {
  25.         depth--;
  26.     }

  27.     handles.finish(descHandle);
  28.     passHandle = descHandle;

  29.     return desc;
  30. }
复制代码


类描述符信息 readDesc传入了resolveClass
  1. protected Class<?> resolveClass(ObjectStreamClass desc)
  2.     throws IOException, ClassNotFoundException
  3. {
  4.     String name = desc.getName();
  5.     try {
  6.         return Class.forName(name, false, latestUserDefinedLoader());
  7.     } catch (ClassNotFoundException ex) {
  8.         Class<?> cl = primClasses.get(name);
  9.         if (cl != null) {
  10.             return cl;
  11.         } else {
  12.             throw ex;
  13.         }
  14.     }
  15. }
复制代码

这个方法 加载与指定流描述符等价的局部类,也就是通过java反射获取当前描述的类别信息。
Java 反射 具体参见 《Java 反射》
在运行时动态的创建对象并调用其属性,不需要在编译期知道运行的对象是谁
调用filterCheck检查获得的描述符cl
  1. private void filterCheck(Class<?> clazz, int arrayLength)
  2.         throws InvalidClassException {
  3.     if (serialFilter != null) {
  4.         RuntimeException ex = null;
  5.         ObjectInputFilter.Status status;
  6.         // Info about the stream is not available if overridden by subclass, return 0
  7.         long bytesRead = (bin == null) ? 0 : bin.getBytesRead();
  8.         try {
  9.             status = serialFilter.checkInput(new FilterValues(clazz, arrayLength,
  10.                     totalObjectRefs, depth, bytesRead));
  11.         } catch (RuntimeException e) {
  12.             // Preventive interception of an exception to log
  13.             status = ObjectInputFilter.Status.REJECTED;
  14.             ex = e;
  15.         }
  16.         if (status == null  ||
  17.                 status == ObjectInputFilter.Status.REJECTED) {
  18.             // Debug logging of filter checks that fail
  19.             if (Logging.infoLogger != null) {
  20.                 Logging.infoLogger.info(
  21.                         "ObjectInputFilter {0}: {1}, array length: {2}, nRefs: {3}, depth: {4}, bytes: {5}, ex: {6}",
  22.                         status, clazz, arrayLength, totalObjectRefs, depth, bytesRead,
  23.                         Objects.toString(ex, "n/a"));
  24.             }
  25.             InvalidClassException ice = new InvalidClassException("filter status: " + status);
  26.             ice.initCause(ex);
  27.             throw ice;
  28.         } else {
  29.             // Trace logging for those that succeed
  30.             if (Logging.traceLogger != null) {
  31.                 Logging.traceLogger.finer(
  32.                         "ObjectInputFilter {0}: {1}, array length: {2}, nRefs: {3}, depth: {4}, bytes: {5}, ex: {6}",
  33.                         status, clazz, arrayLength, totalObjectRefs, depth, bytesRead,
  34.                         Objects.toString(ex, "n/a"));
  35.             }
  36.         }
  37.     }
  38. }
复制代码

serialFilter是在objectInputStream初始化时生成的
当serialFilter存在时,会对信息流进行检测,如果没有通过返回报错
检查结束后,继续回到readNonProxyDesc()
  1. private ObjectStreamClass readNonProxyDesc(boolean unshared)
  2.     throws IOException
  3. {
  4.     ...

  5.     skipCustomData();

  6.     try {
  7.         totalObjectRefs++;
  8.         depth++;
  9.         desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false));
  10.     } finally {
  11.         depth--;
  12.     }

  13.     handles.finish(descHandle);
  14.     passHandle = descHandle;

  15.     return desc;
  16. }
复制代码


跟进skipCustmoData()
  1. private void skipCustomData() throws IOException {
  2.     int oldHandle = passHandle;
  3.     for (;;) {
  4.         if (bin.getBlockDataMode()) {
  5.             bin.skipBlockData();
  6.             bin.setBlockDataMode(false);
  7.         }
  8.         switch (bin.peekByte()) {
  9.             case TC_BLOCKDATA:
  10.             case TC_BLOCKDATALONG:
  11.                 bin.setBlockDataMode(true);
  12.                 break;

  13.             case TC_ENDBLOCKDATA:
  14.                 bin.readByte();
  15.                 passHandle = oldHandle;
  16.                 return;

  17.             default:
  18.                 readObject0(Object.class, false);
  19.                 break;
  20.         }
  21.     }
  22. }
复制代码


跳过所有块数据和对象,直到遇到TC_ENDBLOCKDATA
继续分析readNonPorxyDesc,跟进initNonPorxy
  1. void initNonProxy(ObjectStreamClass model,
  2.                   Class<?> cl,
  3.                   ClassNotFoundException resolveEx,
  4.                   ObjectStreamClass superDesc)
  5.     throws InvalidClassException
  6. {
  7.      long suid = Long.valueOf(model.getSerialVersionUID());
  8.         ObjectStreamClass osc = null;
  9.         if (cl != null) {
  10.             osc = lookup(cl, true);
  11.             if (osc.isProxy) {
  12.                 throw new InvalidClassException(
  13.                         "cannot bind non-proxy descriptor to a proxy class");
  14.             }
  15.             if (model.isEnum != osc.isEnum) {
  16.                 throw new InvalidClassException(model.isEnum ?
  17.                         "cannot bind enum descriptor to a non-enum class" :
  18.                         "cannot bind non-enum descriptor to an enum class");
  19.             }

  20.             if (model.serializable == osc.serializable &&
  21.                     !cl.isArray() &&
  22.                     suid != osc.getSerialVersionUID()) {
  23.                 throw new InvalidClassException(osc.name,
  24.                         "local class incompatible: " +
  25.                                 "stream classdesc serialVersionUID = " + suid +
  26.                                 ", local class serialVersionUID = " +
  27.                                 osc.getSerialVersionUID());
  28.             }

  29.             if (!classNamesEqual(model.name, osc.name)) {
  30.                 throw new InvalidClassException(osc.name,
  31.                         "local class name incompatible with stream class " +
  32.                                 "name "" + model.name + """);
  33.             }

  34.             if (!model.isEnum) {
  35.                 if ((model.serializable == osc.serializable) &&
  36.                         (model.externalizable != osc.externalizable)) {
  37.                     throw new InvalidClassException(osc.name,
  38.                             "Serializable incompatible with Externalizable");
  39.                 }

  40.                 if ((model.serializable != osc.serializable) ||
  41.                         (model.externalizable != osc.externalizable) ||
  42.                         !(model.serializable || model.externalizable)) {
  43.                     deserializeEx = new ExceptionInfo(
  44.                             osc.name, "class invalid for deserialization");
  45.                 }
  46.             }
  47.         }

  48.     this.cl = cl;
  49.     this.resolveEx = resolveEx;
  50.     this.superDesc = superDesc;
  51.     name = model.name;
  52.     this.suid = suid;
  53.     isProxy = false;
  54.     isEnum = model.isEnum;
  55.     serializable = model.serializable;
  56.     externalizable = model.externalizable;
  57.     hasBlockExternalData = model.hasBlockExternalData;
  58.     hasWriteObjectData = model.hasWriteObjectData;
  59.     fields = model.fields;
  60.     primDataSize = model.primDataSize;
  61.     numObjFields = model.numObjFields;

  62.     if (osc != null) {
  63.         localDesc = osc;
  64.         writeObjectMethod = localDesc.writeObjectMethod;
  65.         readObjectMethod = localDesc.readObjectMethod;
  66.         readObjectNoDataMethod = localDesc.readObjectNoDataMethod;
  67.         writeReplaceMethod = localDesc.writeReplaceMethod;
  68.         readResolveMethod = localDesc.readResolveMethod;
  69.         if (deserializeEx == null) {
  70.             deserializeEx = localDesc.deserializeEx;
  71.         }
  72.         domains = localDesc.domains;
  73.         cons = localDesc.cons;
  74.     }

  75.     fieldRefl = getReflector(fields, localDesc);
  76.     // reassign to matched fields so as to reflect local unshared settings
  77.     fields = fieldRefl.getFields();
  78.     initialized = true;
  79. }
复制代码

这个方法会使用readDesc来初始化Desc,所以开头先检查readDesc的正确性使用本地直接new的描述 与model也就是readDesc里的内容进行判断,如果不同就抛出错误
继续往下看initNonProxy()
把localDesc中的属性复制到当前描述的属性上,接着将这个return 给readClassDesc的validateDescriptor
  1. if (descriptor != null) {
  2.     validateDescriptor(descriptor);
  3. }
复制代码


检查结束后,通过就return 到readOrdinaryObject
  1. private Object readOrdinaryObject(boolean unshared)
  2.     throws IOException
  3. {
  4.     ...

  5.     ObjectStreamClass desc = readClassDesc(false);
  6.     desc.checkDeserialize();

  7.     ...
  8.     Object obj;
  9.     try {
  10.         obj = desc.isInstantiable() ? desc.newInstance() : null;
  11.     } catch (Exception ex) {
  12.         throw (IOException) new InvalidClassException(
  13.             desc.forClass().getName(),
  14.             "unable to create instance").initCause(ex);
  15.     }

  16.     ...

  17.     if (desc.isExternalizable()) {
  18.         readExternalData((Externalizable) obj, desc);
  19.     } else {
  20.         readSerialData(obj, desc);
  21.     }

  22.     ...
  23. }
复制代码


看到对返回的desc进行反序列化检查checkDeserialize()
往下继续走
desc.newInstance()就是刚才的构造函数生成的组件
接着,当desc不是Externalizable()就会触发else的readSerialData
  1. private void readSerialData(Object obj, ObjectStreamClass desc)
  2.     throws IOException
  3. {
  4.     ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
  5.     for (int i = 0; i < slots.length; i++) {
  6.         ObjectStreamClass slotDesc = slots[i].desc;

  7.         if (slots[i].hasData) {
  8.             if (obj == null || handles.lookupException(passHandle) != null) {
  9.                 defaultReadFields(null, slotDesc); // skip field values
  10.             } else if (slotDesc.hasReadObjectMethod()) {
  11.                 ThreadDeath t = null;
  12.                 boolean reset = false;
  13.                 SerialCallbackContext oldContext = curContext;
  14.                 if (oldContext != null)
  15.                     oldContext.check();
  16.                 try {
  17.                     curContext = new SerialCallbackContext(obj, slotDesc);

  18.                     bin.setBlockDataMode(true);
  19.                     slotDesc.invokeReadObject(obj, this);
  20.                 } catch (ClassNotFoundException ex) {
  21.                     /*
  22.                      * In most cases, the handle table has already
  23.                      * propagated a CNFException to passHandle at this
  24.                      * point; this mark call is included to address cases
  25.                      * where the custom readObject method has cons'ed and
  26.                      * thrown a new CNFException of its own.
  27.                      */
  28.                     handles.markException(passHandle, ex);
  29.                 } finally {
  30.                     do {
  31.                         try {
  32.                             curContext.setUsed();
  33.                             if (oldContext!= null)
  34.                                 oldContext.check();
  35.                             curContext = oldContext;
  36.                             reset = true;
  37.                         } catch (ThreadDeath x) {
  38.                             t = x;  // defer until reset is true
  39.                         }
  40.                     } while (!reset);
  41.                     if (t != null)
  42.                         throw t;
  43.                 }

  44.                 /*
  45.                  * defaultDataEnd may have been set indirectly by custom
  46.                  * readObject() method when calling defaultReadObject() or
  47.                  * readFields(); clear it to restore normal read behavior.
  48.                  */
  49.                 defaultDataEnd = false;
  50.             } else {
  51.                 defaultReadFields(obj, slotDesc);
  52.                 }
  53.     ...
  54.         }
复制代码

这里先进行判断,当我们没有重写readObject那么就调用defaultReadFilelds,否则调用slotDesc.invokeReadObject(obj,this)
  1. void invokeReadObject(Object obj, ObjectInputStream in)
  2.     throws ClassNotFoundException, IOException,
  3.            UnsupportedOperationException
  4. {
  5.     requireInitialized();
  6.     if (readObjectMethod != null) {
  7.         try {
  8.             readObjectMethod.invoke(obj, new Object[]{ in });
  9.         } catch (InvocationTargetException ex) {
  10.             Throwable th = ex.getTargetException();
  11.             if (th instanceof ClassNotFoundException) {
  12.                 throw (ClassNotFoundException) th;
  13.             } else if (th instanceof IOException) {
  14.                 throw (IOException) th;
  15.             } else {
  16.                 throwMiscException(th);
  17.             }
  18.         } catch (IllegalAccessException ex) {
  19.             // should not occur, as access checks have been suppressed
  20.             throw new InternalError(ex);
  21.         }
  22.     } else {
  23.         throw new UnsupportedOperationException();
  24.     }
  25. }
复制代码

读取我们重写的readObject可以看到readObjectMethod.invoke(obj, new Object[]{ in });
这里的readObjectMethod就是我们自写的readObject方法



readObject 执行流程

Java 反序列化之 readObject 分析


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-29 12:45 , Processed in 0.021863 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表