java序列化基础知识

java序列化基础知识

Java反序列化漏洞专题-基础篇(21/09/05更新类加载部分)_哔哩哔哩_bilibili

随便写个类

image-20220410173136861

直接对他System.out.println(test);的话是这个效果

image-20220410173215043

为了更加直观,我们给他加个魔术方法__tostring

    public String toString(){
        System.out.println("OneClass{");
        System.out.println("public apple:"+this.apple);
        System.out.println("static banana:"+this.banana);
        System.out.println("protected pear:"+this.pear);
        System.out.println("private grapes:"+this.grapes);
        System.out.println("public transient gold:"+this.gold);
        return "}";
    }

new一个试试

image-20220410174549222

序列化反序列化都需要编写如下的函数

public  static  void  serialize(Object obj) throws IOException {
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
    oos.writeObject(obj);
}
//用于序列化
public  static  Object  unserialize(String Filename) throws IOException, ClassNotFoundException {
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
    Object obj = ois.readObject();
    return obj;
}
//用于反序列化

调用

serialize(test);
unserialize("ser.bin");

被序列化的类必须继承序列化接口

测试代码

image-20220410174455061

继承后:

public class OneClass  implements Serializable

image-20220410180112629

注意的点:

  • 静态的属性不可以被序列化
  • transeint关键字修饰的不可以被序列化

测试代码

OneClass.java

package org.apache.commons.collections;

import java.io.Serializable;

public class OneClass  implements Serializable
{
    public String apple;
    public static String banana;
    protected String pear;
    private String grapes;
    public transient String gold;

    public OneClass(){

    }

    public OneClass(String apple,String bannana,String pear,String grapes,String gold){
        this.apple = apple;
        this.banana = bannana;
        this.pear = pear;
        this.grapes = grapes;
        this.gold = gold;
    }
    public String toString(){
        System.out.println("OneClass{");
        System.out.println("public apple:"+this.apple);
        System.out.println("static banana:"+this.banana);
        System.out.println("protected pear:"+this.pear);
        System.out.println("private grapes:"+this.grapes);
        System.out.println("public transient gold:"+this.gold);
        return "}";
    }

}

ccone.java

package org.apache.commons.collections;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.rmi.CORBA.Tie;
import java.io.*;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class ccone {
    public  static  void  serialize(Object obj) throws IOException {
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        oos.writeObject(obj);
    }

    public  static  Object  unserialize(String Filename) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(Filename));
        Object obj = ois.readObject();
        return obj;
    }

    public static void main(String[] args) throws Exception {
        OneClass test = new OneClass("apple1", "banana2", "pear3", "grapes4", "gold");
//        System.out.println(test);
        serialize(test);
        System.out.println(unserialize("ser.bin"));

    }
}

image-20220410182758131

但是这个static好像赋值附上去了,不懂

transient如果想要赋值的话,想要让他反序列化后能出现我们原本赋予的值,则需要重写readObject

参考vnctf的easyJava

image-20220410181801262

进一步理解序列化反序列化

ObjectOutputStream.writeObject

image-20220421203711307

writeObject大致内容就是:

  • 读取该对象及其超类顶层的信息
  • 然后从最顶层超类向下输出对象实例实际数据值

image-20220421204410475

ObjectOutputStream构造方法

image-20220421204441276

  • bout写入一些类元数据还有对象中基本数据类型的值,在下面会分析

  • enableOverride 若是false,说明不支持重写序列化过程,一般用不上

  • writeStreamHeader 写入头信息

image-20220421205456643

①STREAM_MAGIC 声明使用了序列化协议,bout 就是一个流,将对应的头数据写入该流中

②STREAM_VERSION 指定序列化协议版本

调试一下

ObjectOutputStream.writeObject

image-20220421205845429

enableOverride是false所以来到这

代码很长,借用miku的简化代码

private void writeObject0(Object obj, boolean unshared)
    throws IOException
{
    ...
    try {

        Object orig = obj;
        Class<?> cl = obj.getClass();
        ObjectStreamClass desc;

        //①
        desc = ObjectStreamClass.lookup(cl, true);
        ...
        //②
        if (obj instanceof Class) {
            writeClass((Class) obj, unshared);
        } else if (obj instanceof ObjectStreamClass) {
            writeClassDesc((ObjectStreamClass) obj, unshared);
        // END Android-changed:  Make Class and ObjectStreamClass replaceable.
        } else 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());
            }
        }
    } 
    ...
}

有很多个lookup

  • ObjectStreamClass.lookup(cl, true);

它读到了blackcat,也就是一开始继承序列化接口这个类

image-20220421210653328

ObjectStreamClass.lookup(cl, true) 和 throw new NotSerializableException

② 处根据 obj 的类型去执行序列化操作,如果不符合序列化要求,那么会③位置抛出 NotSerializableException 异常。 最后进入 writeOrdinaryObject

image-20220421211341843

writeOrdinaryObject

步入后

bout.writeByte 写入类的元数据, TC_OBJECT . 声明这是一个新的对象,如果写入的是一个 String 类型的数据,那么就需要 TC_STRING 这个标识 writeClassDesc 方法主要作用就是自上而下 (从父类写到子类,注意只会遍历那些实现了序列化接口的类) 写入描述信息。该方法内部会不断的递归调用 desc.isExternalizable 判断需要序列化的对象是否实现了 Externalizable 接口。如果有,那么序列化的过程就会由程序员自己控制, writeExternalData 方法会回调,在这里就可以写需要序列化的数据 writeSerialData : 在没有实现 Externalizable 接口时,就执行这个方法

image-20220421211539050

ObjectOutputstream.writeSerialData

image-20220421213226721

image-20220421213139802

接着

image-20220421213606452

可以看到slotDesc是BlackCat

slotDesc.invokeWriteObject

image-20220421213919153

这里面也有个writeObjectMethod.invoke(obj, new Object[]{ out });

整条链子很长有很多个invoke

Method.invoke

MethodAccessor.invoke

DelegatingMethodAccessorImpl.invoke

NativeMethodAccessorImpl.invoke

defaultWriteFields 这个方法就是 JVM 自动帮我们序列化了 Serialzable 序列化的源码分析就完成了

readObject

基本上是对称的

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇