反序列化底層學習

nn0nkeyk1n9發表於2024-06-25

反序列化底層學習

前言

以前也是懶得學,覺得沒有必要,學到現在發現好多東西都需要學習java的底層,而且很多漏洞都是透過反序列化底層挖出來的,比如weblogic的一些繞過,我這裡也主要是為了學習weblogic來學習的,所以一些細節不關注

DEMO

import java.io.*;
public class Main {
    public static class Demo implements Serializable {
        transient String name;
        public Demo(String name) {
            this.name = name;
        }
        public static void main(String[] args) throws IOException, ClassNotFoundException {
            Demo demo = new Demo("nn0nkey");
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("panda.out"));
            outputStream.writeObject(demo);
            outputStream.close();
            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("panda.out"));
            inputStream.readObject();

        }
    }
}

反序列化分析

一般我們就知道反序列化會呼叫readobject方法,現在就好好分析一下readobject幹了啥

首先判斷是不是過載型別的,如果不是呼叫Object obj = readObject0(false);去恢復物件

image-20240625165822881

readObject0方法會對不同的型別進行不同的處理,我這裡是object型別的

image-20240625170446323

readOrdinaryObject(unshared)我們繼續看看是怎麼處理的

內部呼叫ObjectStreamClass desc = readClassDesc(false);去讀取我們的序列化流的描述資訊,跟進

可以看到也是按照不同的型別去處理,有null,引用型別,代理型別,class型別

image-20240625170657019

我們進入到class型別

return readNonProxyDesc(unshared);

先進行一個檢查是不是class型別,然後例項化一個desc物件,desc的資訊是透過readClassDescriptor讀取的

image-20240625170852928

跟進readClassDescriptor

image-20240625171409862

跟進readNonProxy

這個方法是真正意義上的操作

系統會先從位元組流中讀取類名資訊name = in.readUTF();,其次從位元組流中讀取serialVersionUID的資訊,然後再從位元組流中讀取各種SC_*標記資訊,透過該標記資訊設定對應的成員屬性,最後從位元組流中讀取每一個欄位的資訊這些欄位資訊包括:TypeCodefieldNamefieldType

這裡對應的方法是在序列化時使用的writeNonProxy方法,在writeNonProxy中寫入的TypeCodefieldNamefieldType在這裡被讀取。

然後回到我們的readNonProxyDesc,封裝好我的資訊之後使用resolveClass(readDesc)處理為class

image-20240625171853201

resolveClass就是使用class.forname去載入

image-20240625171912221

image-20240625172542804

然後呼叫initNonProxy處理讀取到的資訊初始化

最後返回desc物件

image-20240625172627608

回到readOrdinaryObject方法

關鍵在

image-20240625172436903

做了一個判斷desc.isExternalizable,如果序列化的介面是Externalizable型別,就進入readExternalData,否則進入readSerialData

如果我們的demo物件介面型別是Serializable,所以進入了readSerialData方法

readSerialData方法中用了反射進行呼叫反序列化物件的readObject方法

image-20210811104910320

然後回到readSerialData,會反射呼叫readResolve方法

image-20210811105200717

20201027155537-cca7961c-1829-1

總結

那麼多read方法,我們總結一下

readobject:單純的入口點

readobject0:可以說是我們的readobject方法的具體邏輯點,會跟進不同的位元組流選擇不同的方法去處理

readOrdinaryObject:用來處理我們的object物件型別的,它用於呼叫序列化類中,readObject、readResolve、readExternal的方法會有一些選擇

readClassDesc :分發用於處理位元組流中TC_CLASSDESC的方法,用switch來選擇需要處理的方法

readNonProxyDesc :真正用來處理位元組流中的TC_CLASSDESC方法,會呼叫resolveClass進行建立反序列化的物件

resolveClass:是用Class.forName來建立物件的地方,在這裡可以做一個檢查校驗,用於反序列化攔截。

readNonProxy:真正讀取我們描述資訊的方法

參考

https://www.cnblogs.com/yyhuni/p/15127416.html

https://www.cnblogs.com/yyhuni/p/15127416.html

https://www.cnblogs.com/yyhuni/p/15127416.html

相關文章