原文链接:Java 反序列化之readObejct分析
前言之前介绍 java反序列化的时候已经对readObject有了一个大概的了解,知道Java在将字节数据反序列化为Java对象的时候会调用流对象的readObject。 本文较长,可以直接拉到尾部,查看readObject的执行流程图 readObject 分析例子 :Java 反序列化-开篇中第一个代码 - import java.io.*;
- public class test{
- public static void main(String args[]) throws Exception{
- //定义myObj对象
- MyObject myObj = new MyObject();
- myObj.name = "hi";
- //创建一个包含对象进行反序列化信息的”object”数据文件
- FileOutputStream fos = new FileOutputStream("object");
- ObjectOutputStream os = new ObjectOutputStream(fos);
- //writeObject()方法将myObj对象写入object文件
- os.writeObject(myObj);
- os.close();
- //从文件中反序列化obj对象
- FileInputStream fis = new FileInputStream("object");
- ObjectInputStream ois = new ObjectInputStream(fis);
- //恢复对象
- MyObject objectFromDisk = (MyObject)ois.readObject();
- System.out.println(objectFromDisk.name);
- ois.close();
- }
- }
- class MyObject implements Serializable {
- public String name;
- //重写readObject()方法
- private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{
- //执行默认的readObject()方法
- in.defaultReadObject();
- //执行打开计算器程序命令
- Runtime.getRuntime().exec("open -a Calculator");
- }
- }
复制代码
从ObjectInputStream.readObject()跟进代码- public final Object readObject()
- throws IOException, ClassNotFoundException {
- return readObject(Object.class);
- }
复制代码
这里直接 返回了 readObject(Object.class) 继续跟进
- private final Object readObject(Class<?> type)
- throws IOException, ClassNotFoundException
- {
- if (enableOverride) {
- return readObjectOverride();
- }
- if (! (type == Object.class || type == String.class))
- throw new AssertionError("internal error");
- // if nested read, passHandle contains handle of enclosing object
- int outerHandle = passHandle;
- try {
- Object obj = readObject0(type, false);
- handles.markDependency(outerHandle, passHandle);
- ClassNotFoundException ex = handles.lookupException(passHandle);
- if (ex != null) {
- throw ex;
- }
- if (depth == 0) {
- vlist.doCallbacks();
- }
- return obj;
- } finally {
- passHandle = outerHandle;
- if (closed && depth == 0) {
- clear();
- }
- }
- }
复制代码
开头先判断 enableOverride ,如果为true 调用readObjectOverride() 而不是readObject()
- public ObjectInputStream(InputStream in) throws IOException {
- ...
- enableOverride = false;
- ...
- }
复制代码
只有在构造函数实例化时不带参数,才能使enableOverride 为 true
- protected ObjectInputStream() throws IOException, SecurityException {
- ...
- enableOverride = true;
- }
复制代码
当 enableOverride 为true时,跟进readObjectOverride 函数
- protected Object readObjectOverride()
- throws IOException, ClassNotFoundException
- {
- return null;
- }
复制代码
我们的实例化方法ObjectInputStream 是有参数的,所以不会进入readObjectOverride,继续往下分析Object obj = readObject0(type, false);从readObject0跟进去
这个readObject0是底层readObject 的实现,开头的bin没有在函数中生成,跟一下bin- public ObjectInputStream(InputStream in) throws IOException {
- ...
- bin = new BlockDataInputStream(in);
- ...
- bin.setBlockDataMode(true);
- }
复制代码
bin是在ObjectInputStream初始化的时候生成的,现在还不知道bin的用处,继续跟BlockDataInputStream
BlockDataInputStream是ObjectInputStream 块数据输入流,用来完成对序列化Stream的读取,其分为两种模式:Default mode 和Block mode ,默认关闭Block mode
回到上层代码,如果采用块数据模式,bin.getBlockDataMode()就为true 否则为false
如果是块数据模式,就检测当前数据块中剩余未消耗的字节数,如果不是就跑出错误,然后就就转到defalutDataEnd,重新设置块数据模式为false
- byte tc;
- while ((tc = bin.peekByte()) == TC_RESET) {
- bin.readByte();
- handleReset();
- }
复制代码
继续分析readObject0中这一部分代码,tc 会获取流的下一个字节值
继续分析遇到switch判断,通过调试或者分析生成的序列化文件object可以知道tc会走到TC_OBJECT这个分支
- case TC_OBJECT:
- if (type == String.class) {
- throw new ClassCastException("Cannot cast an object to java.lang.String");
- }
- return checkResolve(readOrdinaryObject(unshared));
复制代码
跟进readOrdinaryObject(unshared)- private Object readOrdinaryObject(boolean unshared)
- throws IOException
- {
- if (bin.readByte() != TC_OBJECT) {
- throw new InternalError();
- }
- ObjectStreamClass desc = readClassDesc(false);
- desc.checkDeserialize();
- Class<?> cl = desc.forClass();
- if (cl == String.class || cl == Class.class
- || cl == ObjectStreamClass.class) {
- throw new InvalidClassException("invalid class descriptor");
- }
- Object obj;
- try {
- obj = desc.isInstantiable() ? desc.newInstance() : null;
- } catch (Exception ex) {
- throw (IOException) new InvalidClassException(
- desc.forClass().getName(),
- "unable to create instance").initCause(ex);
- }
- passHandle = handles.assign(unshared ? unsharedMarker : obj);
- ClassNotFoundException resolveEx = desc.getResolveException();
- if (resolveEx != null) {
- handles.markException(passHandle, resolveEx);
- }
- if (desc.isExternalizable()) {
- readExternalData((Externalizable) obj, desc);
- } else {
- readSerialData(obj, desc);
- }
- handles.finish(passHandle);
- if (obj != null &&
- handles.lookupException(passHandle) == null &&
- desc.hasReadResolveMethod())
- {
- Object rep = desc.invokeReadResolve(obj);
- if (unshared && rep.getClass().isArray()) {
- rep = cloneArray(rep);
- }
- if (rep != obj) {
- // Filter the replacement object
- if (rep != null) {
- if (rep.getClass().isArray()) {
- filterCheck(rep.getClass(), Array.getLength(rep));
- } else {
- filterCheck(rep.getClass(), -1);
- }
- }
- handles.setObject(passHandle, obj = rep);
- }
- }
- return obj;
- }
复制代码
开头先做了简单的判断,之后调用方法readClassDesc
- private ObjectStreamClass readClassDesc(boolean unshared)
- throws IOException
- {
- byte tc = bin.peekByte();
- ObjectStreamClass descriptor;
- switch (tc) {
- case TC_NULL:
- descriptor = (ObjectStreamClass) readNull();
- break;
- case TC_REFERENCE:
- descriptor = (ObjectStreamClass) readHandle(unshared);
- // Should only reference initialized class descriptors
- descriptor.checkInitialized();
- break;
- case TC_PROXYCLASSDESC:
- descriptor = readProxyDesc(unshared);
- break;
- case TC_CLASSDESC:
- descriptor = readNonProxyDesc(unshared);
- break;
- default:
- throw new StreamCorruptedException(
- String.format("invalid type code: %02X", tc));
- }
- if (descriptor != null) {
- validateDescriptor(descriptor);
- }
- return descriptor;
- }
复制代码
读取并返回类描述符,在文件格式分析那篇文章中,分析了object的文件格式,读到的第一个Byte是 0x72,用来描述类的结构,类名,成员类型等
跟进去descriptor = readNonProxyDesc(unshared)
- private ObjectStreamClass readNonProxyDesc(boolean unshared)
- throws IOException
- {
- if (bin.readByte() != TC_CLASSDESC) {
- throw new InternalError();
- }
- ObjectStreamClass desc = new ObjectStreamClass();
- int descHandle = handles.assign(unshared ? unsharedMarker : desc);
- passHandle = NULL_HANDLE;
- ObjectStreamClass readDesc = null;
- try {
- readDesc = readClassDescriptor();
- } catch (ClassNotFoundException ex) {
- throw (IOException) new InvalidClassException(
- "failed to read class descriptor").initCause(ex);
- }
- Class<?> cl = null;
- ClassNotFoundException resolveEx = null;
- bin.setBlockDataMode(true);
- final boolean checksRequired = isCustomSubclass();
- try {
- if ((cl = resolveClass(readDesc)) == null) {
- resolveEx = new ClassNotFoundException("null class");
- } else if (checksRequired) {
- ReflectUtil.checkPackageAccess(cl);
- }
- } catch (ClassNotFoundException ex) {
- resolveEx = ex;
- }
- // Call filterCheck on the class before reading anything else
- filterCheck(cl, -1);
- skipCustomData();
- try {
- totalObjectRefs++;
- depth++;
- desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false));
- } finally {
- depth--;
- }
- handles.finish(descHandle);
- passHandle = descHandle;
- return desc;
- }
复制代码
读入并返回非动态代理类的类描述符,首先初始化ObjectStreamClass给desc,然后通过readClassDescriptor()去初始化ObjectStreamClass,在通过readNonProxy(this)来读取非代理类描述符信息
- void readNonProxy(ObjectInputStream in)
- throws IOException, ClassNotFoundException
- {
- name = in.readUTF();
- suid = Long.valueOf(in.readLong());
- isProxy = false;
- byte flags = in.readByte();
- hasWriteObjectData =
- ((flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0);
- hasBlockExternalData =
- ((flags & ObjectStreamConstants.SC_BLOCK_DATA) != 0);
- externalizable =
- ((flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0);
- boolean sflag =
- ((flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0);
- if (externalizable && sflag) {
- throw new InvalidClassException(
- name, "serializable and externalizable flags conflict");
- }
- serializable = externalizable || sflag;
- isEnum = ((flags & ObjectStreamConstants.SC_ENUM) != 0);
- if (isEnum && suid.longValue() != 0L) {
- throw new InvalidClassException(name,
- "enum descriptor has non-zero serialVersionUID: " + suid);
- }
- int numFields = in.readShort();
- if (isEnum && numFields != 0) {
- throw new InvalidClassException(name,
- "enum descriptor has non-zero field count: " + numFields);
- }
- fields = (numFields > 0) ?
- new ObjectStreamField[numFields] : NO_FIELDS;
- for (int i = 0; i < numFields; i++) {
- char tcode = (char) in.readByte();
- String fname = in.readUTF();
- String signature = ((tcode == 'L') || (tcode == '[')) ?
- in.readTypeString() : new String(new char[] { tcode });
- try {
- fields[i] = new ObjectStreamField(fname, signature, false);
- } catch (RuntimeException e) {
- throw (IOException) new InvalidClassException(name,
- "invalid descriptor for field " + fname).initCause(e);
- }
- }
- computeFieldOffsets();
- }
复制代码
从这里就是对输入流进行细致的判断读取了,
name 就是 class descriptor的class name suid 就是 serialVersionUID 用来验证该类是否被修改过的验证码,至于serialversionUID是怎么生成的 可以在我java 反序列化writeObject分析中查看 readNonProxy()初始化类名,suid之后,readClassDescriptor将得到的类描述符返回后,继续往下分析 - private ObjectStreamClass readNonProxyDesc(boolean unshared)
- throws IOException
- ...
- Class<?> cl = null;
- ClassNotFoundException resolveEx = null;
- bin.setBlockDataMode(true);
- final boolean checksRequired = isCustomSubclass();
- try {
- if ((cl = resolveClass(readDesc)) == null) {
- resolveEx = new ClassNotFoundException("null class");
- } else if (checksRequired) {
- ReflectUtil.checkPackageAccess(cl);
- }
- } catch (ClassNotFoundException ex) {
- resolveEx = ex;
- }
- // Call filterCheck on the class before reading anything else
- filterCheck(cl, -1);
- skipCustomData();
- try {
- totalObjectRefs++;
- depth++;
- desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false));
- } finally {
- depth--;
- }
- handles.finish(descHandle);
- passHandle = descHandle;
- return desc;
- }
复制代码
类描述符信息 readDesc传入了resolveClass
- protected Class<?> resolveClass(ObjectStreamClass desc)
- throws IOException, ClassNotFoundException
- {
- String name = desc.getName();
- try {
- return Class.forName(name, false, latestUserDefinedLoader());
- } catch (ClassNotFoundException ex) {
- Class<?> cl = primClasses.get(name);
- if (cl != null) {
- return cl;
- } else {
- throw ex;
- }
- }
- }
复制代码
这个方法 加载与指定流描述符等价的局部类,也就是通过java反射获取当前描述的类别信息。Java 反射 具体参见 《Java 反射》 在运行时动态的创建对象并调用其属性,不需要在编译期知道运行的对象是谁 调用filterCheck检查获得的描述符cl
- private void filterCheck(Class<?> clazz, int arrayLength)
- throws InvalidClassException {
- if (serialFilter != null) {
- RuntimeException ex = null;
- ObjectInputFilter.Status status;
- // Info about the stream is not available if overridden by subclass, return 0
- long bytesRead = (bin == null) ? 0 : bin.getBytesRead();
- try {
- status = serialFilter.checkInput(new FilterValues(clazz, arrayLength,
- totalObjectRefs, depth, bytesRead));
- } catch (RuntimeException e) {
- // Preventive interception of an exception to log
- status = ObjectInputFilter.Status.REJECTED;
- ex = e;
- }
- if (status == null ||
- status == ObjectInputFilter.Status.REJECTED) {
- // Debug logging of filter checks that fail
- if (Logging.infoLogger != null) {
- Logging.infoLogger.info(
- "ObjectInputFilter {0}: {1}, array length: {2}, nRefs: {3}, depth: {4}, bytes: {5}, ex: {6}",
- status, clazz, arrayLength, totalObjectRefs, depth, bytesRead,
- Objects.toString(ex, "n/a"));
- }
- InvalidClassException ice = new InvalidClassException("filter status: " + status);
- ice.initCause(ex);
- throw ice;
- } else {
- // Trace logging for those that succeed
- if (Logging.traceLogger != null) {
- Logging.traceLogger.finer(
- "ObjectInputFilter {0}: {1}, array length: {2}, nRefs: {3}, depth: {4}, bytes: {5}, ex: {6}",
- status, clazz, arrayLength, totalObjectRefs, depth, bytesRead,
- Objects.toString(ex, "n/a"));
- }
- }
- }
- }
复制代码
serialFilter是在objectInputStream初始化时生成的当serialFilter存在时,会对信息流进行检测,如果没有通过返回报错 检查结束后,继续回到readNonProxyDesc() - private ObjectStreamClass readNonProxyDesc(boolean unshared)
- throws IOException
- {
- ...
- skipCustomData();
- try {
- totalObjectRefs++;
- depth++;
- desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false));
- } finally {
- depth--;
- }
- handles.finish(descHandle);
- passHandle = descHandle;
- return desc;
- }
复制代码
跟进skipCustmoData()
- private void skipCustomData() throws IOException {
- int oldHandle = passHandle;
- for (;;) {
- if (bin.getBlockDataMode()) {
- bin.skipBlockData();
- bin.setBlockDataMode(false);
- }
- switch (bin.peekByte()) {
- case TC_BLOCKDATA:
- case TC_BLOCKDATALONG:
- bin.setBlockDataMode(true);
- break;
- case TC_ENDBLOCKDATA:
- bin.readByte();
- passHandle = oldHandle;
- return;
- default:
- readObject0(Object.class, false);
- break;
- }
- }
- }
复制代码
跳过所有块数据和对象,直到遇到TC_ENDBLOCKDATA 继续分析readNonPorxyDesc,跟进initNonPorxy
- void initNonProxy(ObjectStreamClass model,
- Class<?> cl,
- ClassNotFoundException resolveEx,
- ObjectStreamClass superDesc)
- throws InvalidClassException
- {
- long suid = Long.valueOf(model.getSerialVersionUID());
- ObjectStreamClass osc = null;
- if (cl != null) {
- osc = lookup(cl, true);
- if (osc.isProxy) {
- throw new InvalidClassException(
- "cannot bind non-proxy descriptor to a proxy class");
- }
- if (model.isEnum != osc.isEnum) {
- throw new InvalidClassException(model.isEnum ?
- "cannot bind enum descriptor to a non-enum class" :
- "cannot bind non-enum descriptor to an enum class");
- }
- if (model.serializable == osc.serializable &&
- !cl.isArray() &&
- suid != osc.getSerialVersionUID()) {
- throw new InvalidClassException(osc.name,
- "local class incompatible: " +
- "stream classdesc serialVersionUID = " + suid +
- ", local class serialVersionUID = " +
- osc.getSerialVersionUID());
- }
- if (!classNamesEqual(model.name, osc.name)) {
- throw new InvalidClassException(osc.name,
- "local class name incompatible with stream class " +
- "name "" + model.name + """);
- }
- if (!model.isEnum) {
- if ((model.serializable == osc.serializable) &&
- (model.externalizable != osc.externalizable)) {
- throw new InvalidClassException(osc.name,
- "Serializable incompatible with Externalizable");
- }
- if ((model.serializable != osc.serializable) ||
- (model.externalizable != osc.externalizable) ||
- !(model.serializable || model.externalizable)) {
- deserializeEx = new ExceptionInfo(
- osc.name, "class invalid for deserialization");
- }
- }
- }
- this.cl = cl;
- this.resolveEx = resolveEx;
- this.superDesc = superDesc;
- name = model.name;
- this.suid = suid;
- isProxy = false;
- isEnum = model.isEnum;
- serializable = model.serializable;
- externalizable = model.externalizable;
- hasBlockExternalData = model.hasBlockExternalData;
- hasWriteObjectData = model.hasWriteObjectData;
- fields = model.fields;
- primDataSize = model.primDataSize;
- numObjFields = model.numObjFields;
- if (osc != null) {
- localDesc = osc;
- writeObjectMethod = localDesc.writeObjectMethod;
- readObjectMethod = localDesc.readObjectMethod;
- readObjectNoDataMethod = localDesc.readObjectNoDataMethod;
- writeReplaceMethod = localDesc.writeReplaceMethod;
- readResolveMethod = localDesc.readResolveMethod;
- if (deserializeEx == null) {
- deserializeEx = localDesc.deserializeEx;
- }
- domains = localDesc.domains;
- cons = localDesc.cons;
- }
- fieldRefl = getReflector(fields, localDesc);
- // reassign to matched fields so as to reflect local unshared settings
- fields = fieldRefl.getFields();
- initialized = true;
- }
复制代码
这个方法会使用readDesc来初始化Desc,所以开头先检查readDesc的正确性使用本地直接new的描述 与model也就是readDesc里的内容进行判断,如果不同就抛出错误
继续往下看initNonProxy()
把localDesc中的属性复制到当前描述的属性上,接着将这个return 给readClassDesc的validateDescriptor
- if (descriptor != null) {
- validateDescriptor(descriptor);
- }
复制代码
检查结束后,通过就return 到readOrdinaryObject
- private Object readOrdinaryObject(boolean unshared)
- throws IOException
- {
- ...
- ObjectStreamClass desc = readClassDesc(false);
- desc.checkDeserialize();
- ...
- Object obj;
- try {
- obj = desc.isInstantiable() ? desc.newInstance() : null;
- } catch (Exception ex) {
- throw (IOException) new InvalidClassException(
- desc.forClass().getName(),
- "unable to create instance").initCause(ex);
- }
- ...
- if (desc.isExternalizable()) {
- readExternalData((Externalizable) obj, desc);
- } else {
- readSerialData(obj, desc);
- }
- ...
- }
复制代码
看到对返回的desc进行反序列化检查checkDeserialize()
往下继续走 desc.newInstance()就是刚才的构造函数生成的组件 接着,当desc不是Externalizable()就会触发else的readSerialData - private void readSerialData(Object obj, ObjectStreamClass desc)
- throws IOException
- {
- ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
- for (int i = 0; i < slots.length; i++) {
- ObjectStreamClass slotDesc = slots[i].desc;
- if (slots[i].hasData) {
- if (obj == null || handles.lookupException(passHandle) != null) {
- defaultReadFields(null, slotDesc); // skip field values
- } else if (slotDesc.hasReadObjectMethod()) {
- ThreadDeath t = null;
- boolean reset = false;
- SerialCallbackContext oldContext = curContext;
- if (oldContext != null)
- oldContext.check();
- try {
- curContext = new SerialCallbackContext(obj, slotDesc);
- bin.setBlockDataMode(true);
- slotDesc.invokeReadObject(obj, this);
- } catch (ClassNotFoundException ex) {
- /*
- * In most cases, the handle table has already
- * propagated a CNFException to passHandle at this
- * point; this mark call is included to address cases
- * where the custom readObject method has cons'ed and
- * thrown a new CNFException of its own.
- */
- handles.markException(passHandle, ex);
- } finally {
- do {
- try {
- curContext.setUsed();
- if (oldContext!= null)
- oldContext.check();
- curContext = oldContext;
- reset = true;
- } catch (ThreadDeath x) {
- t = x; // defer until reset is true
- }
- } while (!reset);
- if (t != null)
- throw t;
- }
- /*
- * defaultDataEnd may have been set indirectly by custom
- * readObject() method when calling defaultReadObject() or
- * readFields(); clear it to restore normal read behavior.
- */
- defaultDataEnd = false;
- } else {
- defaultReadFields(obj, slotDesc);
- }
- ...
- }
复制代码
这里先进行判断,当我们没有重写readObject那么就调用defaultReadFilelds,否则调用slotDesc.invokeReadObject(obj,this)- void invokeReadObject(Object obj, ObjectInputStream in)
- throws ClassNotFoundException, IOException,
- UnsupportedOperationException
- {
- requireInitialized();
- if (readObjectMethod != null) {
- try {
- readObjectMethod.invoke(obj, new Object[]{ in });
- } catch (InvocationTargetException ex) {
- Throwable th = ex.getTargetException();
- if (th instanceof ClassNotFoundException) {
- throw (ClassNotFoundException) th;
- } else if (th instanceof IOException) {
- throw (IOException) th;
- } else {
- throwMiscException(th);
- }
- } catch (IllegalAccessException ex) {
- // should not occur, as access checks have been suppressed
- throw new InternalError(ex);
- }
- } else {
- throw new UnsupportedOperationException();
- }
- }
复制代码
读取我们重写的readObject可以看到readObjectMethod.invoke(obj, new Object[]{ in });
这里的readObjectMethod就是我们自写的readObject方法
readObject 执行流程
Java 反序列化之 readObject 分析
|