Java反編譯器剖析(中)-Java開發(fā)Java經(jīng)驗技巧_第1頁
Java反編譯器剖析(中)-Java開發(fā)Java經(jīng)驗技巧_第2頁
Java反編譯器剖析(中)-Java開發(fā)Java經(jīng)驗技巧_第3頁
Java反編譯器剖析(中)-Java開發(fā)Java經(jīng)驗技巧_第4頁
Java反編譯器剖析(中)-Java開發(fā)Java經(jīng)驗技巧_第5頁
已閱讀5頁,還剩3頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

1、java反編譯器剖析(中)-編程開發(fā)技術(shù)java反編譯器剖析(中)木文由importnew烏|1柏 翻譯自javacodegeekso歡迎加入翻譯小紐.。轉(zhuǎn)載請見文末要求。 在上一篇文章中,我們介紹了翻譯器的功能、簡單的字節(jié)碼知識回顧、反編譯和 棧分析。本文將繼續(xù)討論反編譯器中對條件表達式、變量類型分析、短路運算符 和方法調(diào)用在反編譯器屮的處理。條件表達式在這里可以決定我們的代碼是否使用了三元運算符(?:):冇一個判斷條件,條 件的每個分支都對同一個棧變量sl,2進行一次賦值,賦值后兩條路徑會進行 合并。-旦確定了這個模式,就可直接使用三元表達式。復(fù)制傳播后合并三元表達式0145891011訐

2、(vo = 0) goto #8 sl,2 = vlgoto 9sl,2 =v2v3 = s 1,2)return v3v3 = v0!=0? vl : v2return v3值得注意的是,作為轉(zhuǎn)換的一部分,我們對#9處的條件進行了取反??梢钥闯?javac生成的代碼對判斷條件取反這一行為是有規(guī)律的。因此,如果將轉(zhuǎn)換后的 條件取反,就口j以更加接近原來的代碼。畫外音:類型是什么?當(dāng)處理棧值時,jvm使用了一個比java代碼更為簡單的類型系統(tǒng)。特別是 boolean、char和short的值都被作為int值使用同一指令處理。因此,vo! =0可以翻譯成: vo != false ? vl : v

3、2或者vo != 0 ? vl : v2甚至還可以翻譯為vo != false ? v1 = true : v2 = true還有很多其它的翻譯結(jié)果!在這個例子屮,我們很幸運地知道vo的精確類型,這個類型包含在方法描述中: descriptor: (zii)iflags: acc_publtc, acc_stattc方法簽名由此可以知形式如下:public static int plus (boolean, int, int)通過簽名還可以知道,v3是int型(而不是boolean型)。因為它是返回值,通過描述符已經(jīng)知道了返回值類型。接下來,還需要翻譯:v3 = vo ? vl : v2ret

4、urn v3另外,如果vo是一個本地變量(不是形參),可能無法知道其類型是boolean 而不是into還記得我們之前提到的本地變量表,就是包含了原始本地變量名的 那個表嗎?除了變量名,它述記錄了有變量的類型。因此,如果編譯時帶有debug 信息,就可以從本地變量表屮知道變量的類型。此外,還有一張localvariab 1 etypetab 1 e表,此表也包含類似的信息。兩者的主要區(qū)別在于 local vari abletypetable包含了泛型信息。然而,由于localvariabletypetable中的信息是未經(jīng)驗證的元數(shù)據(jù),因此不能完全依賴這 些數(shù)據(jù)。一些非常規(guī)的混淆器(obfus

5、cator)會在這些表小填入假信息,但是修 改后的字節(jié)碼卻依然可以執(zhí)行!所以請自行決定如何使用這些表。短路運算符(試和,)public staticreturn aboolcan fn(boolcan a, boolcan b, boolcan c) b && c;怎么能更簡單呢?不幸的是,關(guān)于字節(jié)碼的理解總是冇一點痛苦字節(jié)碼棧變量復(fù)制傳播后0iload_0so = vo1ifne#12if(s() !=() goto #12if(v() !=() goto #124iload_lsl = vl5ifeq #16if (sl = 0) goto #16if (vl = 0) g

6、oto #168iload_2s2 = v29ifeq #16if (s2 = 0) goto #16訐(v2 = 0) goto # 1612iconst_ls3 = 1s3,4) = 113goto #17goto 17goto 1716iconst_0s4 = 0s3,4 =017i returnreturn s3,4return s3,4根據(jù)選擇的路徑不同,位于#17位置的ireturn指令可能返冋s3或者s4o 我們?yōu)槠浞謩e命名,然后使用復(fù)制傳播來消除so、si和s2。接下來,在#1、#5和#7位置冇三個連續(xù)的條件。如之前提到的那樣,條件分 支要么跳轉(zhuǎn),要么接著執(zhí)行下一條指令。上面的

7、字節(jié)碼包含了一組遵循特定的使用模式,這些模式非常實用:條件與(&&)條件或(idtl:tl:訐(cl) goto li訐(cl) goto l2if (c2) goto l2if (c2) goto l2li:li:變成了變成了if (!cl && c2) goto l2if (cl | c2) goto l2li:li: 如果考慮上面表中的臨近條件組,#1#5不遵循上面任何一種模式,但#5 #9卻是一個條件或(|),因此可以進行如卜轉(zhuǎn)換:1: if (vo !二 0) goto #125: if (vl 二二 0 v2 二二 0) goto #1612:s3,

8、4 = 113: goto #1716:s3, 4 = 017: return s 3,4注意:每次轉(zhuǎn)換都可能引入新的轉(zhuǎn)換。這種情況下,可以應(yīng)用ii對條件進行重 組。現(xiàn)在可以對# 1#5應(yīng)用&&模式!通過將這些代碼合并為單個條件分支 可以進一步簡化方法:1:if (vo = 0 && (vl = 012:s3,4二 113:goto #1716:s3, 4 = 017:return s 3,4v2 = 0) goto #16這是不是看起來和其他地方很類似?是的,現(xiàn)在這個字節(jié)碼就符合之前的三元操 作符(?:)規(guī)則了。我們可以將#1.#16縮減為一個獨立的表達式,再

9、使用 復(fù)制傳播將s3, 4內(nèi)聯(lián)到為#17的return語句。v2 = 0) ? 0 : 1;return (v0 = 0 && (vl 二二 0利用方法描述符和木地變量類型表可以推斷變量類型,這樣縮減后的表達式如 下:v2 = false) ? false : true;return (v0 = false && (vl = false好吧,現(xiàn)在的結(jié)果比反編譯的內(nèi)容更加精煉了,但是仍然不夠美觀。讓我們看看 可以做點什么。首先,折疊比較運算符,比如把x二二true和x二二false簡寫為x 和!xo還可以消除三元操作符,比如把x ? false: true簡寫為!

10、x。return ! (!v0 && (!vl | !v2);如果你述記得你高屮的離散數(shù)學(xué),那么根據(jù)徳摩根定理,更進一步可以縮寫為:> >- - - 7 7 b b h&& a a z(x /(xi 1 z/( /(x17 17 a ai n /(x z(x因此,return ! ( !v0 && ( !vl!v2 )可以變?yōu)?接著變成,return ( vo ! (!vl | !v2 )最終會變成:return ( vo (vl && v2)萬歲!處理方法調(diào)用我們已經(jīng)了解調(diào)用方法的流程:先將參數(shù)“存入”本地數(shù)組;要進

11、行方法渤7, 必須將參數(shù)推到棧上,并且緊跟一個指向?qū)嵗椒ǖ膖his指針。方法調(diào)用的字 節(jié)碼正如你預(yù)想的那樣:push arg_0push arg_linvokevirtual methodref在上面的代碼屮可以看到invokevirtual,該指令可以用來調(diào)用大多數(shù)的實例方 法。jvm有一組方法調(diào)用的指令,每個指令都有特定的功能:1. invokcintcrfacc:調(diào)用接口方法。2. invokevirtual:調(diào)用使用virtual語義的實例方法,比如調(diào)用的方法在運行時 根據(jù)重載分派到不同的實例方法。3. invokespecial:調(diào)用一個具體的實例方法(非virtual語義)。該指

12、令常用來 調(diào)用構(gòu)造器(constructor),但也町以調(diào)用類似super, method ()這樣的方法。4. invokestatic:調(diào)用靜態(tài)方法。5. invokedynamic:使用“引導(dǎo)方法”(bootstrap)啟動自定義調(diào)用點,該命令(在java 中)很少使用。引入該命令是為了支持動態(tài)語言,在java8中被用來實現(xiàn)lambda表 達式。反編譯器有一個重要細節(jié),class的菖量滋中包含了所冇方法調(diào)用的信息,包括 參數(shù)的數(shù)量、類型和返回值類型。調(diào)用的類會記錄這些信息,運行時會確保該方 法在調(diào)用時已存在,并對方法簽名進行檢查。如果調(diào)用的是第三方代碼的函數(shù), 并且函數(shù)的簽名發(fā)生了改變,任何試圖對舊版本的調(diào)用都會拋出錯誤(而不是產(chǎn) 生不可預(yù)知的行為)?;氐缴隙睦?,從invokevirtual操作碼可以得知目標方法是一種實例方法。 因此,需要將this指針作為隱含的第一參數(shù)。常量池中的methodref告訴我 們該這個方法有一個形參,所以除了實例方法的指針述需要從棧上彈出一個參 數(shù)。接下來代碼可以重寫為:arg_0. methodref(arg_l)當(dāng)然,不是所有的字節(jié)碼看起來都如此“友好”。棧小的參數(shù)并不要求一個接一 個排列整齊。假如參數(shù)屮有一個三元表達式,那么屮間就會有加載、存儲和分支 指令,這些都需

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論