反序列化底層學習
前言
以前也是懶得學,覺得沒有必要,學到現在發現好多東西都需要學習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);
去恢復物件
readObject0方法會對不同的型別進行不同的處理,我這裡是object型別的
readOrdinaryObject(unshared)
我們繼續看看是怎麼處理的
內部呼叫ObjectStreamClass desc = readClassDesc(false);
去讀取我們的序列化流的描述資訊,跟進
可以看到也是按照不同的型別去處理,有null,引用型別,代理型別,class型別
我們進入到class型別
return readNonProxyDesc(unshared);
先進行一個檢查是不是class型別,然後例項化一個desc物件,desc的資訊是透過readClassDescriptor讀取的
跟進readClassDescriptor
跟進readNonProxy
這個方法是真正意義上的操作
系統會先從位元組流中讀取類名資訊name = in.readUTF();
,其次從位元組流中讀取serialVersionUID
的資訊,然後再從位元組流中讀取各種SC_*
標記資訊,透過該標記資訊設定對應的成員屬性,最後從位元組流中讀取每一個欄位的資訊這些欄位資訊包括:TypeCode
、fieldName
、fieldType
這裡對應的方法是在序列化時使用的writeNonProxy
方法,在writeNonProxy
中寫入的TypeCode
、fieldName
、fieldType
在這裡被讀取。
然後回到我們的readNonProxyDesc,封裝好我的資訊之後使用resolveClass(readDesc)處理為class
resolveClass就是使用class.forname去載入
然後呼叫initNonProxy處理讀取到的資訊初始化
最後返回desc物件
回到readOrdinaryObject
方法
關鍵在
做了一個判斷desc.isExternalizable
,如果序列化的介面是Externalizable型別,就進入readExternalData,否則進入readSerialData
如果我們的demo物件介面型別是Serializable,所以進入了readSerialData
方法
readSerialData
方法中用了反射進行呼叫反序列化物件的readObject
方法
然後回到readSerialData,會反射呼叫readResolve方法
總結
那麼多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