安全矩阵

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

Java 序列化 writeObject分析

[复制链接]

855

主题

862

帖子

2940

积分

金牌会员

Rank: 6Rank: 6

积分
2940
发表于 2021-8-2 09:16:53 | 显示全部楼层 |阅读模式
原文链接:Java 序列化 writeObject分析
接着上一篇Java readObject分析,这一篇我们来看一下writeObject知识点
先来了解几个序列化中常见的属性
serialVersionUID
也叫suid ,基于类的属性方法等参数会生成一个默认的suid,用来验证类是否发生变化,在readObject分析中讲到,会检查suid,如果发生变化,那么是不能反序列化的。
transient 修饰符
在之前提到序列化的条件中讲到过,实现java.io.Serializable的接口,才可被反序列化,并且所有属性都是可序列化的,而这里并不包括被trainsient修饰符修饰的属性,该属性是不参数序列化和反序列化的
static 关键字
序列化仅对特定的变量产生作用,但 static 修饰的变量并不特定于任何对象。因此,静态变量不会参与序列化。
writeObject 分析
例子 :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");
    }
}
在调用writeObject()进行序列化之前会调用ObjectOutputStream的构造函数,new 一个ObjectOutputStream对象
public ObjectOutputStream(OutputStream out) throws IOException {
    verifySubclass();
    bout = new BlockDataOutputStream(out);
    handles = new HandleTable(10, (float) 3.00);
    subs = new ReplaceTable(10, (float) 3.00);
    enableOverride = false;
    writeStreamHeader();
    bout.setBlockDataMode(true);
    if (extendedDebugInfo) {
        debugInfoStack = new DebugTraceInfoStack();
    } else {
        debugInfoStack = null;
    }
}
boot是底层的字节数据容器,接着实现writeStreamHeader()方法
protected void writeStreamHeader() throws IOException {
    bout.writeShort(STREAM_MAGIC);
    bout.writeShort(STREAM_VERSION);
}
写入文件头,魔数和版本
final static short STREAM_MAGIC = (short)0xaced;

/**
* Version number that is written to the stream header.
*/
final static short STREAM_VERSION = 5;
这个也是序列化文件的特征之一。
从ObjectOutputStream.writeObject 跟进代码
public final void writeObject(Object obj) throws IOException {
    if (enableOverride) {
        writeObjectOverride(obj);
        return;
    }
    try {
        writeObject0(obj, false);
    } catch (IOException ex) {
        if (depth == 0) {
            writeFatalException(ex);
        }
        throw ex;
    }
}
这里的enableOverride在新建ObjectOutputStream对象时实例化,如果为true,则调用writeObjectOverride()而不是writeObject()
writeObject0(obj, false); 跟进去
private void writeObject0(Object obj, boolean unshared)
    throws IOException
{
    ...
    try {
        // handle previously written and non-replaceable objects
        ...

        // check for replacement object
        Object orig = obj;
        Class<?> cl = obj.getClass();
        ObjectStreamClass desc;
        for (;;) {
            // REMIND: skip this check for strings/arrays?
            Class<?> repCl;
            desc = ObjectStreamClass.lookup(cl, true);
            if (!desc.hasWriteReplaceMethod() ||
                (obj = desc.invokeWriteReplace(obj)) == null ||
                (repCl = obj.getClass()) == cl)
            {
                break;
            }
            cl = repCl;
        }
        ...

        // if object replaced, run through original checks a second time
        ...

        // remaining cases
        if (obj instanceof String) {
            writeString((String) obj, unshared);
        } else if (cl.isArray()) {
            writeArray(obj, desc, unshared);
        } else if (obj instanceof Enum) {
            writeEnum((Enum<?>) obj, desc, unshared);
        } else if (obj instanceof Serializable) {
            writeOrdinaryObject(obj, desc, unshared);
        } else {
            if (extendedDebugInfo) {
                throw new NotSerializableException(
                    cl.getName() + "\n" + debugInfoStack.toString());
            } else {
                throw new NotSerializableException(cl.getName());
            }
        }
    } finally {
        depth--;
        bout.setBlockDataMode(oldMode);
    }
}
生成一个描述序列化对象的类元信息 的ObjectStreamClass对象,根据code 看出,obj如果是String,Array,Enum 直接调用默认方法进行序列化,如果实现了Serializable,则会调用writeOrdinaryObject
private void writeOrdinaryObject(Object obj,
                                 ObjectStreamClass desc,
                                 boolean unshared)
    throws IOException
{
    if (extendedDebugInfo) {
        debugInfoStack.push(
            (depth == 1 ? "root " : "") + "object (class \"" +
            obj.getClass().getName() + "\", " + obj.toString() + ")");
    }
    try {
        desc.checkSerialize();

        bout.writeByte(TC_OBJECT);
        writeClassDesc(desc, false);
        handles.assign(unshared ? null : obj);
        if (desc.isExternalizable() && !desc.isProxy()) {
            writeExternalData((Externalizable) obj);
        } else {
            writeSerialData(obj, desc);
        }
    } finally {
        if (extendedDebugInfo) {
            debugInfoStack.pop();
        }
    }
}
bout在ObjectOutputStream新建时已经提到 ,这是个底层字节数据容器,在代码中先写入了TC_OBJECT,表示这是一个新的Object 也就是在文件格式提到的0x73
final static byte TC_OBJECT =       (byte)0x73;
跟进writeClassDesc
private void writeClassDesc(ObjectStreamClass desc, boolean unshared)
    throws IOException
{
    int handle;
    if (desc == null) {
        writeNull();
    } else if (!unshared && (handle = handles.lookup(desc)) != -1) {
        writeHandle(handle);
    } else if (desc.isProxy()) {
        writeProxyDesc(desc, unshared);
    } else {
        writeNonProxyDesc(desc, unshared);
    }
}
将类描述符以流的方式写入,这里有几种方法:动态代理,普通方式。先检测传入的类描述符是否为null 不为空,调用writeNonProxyDesc()
private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared)
    throws IOException
{
    bout.writeByte(TC_CLASSDESC);
    handles.assign(unshared ? null : desc);

    if (protocol == PROTOCOL_VERSION_1) {
        // do not invoke class descriptor write hook with old protocol
        desc.writeNonProxy(this);
    } else {
        writeClassDescriptor(desc);
    }

    Class<?> cl = desc.forClass();
    bout.setBlockDataMode(true);
    if (cl != null && isCustomSubclass()) {
        ReflectUtil.checkPackageAccess(cl);
    }
    annotateClass(cl);
    bout.setBlockDataMode(false);
    bout.writeByte(TC_ENDBLOCKDATA);

    writeClassDesc(desc.getSuperDesc(), false);
}
首先写入一个字节的TC_CLASSDESC,表示这额是一个新的class 描述符,接着调用writeNonProxy
void writeNonProxy(ObjectOutputStream out) throws IOException {
    out.writeUTF(name);
    out.writeLong(getSerialVersionUID());

    byte flags = 0;
    if (externalizable) {
        flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
        int protocol = out.getProtocolVersion();
        if (protocol != ObjectStreamConstants.PROTOCOL_VERSION_1) {
            flags |= ObjectStreamConstants.SC_BLOCK_DATA;
        }
    } else if (serializable) {
        flags |= ObjectStreamConstants.SC_SERIALIZABLE;
    }
    if (hasWriteObjectData) {
        flags |= ObjectStreamConstants.SC_WRITE_METHOD;
    }
    if (isEnum) {
        flags |= ObjectStreamConstants.SC_ENUM;
    }
    out.writeByte(flags);

    out.writeShort(fields.length);
    for (int i = 0; i < fields.length; i++) {
        ObjectStreamField f = fields;
        out.writeByte(f.getTypeCode());
        out.writeUTF(f.getName());
        if (!f.isPrimitive()) {
            out.writeTypeString(f.getTypeString());
        }
    }
}
将类元信息写入,如:类名 name , 序列号版本 suid,类的标识 flags
我们 跟进out.writeUTF(name);看看如何写入name的所属信息的
void writeUTF(String s, long utflen) throws IOException {
    if (utflen > 0xFFFFL) {
        throw new UTFDataFormatException();
    }
    writeShort((int) utflen);
    if (utflen == (long) s.length()) {
        writeBytes(s);
    } else {
        writeUTFBody(s);
    }
}
调用writeUTF()方法写入对象所属类名,以UTF格式写入给定的属性,
回到上层代码,继续看,SUID的生成方式,跟进getSerialVersionUID
public long getSerialVersionUID() {
    // REMIND: synchronize instead of relying on volatile?
    if (suid == null) {
        suid = AccessController.doPrivileged(
            new PrivilegedAction<Long>() {
                public Long run() {
                    return computeDefaultSUID(cl);
                }
            }
        );
    }
    return suid.longValue();
}
若suid为空,就进入computeDefaultSUID(cl) 计算
会创建对象DataOutputStream将信息写入
private static long computeDefaultSUID(Class<?> cl) {
    if (!Serializable.class.isAssignableFrom(cl) || Proxy.isProxyClass(cl))
    {
        return 0L;
    }
   
    try {
        //创建DataOutputStream 将信息写入ByteArrayOutputStream中
        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        DataOutputStream dout = new DataOutputStream(bout);
                                // 写入类名
        dout.writeUTF(cl.getName());
                                // 写入 modifier
        int classMods = cl.getModifiers() &
            (Modifier.PUBLIC | Modifier.FINAL |
             Modifier.INTERFACE | Modifier.ABSTRACT);

        /*
         * compensate for javac bug in which ABSTRACT bit was set for an
         * interface only if the interface declared methods
         */
        Method[] methods = cl.getDeclaredMethods();
        if ((classMods & Modifier.INTERFACE) != 0) {
            classMods = (methods.length > 0) ?
                (classMods | Modifier.ABSTRACT) :
                (classMods & ~Modifier.ABSTRACT);
        }
        dout.writeInt(classMods);
                                // 按照interface name 排序写入
        if (!cl.isArray()) {
            /*
             * compensate for change in 1.2FCS in which
             * Class.getInterfaces() was modified to return Cloneable and
             * Serializable for array classes.
             */
            Class<?>[] interfaces = cl.getInterfaces();
            String[] ifaceNames = new String[interfaces.length];
            for (int i = 0; i < interfaces.length; i++) {
                ifaceNames = interfaces.getName();
            }
            Arrays.sort(ifaceNames);
            for (int i = 0; i < ifaceNames.length; i++) {
                dout.writeUTF(ifaceNames);
            }
        }
                                根据field拍下,把name,modifier,signature写入
        Field[] fields = cl.getDeclaredFields();
        MemberSignature[] fieldSigs = new MemberSignature[fields.length];
        for (int i = 0; i < fields.length; i++) {
            fieldSigs = new MemberSignature(fields);
        }
        Arrays.sort(fieldSigs, new Comparator<MemberSignature>() {
            public int compare(MemberSignature ms1, MemberSignature ms2) {
                return ms1.name.compareTo(ms2.name);
            }
        });
        // 当modifier为 privite,static transient 就不写入,
        for (int i = 0; i < fieldSigs.length; i++) {
            MemberSignature sig = fieldSigs;
            int mods = sig.member.getModifiers() &
                (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED |
                 Modifier.STATIC | Modifier.FINAL | Modifier.VOLATILE |
                 Modifier.TRANSIENT);
            if (((mods & Modifier.PRIVATE) == 0) ||
                ((mods & (Modifier.STATIC | Modifier.TRANSIENT)) == 0))
            {
                dout.writeUTF(sig.name);
                dout.writeInt(mods);
                dout.writeUTF(sig.signature);
            }
        }
                                //这里判断存在staticInitializer
        if (hasStaticInitializer(cl)) {
            dout.writeUTF("<clinit>");
            dout.writeInt(Modifier.STATIC);
            dout.writeUTF("()V");
        }
                                //根据signature排序,然后将非private的构造函数写入
        Constructor<?>[] cons = cl.getDeclaredConstructors();
        MemberSignature[] consSigs = new MemberSignature[cons.length];
        for (int i = 0; i < cons.length; i++) {
            consSigs = new MemberSignature(cons);
        }
        Arrays.sort(consSigs, new Comparator<MemberSignature>() {
            public int compare(MemberSignature ms1, MemberSignature ms2) {
                return ms1.signature.compareTo(ms2.signature);
            }
        });
        for (int i = 0; i < consSigs.length; i++) {
            MemberSignature sig = consSigs;
            int mods = sig.member.getModifiers() &
                (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED |
                 Modifier.STATIC | Modifier.FINAL |
                 Modifier.SYNCHRONIZED | Modifier.NATIVE |
                 Modifier.ABSTRACT | Modifier.STRICT);
            if ((mods & Modifier.PRIVATE) == 0) {
                dout.writeUTF("<init>");
                dout.writeInt(mods);
                dout.writeUTF(sig.signature.replace('/', '.'));
            }
        }
                                // 同样,method name 和signature 排序后,写入非private method的那name,modifier,signature
        MemberSignature[] methSigs = new MemberSignature[methods.length];
        for (int i = 0; i < methods.length; i++) {
            methSigs = new MemberSignature(methods);
        }
        Arrays.sort(methSigs, new Comparator<MemberSignature>() {
            public int compare(MemberSignature ms1, MemberSignature ms2) {
                int comp = ms1.name.compareTo(ms2.name);
                if (comp == 0) {
                    comp = ms1.signature.compareTo(ms2.signature);
                }
                return comp;
            }
        });
        for (int i = 0; i < methSigs.length; i++) {
            MemberSignature sig = methSigs;
            int mods = sig.member.getModifiers() &
                (Modifier.PUBLIC | Modifier.PRIVATE | Modifier.PROTECTED |
                 Modifier.STATIC | Modifier.FINAL |
                 Modifier.SYNCHRONIZED | Modifier.NATIVE |
                 Modifier.ABSTRACT | Modifier.STRICT);
            if ((mods & Modifier.PRIVATE) == 0) {
                dout.writeUTF(sig.name);
                dout.writeInt(mods);
                dout.writeUTF(sig.signature.replace('/', '.'));
            }
        }

        dout.flush();
                                // 最后把bout 做SHA 加密,取前8个byte 作为suid
        MessageDigest md = MessageDigest.getInstance("SHA");
        byte[] hashBytes = md.digest(bout.toByteArray());
        long hash = 0;
        for (int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--) {
            hash = (hash << 8) | (hashBytes & 0xFF);
        }
        return hash;
    } catch (IOException ex) {
        throw new InternalError(ex);
    } catch (NoSuchAlgorithmException ex) {
        throw new SecurityException(ex.getMessage());
    }
}
返回suid 通过writeLong方法写入
接下来,通过flags 来判断当前对象所属
flag
bit

SC_EXTERNALIZABLE0x04
SC_SERIALIZABLE0x02
SC_WRITE_METHOD0x01
SC_ENUM0x10
本例子中,flags = 0x02
for循环中,一次写入序列化对象的字段个数,字段的元数据,
TypeCode
Java Type

Bbyte
Cchar
Ddouble
Ffloat
Iint
Jlong
Lclass or interface
Sshort
Zboolean
[array
最后代码走进if判断 writeTypeString(f.getTypeString())
void writeTypeString(String str) throws IOException {
    int handle;
    if (str == null) {
        writeNull();
    } else if ((handle = handles.lookup(str)) != -1) {
        writeHandle(handle);
    } else {
        writeString(str, false);
    }
}
调用writeTypeString()方法写入代表对象或者类型字符串,最后调用writeString
private void writeString(String str, boolean unshared) throws IOException {
    handles.assign(unshared ? null : str);
    long utflen = bout.getUTFLength(str);
    if (utflen <= 0xFFFF) {
        bout.writeByte(TC_STRING);
        bout.writeUTF(str, utflen);
    } else {
        bout.writeByte(TC_LONGSTRING);
        bout.writeLongUTF(str, utflen);
    }
}
写入标志位TC_String,表示接下来的数据是字符串,然后通过writeUTF写入字符串
返回上层代码
private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared)
    throws IOException
{
                ...

    Class<?> cl = desc.forClass();
    bout.setBlockDataMode(true);
    if (cl != null && isCustomSubclass()) {
        ReflectUtil.checkPackageAccess(cl);
    }
    annotateClass(cl);
    bout.setBlockDataMode(false);
    bout.writeByte(TC_ENDBLOCKDATA);

    writeClassDesc(desc.getSuperDesc(), false);
}
接下来写入TC_ENDBLOCKDATA 表示对一个object的描述块结束
然后调用writeClassDesc()方法调用上层代码,形成递归调用,每次写入父类描述,直到没有父类可以被返回,也就是 传入的ObjectStreamClass对象为null时,会写入一个字节的标识位TC_NULL
回到writeOrdinaryObject()方法
private void writeOrdinaryObject(Object obj,
                                 ObjectStreamClass desc,
                                 boolean unshared)
    throws IOException
{
   ...
    try {
        desc.checkSerialize();

        ...
        if (desc.isExternalizable() && !desc.isProxy()) {
            writeExternalData((Externalizable) obj);
        } else {
            writeSerialData(obj, desc); // 写入序列化对象的实例数据
        }
    } finally {
        if (extendedDebugInfo) {
            debugInfoStack.pop();
        }
    }
}
writeOrdinaryObject()方法,先写入子类的类元数据,然后在递归调用写入父类的类元数据。
接下来进入writeSerialData()方法,写入被序列化的对象
private void writeSerialData(Object obj, ObjectStreamClass desc)
    throws IOException
{
    ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
    for (int i = 0; i < slots.length; i++) {
        ObjectStreamClass slotDesc = slots.desc;
        if (slotDesc.hasWriteObjectMethod()) {
                                                try{
                      ...
                slotDesc.invokeWriteObject(obj, this);
                      ...
            }
        } else {
            defaultWriteFields(obj, slotDesc);//调用默认的方法写入实例数据
        }
    }
}
对于没有自定义writeObject()方法,会调用defaultwriteFields()方法写入数据,
private void defaultWriteFields(Object obj, ObjectStreamClass desc)
    throws IOException
{
    Class<?> cl = desc.forClass();
    if (cl != null && obj != null && !cl.isInstance(obj)) {
        throw new ClassCastException();
    }

    desc.checkDefaultSerialize();
               
    int primDataSize = desc.getPrimDataSize();
    if (primVals == null || primVals.length < primDataSize) {
        primVals = new byte[primDataSize];
    }
    // 获取对应类中的基本数据类型的数据,并保存在primVals
    desc.getPrimFieldValues(obj, primVals);
    bout.write(primVals, 0, primDataSize, false);
                // 获取对应类的所有字段对象
    ObjectStreamField[] fields = desc.getFields(false);
    Object[] objVals = new Object[desc.getNumObjFields()];
    int numPrimFields = fields.length - objVals.length;
   
          desc.getObjFieldValues(obj, objVals);
    for (int i = 0; i < objVals.length; i++) {
        if (extendedDebugInfo) {
            debugInfoStack.push(
                "field (class \"" + desc.getName() + "\", name: \"" +
                fields[numPrimFields + i].getName() + "\", type: \"" +
                fields[numPrimFields + i].getType() + "\")");
        }
        try {
          // 对所有Object类型的字段递归调用writeObject0()方法写入对应的数据
            writeObject0(objVals,
                         fields[numPrimFields + i].isUnshared());
        } finally {
            if (extendedDebugInfo) {
                debugInfoStack.pop();
            }
        }
    }
}
这个方法,获取了对应类的基本类型的字段数据,写入底层的字节容器bout,获取对应类型的字段成员,递归writeObject0()写入对应的数据。


writeObject 方法图


回复

使用道具 举报

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

本版积分规则

小黑屋|安全矩阵

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

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

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