`

java反序列化的内部实现(二)

阅读更多

 

Java 反序列化

 

Java的反序列化,是序列化的逆向过程。基础类型的序列化比较简单,这里主要讲解对象的反序列化,从文件或网络中把字节序列读取出来,并解析字节序列中的对象类描述信息、以及对象成员的值信息,并生成一个新对象的过程。

 

先看下对象反序列化的参数:字节序列(字节数组)

这里的字节序列一般是通过上一篇java序列化的内部实现(一)》中序列化过程产生的;

当然如果有必要的话也可以手工拼装(这种场景比较少);

更多是对序列化参数的字节序列进行篡改,操控反序列端的对象生成(如:被攻击者攻击)。

 

一个简单的例子

 

Java的反序列化时通过实例化ObjectInputStream,并调用其readObject方法实现的。为了方便理解,这里的字节序列采用字节数组,方便后续手动篡改。

 

package com.sky.serial;
 
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
 
/**
 * Created by gantianxing on 2017/5/29.
 */
public class UserDemo {
   
private static final byte[] serialByteArray = new byte[]{
 (byte)0xAC,(byte)0xED,0x00,0x05,0x73,0x72,0x00,0x13,0x63,0x6F,0x6D,0x2E,0x73,0x6B,0x79,0x2E,0x73,0x65,0x72,0x69,0x61,0x6C,0x2E,0x55,0x73,0x65,0x72,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x00,0x03,0x49,0x00,0x03,0x73,0x65,0x78,0x4C,0x00,0x04,0x6E,0x61,0x6D,0x65,0x74,0x00,0x12,0x4C,0x6A,0x61,0x76,0x61,0x2F,0x6C,0x61,0x6E,0x67,0x2F,0x53,0x74,0x72,0x69,0x6E,0x67,0x3B,0x4C,0x00,0x08,0x70,0x68,0x6F,0x6E,0x65,0x4E,0x75,0x6D,0x71,0x00,0x7E,0x00,0x01,0x78,0x70,0x00,0x00,0x00,0x01,0x74,0x00,0x09,0x7A,0x68,0x61,0x6E,0x67,0x20,0x73,0x61,0x6E,0x74,0x00,0x0B,0x31,0x33,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38
    };
 
 
    public static void main(String[] args) throws Exception{
 
        //拼装字节序列
        InputStream is = new ByteArrayInputStream(serialByteArray);
        ObjectInputStream in = new ObjectInputStream(is);
 
        //反序列化
        Object newObj = in.readObject();
        System.out.println(newObj.getClass());
       
        //判断是否为User类型
        if(newObj instanceof User){
            User newUser = (User)newObj;
            System.out.println("user name:" + newUser.getName());
            System.out.println(newUser);
        }
    }
}

 

 

 

执行main方法,执行结果为:

 

class com.sky.serial.User
user name:zhang san
user info: name=zhang san,sex=1,phoneNum=13888888888

 

 

 

第一行打印信息 可以看到本次反序列化生成的对象,其实就是上一篇分享示例中的User类。

第二 三行打印信息,跟上一篇分享示例中打印内容相同,细心的朋友其实已经发现这里的字节数组,就是上一篇分享示例中序列化到文件user.txt中的内容:



  

 

篡改字节序列

 

这里我们模拟把“张三”的性别sex该为女性0,即在字节数组中把0x00,0x,00,0x00,0x01改为0x00,0x,00,0x00,0x00,代码如下:

 

package com.sky.serial;
 
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
 
/**
 * Created by gantianxing on 2017/5/29.
 */
public class UserDemo {
 
    private static final byte[] serialByteArray = new byte[]{
            (byte)0xAC,(byte)0xED,0x00,0x05,0x73,0x72,0x00,0x13,0x63,0x6F,0x6D,0x2E,0x73,0x6B,0x79,0x2E
            ,0x73,0x65,0x72,0x69,0x61,0x6C,0x2E,0x55,0x73,0x65,0x72,0x00,0x00,0x00,0x00,0x00
            ,0x00,0x00,0x01,0x02,0x00,0x03,0x49,0x00,0x03,0x73,0x65,0x78,0x4C,0x00,0x04,0x6E
            ,0x61,0x6D,0x65,0x74,0x00,0x12,0x4C,0x6A,0x61,0x76,0x61,0x2F,0x6C,0x61,0x6E,0x67
            ,0x2F,0x53,0x74,0x72,0x69,0x6E,0x67,0x3B,0x4C,0x00,0x08,0x70,0x68,0x6F,0x6E,0x65
            ,0x4E,0x75,0x6D,0x71,0x00,0x7E,0x00,0x01,0x78,0x70,0x00,0x00,0x00,0x00,0x74,0x00
            ,0x09,0x7A,0x68,0x61,0x6E,0x67,0x20,0x73,0x61,0x6E,0x74,0x00,0x0B,0x31,0x33,0x38
            ,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38
    };
 
 
    public static void main(String[] args) throws Exception{
 
        //拼装字节序列
        InputStream is = new ByteArrayInputStream(serialByteArray);
        ObjectInputStream in = new ObjectInputStream(is);
 
        //反序列化
        Object newObj = in.readObject();
        System.out.println(newObj.getClass());
 
        //判断是否为User类型
        if(newObj instanceof User){
            User newUser = (User)newObj;
            System.out.println("user name:" + newUser.getName());
            System.out.println(newUser);
        }
    }
}

 

 

执行main方法,打印信息如下:

 

class com.sky.serial.User
user name:zhang san
user info: name=zhang san,sex=0,phoneNum=13888888888

 

 

 

从第三行打印信息看出,张三的性别已经变为“女性”了。

 

我们从User类的定义中看到 sexfinal的(private final int sex;),即在第一次被构造方法初始化后,就不允许被改变。但反序列化会打破这个逻辑,如何防范这种篡改呢,我将在下一遍《java序列化用法以及理论》中进行讲解。本篇重点关注,java反序列化的内容实现。

 

Java反序列化内部实现

 

1Java的反序列化时通过实例化ObjectInputStream,并调用其readObject方法实现的。我们以ObjectInputStream的构造方法为起点进行源码解析:

    

public ObjectInputStream(InputStream in) throws IOException {
        verifySubclass(); //检查是否是ObjectInputStream的子类,这里不是
        bin = new BlockDataInputStream(in);//初始化输入流对象bin,后续通过bin.readxxx()方法读取字节信息
        handles = new HandleTable(10);//已读取的
        vlist = new ValidationList();
        enableOverride = false;
        readStreamHeader();//读取魔法数+版本号(AC ED),并进行检查
        bin.setBlockDataMode(true);
}

 

 

 

//检查 魔法数+版本号方法
protected void readStreamHeader()
        throws IOException, StreamCorruptedException
    {
        short s0 = bin.readShort(); //读取STREAM_MAGIC
        short s1 = bin.readShort();//读取 STREAM_VERSION
        if (s0 != STREAM_MAGIC || s1 != STREAM_VERSION) { //如果不匹配AC ED,直接抛出异常,反序列化失败
            throw new StreamCorruptedException(
                String.format("invalid stream header: %04X%04X", s0, s1));
        }
    }

 

 

 

构造方法,主要初始化工作包括:初始化输入流对象bin,后续通过bin.readxxx()方法读取字节信息;初始化handlesvlist;读取魔法数+版本号(AC ED),并进行检查,如果检查失败直接抛出异常,反序列化失败。

 

2、调用readObjet方法(Object newObj = in.readObject();)开始反序列化:

 

public final Object readObject()
        throws IOException, ClassNotFoundException
    {
        if (enableOverride) { //判断是否开启替换,这里没有
            return readObjectOverride();
        }
 
        // if nested read, passHandle contains handle of enclosing object
        int outerHandle = passHandle;
        try {
            Object obj = readObject0(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();
            }
        }
}

 

 

 

readObject0方法的主要工作流程:读取TC标记,判断TC标记类型,根据不同的类型,进行不同逻辑处理。

 

 

private Object readObject0(boolean unshared) throws IOException {
        boolean oldMode = bin.getBlockDataMode();
        if (oldMode) {
            int remain = bin.currentBlockRemaining();
            if (remain > 0) {
                throw new OptionalDataException(remain);
            } else if (defaultDataEnd) {
                /*
                 * Fix for 4360508: stream is currently at the end of a field
                 * value block written via default serialization; since there
                 * is no terminating TC_ENDBLOCKDATA tag, simulate
                 * end-of-custom-data behavior explicitly.
                 */
                throw new OptionalDataException(true);
            }
            bin.setBlockDataMode(false);
        }
 
        byte tc;
        while ((tc = bin.peekByte()) == TC_RESET) { //peek读取下一个字节,这个字节是个TC标记,并判断是不是TC_RESET
            bin.readByte();
            handleReset();
        }
 
        depth++; //该方法会被递归调用,记录入栈次数
        try {
            switch (tc) { //判断TC标记的类型
                case TC_NULL:空对象标记
                    return readNull();//读取空对象
 
                case TC_REFERENCE://引用已存在的TC标记
                    return readHandle(unshared); //读取引用对象
 
                case TC_CLASS: //class类型TC标记
                    return readClass(unshared);
 
                case TC_CLASSDESC: //普通类描述TC标记
                case TC_PROXYCLASSDESC: //代理类描述TC标记
                    return readClassDesc(unshared); //
                case TC_STRING: //string TC标记
                case TC_LONGSTRING: //长string TC标记
                    return checkResolve(readString(unshared)); 本例中第二次进入readObject0方法,为对象类型成员变量name字段赋值。
 
 
                case TC_ARRAY: //数组TC标记
                    return checkResolve(readArray(unshared));
 
                case TC_ENUM://枚举TC标记
                    return checkResolve(readEnum(unshared));
 
                case TC_OBJECT://新对象TC标记73,本实例第一次进入readObject0方法,会匹配到该处
                    return checkResolve(readOrdinaryObject(unshared));
 
                case TC_EXCEPTION://异常TC标记
                    IOException ex = readFatalException();
                    throw new WriteAbortedException("writing aborted", ex);
 
                case TC_BLOCKDATA:
                case TC_BLOCKDATALONG:
                    if (oldMode) {
                        bin.setBlockDataMode(true);
                        bin.peek();             // force header read
                        throw new OptionalDataException(
                            bin.currentBlockRemaining());
                    } else {
                        throw new StreamCorruptedException(
                            "unexpected block data");
                    }
 
                case TC_ENDBLOCKDATA://类描述结束TC标记
                    if (oldMode) {
                        throw new OptionalDataException(true);
                    } else {
                        throw new StreamCorruptedException(
                            "unexpected end of block data");
                    }
 
                default://没有找到匹配的 TC类型
                    throw new StreamCorruptedException(
                        String.format("invalid type code: %02X", tc));
            }
        } finally {
            depth--;
            bin.setBlockDataMode(oldMode);
        }
    }

 

 

阅读这个方法的关键是搞清楚,bin.peekByte()bin.readByte()区别,前者字节数组的index指针不会往后移,下次读取还是从原来的位置开始;后者数组的index会往后移一个字节,下次读取会从这个新位置开始。

本实例中第一次进入readObject0方法会匹配到 case TC_OBJECT: (新对象标记73),第二次进入readObject0方法会匹配到case TC_STRING:为name字段赋值,第三进入是为phoneNum字段赋值。

 

3、先看下第一次进入readObject0时,会先调用readOrdinaryObject方法,然后调用checkResolve方法checkResolve方法放到最后解释):

 

private Object readOrdinaryObject(boolean unshared)
        throws IOException
    {
        if (bin.readByte() != TC_OBJECT) {//读取出TC标记,并验证是否是新对象标记
            throw new InternalError();
        }
 
        ObjectStreamClass desc = readClassDesc(false);//根据对象的格式,下一步是读取类描述信息
        desc.checkDeserialize();
 
        Class<?> cl = desc.forClass(); //读取Class:com.sky.serial.User
        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; //通过类描述信息,初始化对象obj,注意不是通过调用构造方法初始化
        } catch (Exception ex) {
            throw (IOException) new InvalidClassException(
                desc.forClass().getName(),
                "unable to create instance").initCause(ex);
        }
 
        passHandle = handles.assign(unshared ? unsharedMarker : obj);//把对象obj放入handles map中,以便后续直接引用。
        ClassNotFoundException resolveEx = desc.getResolveException();
        if (resolveEx != null) {
            handles.markException(passHandle, resolveEx);
        }
 
        if (desc.isExternalizable()) {//判断是否实现Externalizable接口
            readExternalData((Externalizable) obj, desc); //最终调用对象的实现的readExternal方法,反序列化,为obj成员变量赋值
        } else {
            readSerialData(obj, desc);//调用默认方法为obj成员变量赋值
        }
 
        handles.finish(passHandle);
 
        if (obj != null &&
            handles.lookupException(passHandle) == null &&
            desc.hasReadResolveMethod()) //判断对象是否实现readResolve方法
        {
            Object rep = desc.invokeReadResolve(obj);//反射调用对象的readResolve方法
            if (unshared && rep.getClass().isArray()) {
                rep = cloneArray(rep);
            }
            if (rep != obj) {//如果对象readResolve返回的对象与 默认序列化对象不等,返回readResolve方法返回的对象
                handles.setObject(passHandle, obj = rep);
            }
        }
 
        return obj;
    }
 
   

 

 

  该方法会先调用readClassDesc获取对象的类描述信息,然后调用newInstance方法实例化对象obj,注意此处会生成一个空Obj(User类型)对象(成员变量值为空或者默认初始值),不是通过调用构造方法,而是JVM底层实现。然后调用readSerialData方法为空对象的成员变量赋值。

 

   newInstance方法

 

public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, null, modifiers);
            }
        }
        if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
        ConstructorAccessor ca = constructorAccessor;   // read volatile
        if (ca == null) {
            ca = acquireConstructorAccessor();
        }
        @SuppressWarnings("unchecked")
        T inst = (T) ca.newInstance(initargs);//此处会生成一个User空对象,不是通过调用构造方法,而是JVM底层实现。
        return inst;
    }

 

 

 4、调用readClassDesc方法,根据本例中的TC信息会调用readNonProxyDesc方法获取对象的类描述信息:

 

 

private ObjectStreamClass readClassDesc(boolean unshared)
        throws IOException
    {
        byte tc = bin.peekByte(); //peek读取TC标记
        switch (tc) {
            case TC_NULL:
                return (ObjectStreamClass) readNull();
 
            case TC_REFERENCE:
                return (ObjectStreamClass) readHandle(unshared);
 
            case TC_PROXYCLASSDESC:
                return readProxyDesc(unshared);
 
            case TC_CLASSDESC:
                return readNonProxyDesc(unshared); //本实例中会首先匹配到这里,开始读取类描述信息
 
            default:
                throw new StreamCorruptedException(
                    String.format("invalid type code: %02X", tc));
        }
}
 
private ObjectStreamClass readNonProxyDesc(boolean unshared)
        throws IOException
    {
        if (bin.readByte() != TC_CLASSDESC) {//读取TC标记,并验证是否是类描述TC标记
            throw new InternalError();
        }
 
        ObjectStreamClass desc = new ObjectStreamClass();
        int descHandle = handles.assign(unshared ? unsharedMarker : desc);//放入Handles map中,以便,后续遇到相同类型直接引用。
        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();//检查是否是ObjectInputStream的自定义子类,通过classLoader判断,自定义的classLoader为 App
        try {
            if ((cl = resolveClass(readDesc)) == null) {//通过反射,反射出对象类型
                resolveEx = new ClassNotFoundException("null class");
            } else if (checksRequired) {
                ReflectUtil.checkPackageAccess(cl);
            }
        } catch (ClassNotFoundException ex) {
            resolveEx = ex;
        }
        skipCustomData(); //跳过类描述结束符
 
        desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false));
 
        handles.finish(descHandle);
        passHandle = descHandle;
        return desc;//返回类描述信息
    }
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: //本实例会匹配到这里:78, 类描述读取结束
                    bin.readByte();
                    passHandle = oldHandle;
                    return;
 
                default:
                    readObject0(false);
                    break;
            }
        }
    }

 

 

5、真正读取类描述信息的方法是readClassDescriptor,所有的类信息、成员描述信息都会在本方法中完成,最终返回一个类描述信息对象ObjectStreamClass 

 

 

protected ObjectStreamClass readClassDescriptor()
        throws IOException, ClassNotFoundException
    {
        ObjectStreamClass desc = new ObjectStreamClass();
        desc.readNonProxy(this);
        return desc;
}
 
调用ObjectStreamClass类的readNonProxy方法进行类描述信息读取:
 
void readNonProxy(ObjectInputStream in)
        throws IOException, ClassNotFoundException
    {
        name = in.readUTF(); //读取类名,这里为“com.sky.serial.User”
        suid = Long.valueOf(in.readLong());//读取SUID,这里为1L
        isProxy = false;
 
        byte flags = in.readByte();//读取下一个字节,这里为02,表示该类实现了serializable接口
        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; // 这里serializable赋值为true
        isEnum = ((flags & ObjectStreamConstants.SC_ENUM) != 0);// 这里isEnum 本次赋值为false,User类不是范型
        if (isEnum && suid.longValue() != 0L) {//说明范型不能有非0的suid
            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(); //读取成员变量type,这里的三个成员类型分别为:I(int),L(String),L(String)
            String fname = in.readUTF();//读取成员变量名称这里分别为:sex、name、phoneNum
            String signature = ((tcode == 'L') || (tcode == '[')) ?
                in.readTypeString() : new String(new char[] { tcode });//如果成员变量是对象(L)或者数组([), 还需通过in.readTypeString()获取真实类型
            try {
                fields[i] = new ObjectStreamField(fname, signature, false);//调用ObjectStreamField构造方法生成成员变量描述对象
            } catch (RuntimeException e) {
                throw (IOException) new InvalidClassException(name,
                    "invalid descriptor for field " + fname).initCause(e);
            }
        }
        computeFieldOffsets();//对primDataSize、numObjFields赋值
    }
 
ObjectStreamField构造方法:
 
ObjectStreamField(String name, String signature, boolean unshared) {
        if (name == null) {
            throw new NullPointerException();
        }
        this.name = name;
        this.signature = signature.intern();
        this.unshared = unshared;
        field = null;
 
        switch (signature.charAt(0)) {
            case 'Z': type = Boolean.TYPE; break;
            case 'B': type = Byte.TYPE; break;
            case 'C': type = Character.TYPE; break;
            case 'S': type = Short.TYPE; break;
            case 'I': type = Integer.TYPE; break;
            case 'J': type = Long.TYPE; break;
            case 'F': type = Float.TYPE; break;
            case 'D': type = Double.TYPE; break;
            case 'L':
            case '[': type = Object.class; break;
            default: throw new IllegalArgumentException("illegal signature");
        }
    }

 

 

 

该类的所有成员描述信息解析结束。回到readNonProxyDesc方法,继续判断是否有父类,如果有继续解析父类描述信息,否则开始解析成员变量的值信息,为每个成员赋值。

 

6、在readOrdinaryObject方法中根据类描述信息初始化对象obj成功后,开始为成员变量赋值,这里调用的readSerialData方法:

 

private void readSerialData(Object obj, ObjectStreamClass desc)
        throws IOException
    {
        ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();//这步获取对象父子关系拓扑结构,这里User没有直接父类(Object除外),只需要对User对象进行赋值。
        for (int i = 0; i < slots.length; i++) {//遍历拓扑结构,这里slots只有一个User对象。
            ObjectStreamClass slotDesc = slots[i].desc;//User对象的类描述信息
 
            if (slots[i].hasData) {
                if (obj == null || handles.lookupException(passHandle) != null) {
                    defaultReadFields(null, slotDesc); // skip field values
                } else if (slotDesc.hasReadObjectMethod()) {//判断是否有readObject方法,如果有通过反射调用对象的readObject方法为成员变量赋值
                    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 {
                        curContext.setUsed();
                        if (oldContext!= null)
                            oldContext.check();
                        curContext = oldContext;
                    }
 
                    /*
                     * 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);
                }
 
                if (slotDesc.hasWriteObjectData()) {
                    skipCustomData();
                } else {
                    bin.setBlockDataMode(false);
                }
            } else {
                if (obj != null &&
                    slotDesc.hasReadObjectNoDataMethod() &&
                    handles.lookupException(passHandle) == null)
                {
                    slotDesc.invokeReadObjectNoData(obj);//调用对象的readObjectNoData方法进行赋值
                }
            }
        }
    }

 

 

 

为对象成员变量赋值的默认方法defaultReadFields

 

 

private void defaultReadFields(Object obj, ObjectStreamClass desc)
        throws IOException
    {
        Class<?> cl = desc.forClass();
        if (cl != null && obj != null && !cl.isInstance(obj)) {
            throw new ClassCastException();
        }
 
        int primDataSize = desc.getPrimDataSize();
        if (primVals == null || primVals.length < primDataSize) {
            primVals = new byte[primDataSize];
        }
        bin.readFully(primVals, 0, primDataSize, false);
        if (obj != null) {
            desc.setPrimFieldValues(obj, primVals);//为prim类型赋值(如,int char等基础类型)
        }
 
        int objHandle = passHandle;
        ObjectStreamField[] fields = desc.getFields(false);
        Object[] objVals = new Object[desc.getNumObjFields()];
        int numPrimFields = fields.length - objVals.length;
        for (int i = 0; i < objVals.length; i++) {//获取对象类型成员变量值数组(如,String、数组、自定义对象等)
            ObjectStreamField f = fields[numPrimFields + i];
            objVals[i] = readObject0(f.isUnshared()); //递归调用readObject0为对象赋值,这里为String,见readObject0方法注释
            if (f.getField() != null) {
                handles.markDependency(objHandle, passHandle);
            }
        }
        if (obj != null) {
            desc.setObjFieldValues(obj, objVals); //为对象成员变量赋值
        }
        passHandle = objHandle;
    }

 

 

 

7、为基础类型赋值方法setPrimFieldValues,这里只有一个基础类型int型的sex成员变量:

 

 

void setPrimFieldValues(Object obj, byte[] buf) {
            if (obj == null) {
                throw new NullPointerException();
            }
            for (int i = 0; i < numPrimFields; i++) {
                long key = writeKeys[i];
                if (key == Unsafe.INVALID_FIELD_OFFSET) {
                    continue;           // discard value
                }
                int off = offsets[i];
                switch (typeCodes[i]) {
                    case 'Z':
                        unsafe.putBoolean(obj, key, Bits.getBoolean(buf, off));
                        break;
 
                    case 'B':
                        unsafe.putByte(obj, key, buf[off]);
                        break;
 
                    case 'C':
                        unsafe.putChar(obj, key, Bits.getChar(buf, off));
                        break;
 
                    case 'S':
                        unsafe.putShort(obj, key, Bits.getShort(buf, off));
                        break;
 
                    case 'I':
                        unsafe.putInt(obj, key, Bits.getInt(buf, off));//从字节数组buf中读取数据,为对象的sex成员变量赋值,并移动字节数组下标,类似in.read()方法。注意不是调用setxxx方法赋值,是有jvm底层实现,看不到源码。
                        break;
 
                    case 'F':
                        unsafe.putFloat(obj, key, Bits.getFloat(buf, off));
                        break;
 
                    case 'J':
                        unsafe.putLong(obj, key, Bits.getLong(buf, off));
                        break;
 
                    case 'D':
                        unsafe.putDouble(obj, key, Bits.getDouble(buf, off));
                        break;
 
                    default:
                        throw new InternalError();
                }
            }
        }

 

 

 

8、为对象类型成员变量赋值:递归调用readObject0,匹配到TC_STRING, 调用checkResolve(readString(unshared));name、phoneNum字段赋值。

 

 

private String readString(boolean unshared) throws IOException {
        String str;
        byte tc = bin.readByte();//读取TC标记
        switch (tc) {
            case TC_STRING:
                str = bin.readUTF(); //匹配到这里,读取String内容,返回                break;
 
            case TC_LONGSTRING:
                str = bin.readLongUTF();
                break;
 
            default:
                throw new StreamCorruptedException(
                    String.format("invalid type code: %02X", tc));
        }
        passHandle = handles.assign(unshared ? unsharedMarker : str); //放入handles map,
        handles.finish(passHandle);
        return str;
    }
 
为对象类型的成员变量赋值,注意不是调用的setxx方法赋值,而是jvm底层实现赋值。
void setObjFieldValues(Object obj, Object[] vals) {
            if (obj == null) {
                throw new NullPointerException();
            }
            for (int i = numPrimFields; i < fields.length; i++) {
                long key = writeKeys[i];
                if (key == Unsafe.INVALID_FIELD_OFFSET) {
                    continue;           // discard value
                }
                switch (typeCodes[i]) {
                    case 'L':
                    case '[':
                        Object val = vals[offsets[i]];
                        if (val != null &&
                            !types[i - numPrimFields].isInstance(val))
                        {
                            Field f = fields[i].getField();
                            throw new ClassCastException(
                                "cannot assign instance of " +
                                val.getClass().getName() + " to field " +
                                f.getDeclaringClass().getName() + "." +
                                f.getName() + " of type " +
                                f.getType().getName() + " in instance of " +
                                obj.getClass().getName());
                        }
                        unsafe.putObject(obj, key, val);//调用jvm底层实现为对象类型成员变量赋值
                        break;
 
                    default:
                        throw new InternalError();
                }
            }
        }

 

 

 

 

9、checkResolve方法:最后看下checkResolve方法,在使用自定义并继承ObjectInputStream类的对象进行反序列化时,可以开启enableResolve=true,并重写resolveObject方法,并用resolveObject方法返回的对象替换默认序列化对象返回:

 

private Object checkResolve(Object obj) throws IOException {
        if (!enableResolve || handles.lookupException(passHandle) != null) {
            return obj;//本示例中 到这步返回
        }
        Object rep = resolveObject(obj);//调用子类实现的resolveObject方法
        if (rep != obj) {//如果与序列化的对象不相等,则进行替换
            handles.setObject(passHandle, rep);
        }
        return rep;
    }

 

 

 

到这里整个反序列化流程结束。

 

简单总结下,对象的反序列化过程跟序列化过程类似,分两步完成:

   第一步读取类描述信息,并根据这些信息通过jvm底层实例化一个指定类型的空对象;

   第二步读取成员变量值信息对这个空对象的成员变量进行赋值,成员变量分为基础类型、对象类型,基础类型直接赋值,对象类型继续递归调用readObject0方法进行赋值,这里的赋值并不是调用的setter方法,而是调用jvm底层实现的方法进行赋值。

 

   可见对象的反序列化过程,可以不调用对象的构造方法,以及setter方法就可以完成新对象的创建,并对成员变量进行赋值。

 

其他序列化和反序列化方式

 

  本篇对象反序列化和前一篇对象序列化的示例中,都是采用默认实现Serializable接口的方式,调用默认的序列化和反序列化方法进行分析。源码中我们可以看到还有很多其他方法不在实例中体现,比如:自定义序列化方法readObjectwriteObject;实现Externalizable序列化方式的readExternalwriteExternalreadResolve替换方法;readObjectNoDataMethod方法;子类扩展的替换方法resolveObjectreplaceObject方法;以及Enum枚举类型序列化。这些方法源码处理逻辑在以上注释中都有体现,下一篇主要讲解这些方法的区别和适用场景。

 

   对象的序列化和发序列化讲解完毕,基础类型的序列化和反序列化比较简单,直接调用out.writexxx完成序列化,调用in.readxxx完成反序列化(比如out.writeInt()in.readInt())。其实对象的序列化最终也是转化为对基础类型的序列化,但是为了标记他们在对象里的关系,添加了很多TC标记而已。

1
0
分享到:
评论

相关推荐

    java反序列化利用工具

    Java反序列化是一种将已序列化的对象状态转换回对象的过程,通常用于持久化数据或在网络间传输对象。在Java中,这一过程涉及到`java.io.ObjectInputStream`类,它能够读取由`java.io.ObjectOutputStream`写入的字节...

    jackson库实现定制化的java序列化反序列化操作

    标题中的“jackson库实现定制化的java序列化反序列化操作”指的是利用Jackson库的能力,通过自定义规则来控制对象的序列化和反序列化过程。这通常涉及到创建自定义的`JsonSerializer`和`JsonDeserializer`,或者使用...

    java序列化实现演示

    这个过程包括两个主要操作:序列化(将对象转换为字节流)和反序列化(将字节流恢复为对象)。在Java中,如果一个类需要支持序列化,它应该实现`java.io.Serializable`接口。 在Java序列化过程中,`...

    hessian学习基础篇——序列化和反序列化

    Hessian反序列化过程接收二进制流,并根据其定义解析成Java对象。这个过程对于跨语言通信特别有用,因为它能将一个语言的对象转换为另一种语言可以理解的格式。 在实际应用中,Hessian常用于构建轻量级的Web服务,...

    Java中的序列化与反序列化.pdf

    虽然实现`Serializable`接口能够方便地进行序列化和反序列化,但需要注意的是,序列化会暴露对象的内部状态,可能引发安全问题。此外,如果一个类包含不希望被序列化的字段,可以通过添加`transient`关键字来标记...

    java中的序列化与反序列化

    Java中的序列化与反序列化是Java编程语言中用于处理对象状态持久化和网络传输的重要技术。序列化是指将一个Java对象转换为字节流的过程,这样就可以将对象的状态保存到磁盘上或者通过网络传输。反序列化则是将字节流...

    Protocol Buffer序列化对比Java序列化.

    - Protocol Buffer:PB在序列化和反序列化时通常比Java序列化更快,因为它的数据格式更加紧凑,且解析算法优化。 - Java序列化:Java序列化虽然方便,但生成的数据量较大,且序列化和反序列化速度相对较慢。 2. ...

    Java 序列化和反序列化实例详解

    在 Java 中,实现反序列化的接口是 Serializable,该接口没有任何方法,但是它规定了一个类必须实现的格式,以便能够被反序列化。 在我们的示例中,我们使用了 ByteArrayInputStream 将字节流读取出来,并将其恢复...

    【技术分享】二进制角度构造Java反序列化Payload .pdf

    【技术分享】二进制角度构造Java反序列化Payload的主题主要涉及Java安全领域的漏洞分析与利用,特别是针对Java反序列化漏洞的构造方法。反序列化漏洞常常出现在企业级应用中,因为Java对象序列化机制在传递和存储...

    Java中对象序列化与反序列化详解

    本文将深入探讨Java中的对象序列化与反序列化的概念、原理、实现方法以及相关的注意事项。 **一、对象序列化的概念和作用** 对象序列化是将一个Java对象转换成字节流的过程,这个字节流可以存储在磁盘上,也可以在...

    Java中Tree的序列化

    2. **版本控制**:使用`serialVersionUID`字段进行版本控制,以防在不同版本的类之间进行序列化和反序列化时出现问题。 3. **非序列化成员**:有时我们不希望某些成员被序列化,可以使用`transient`关键字标记这些...

    java 序列化

    Java序列化不仅适用于Java对象,还可以通过反序列化过程将这些字节流重新构建回原始对象。 Java中实现序列化的接口是`java.io.Serializable`,任何类如果实现了这个接口,那么它的实例就可以被序列化。这个接口没有...

    java 序列化 将实体类本地序列化

    反序列化时,Java会尝试恢复对象的状态,包括它的字段值。 然而,需要注意的是,序列化可能会带来安全风险,因为恶意用户可能利用序列化的漏洞进行攻击。因此,除非必要,应避免序列化敏感数据。同时,如果你的类...

    序列化和反序列化1

    总结来说,Java的序列化和反序列化是通过`Serializable`接口和相关机制实现的,它对于数据持久化、网络通信和跨进程通信至关重要。通过自定义序列化和反序列化方法,我们可以更好地控制对象的存储和恢复过程,确保...

    Java对象序列化

    - **序列化工具类**:Java提供了`java.io.ObjectOutputStream`和`java.io.ObjectInputStream`两个类来进行对象的序列化和反序列化操作。 ##### ObjectOutputStream - **构造函数**:需要传入一个`OutputStream`...

    JAVA及Jackson反序列化漏洞.docx

    【JAVA反序列化原理】 Java反序列化是将已序列化的字节流恢复为原始Java对象的过程。这一过程涉及到`java.io.ObjectInputStream`类的`readObject()`方法,该方法能够从输入流中读取字节序列并将其转化为对象。为了...

    如何正确的使用Java序列化技术

    - **实现 `writeObject()` 和 `readObject()` 方法**:可以通过实现这两个方法来自定义序列化和反序列化的逻辑。 #### 3.2 类加载机制 序列化过程中,涉及到的对象可能需要通过类加载器动态加载。Java的类加载机制...

    redis使用过程中由于序列化工具引起的问题

    5. **安全**:序列化漏洞可能导致安全问题,例如反序列化攻击。 因此,选择合适的序列化工具至关重要。对于Java对象,推荐使用高效的JSON序列化库,如Jackson或Gson,它们支持配置以优化性能和数据大小。同时,确保...

    03-03-02-序列化和反序列化1

    总结来说,Java的序列化和反序列化机制是实现对象在网络间传输的重要工具,而通过Socket进行对象传输时需要确保对象是可序列化的,并处理好版本控制和安全性问题。在实际开发中,我们需要根据具体需求选择合适的序列...

Global site tag (gtag.js) - Google Analytics