贛州市鐵路建設辦公室網(wǎng)站湖南靠譜關鍵詞優(yōu)化
目錄
1.概述
?2.字節(jié)碼文件構成
2.1.魔數(shù)
2.2.版本號
2.3.常量池
2.4.訪問標志?
2.5.索引
2.6.字段表
2.7.方法表
3.字節(jié)碼指令
3.1.概述
3.2.指令分類
3.2.1.加載存儲指令
3.2.2.運算指令
3.2.3.其他指令
3.3.完整指令工作流程
4.字節(jié)碼保護
1.概述
以往的編程語言是直接編譯為計算機可識別并直接執(zhí)行的機器碼,但不同平臺(機器)的指令集不同,編譯出來的機器碼不同,一個平臺編譯出來的機器碼在另一個平臺上可能無法正常運行。JAVA為了實現(xiàn)“write once run anywhere”的效果,先將JAVA代碼編譯為平臺無關的.class文件(字節(jié)碼文件),不論什么平臺編譯出來的字節(jié)碼都是一樣的,然后用適配不同平臺的JVM來加載字節(jié)碼文件,將字節(jié)碼文件翻譯給對應平臺來執(zhí)行。
字節(jié)碼文件是個二進制文件,由JVM定義字節(jié)碼文件的規(guī)范,任何滿足這種規(guī)范的class文件都會被JVM加載運行。字節(jié)碼規(guī)范規(guī)定字節(jié)碼應該具有以下基本結構:
?2.字節(jié)碼文件構成
2.1.魔數(shù)
字節(jié)碼的前4個字節(jié)是魔數(shù),所有字節(jié)碼文件的魔數(shù)是固定的,4個byte合起來是cafebabe,用來標識該文件是JAVA的class文件。
2.2.版本號
字節(jié)碼中記錄編譯使用的JDK版本,字節(jié)碼的第5、6位表示次版本號,第7、8位表示主版本號:
查表可以得,示例中的版本號為JDK12。
2.3.常量池
常量池存放兩大類常量:
- 字面量,如文本字符串、fnal的常量值等
- 符號引用,類和接口的全限定名、字段的名稱和描述符、方法的名稱和描述符
字節(jié)碼中存放有常量池,使用java -verbose命令可以反編譯得到常量池信息。
2.4.訪問標志?
常量池結束后的兩個字節(jié),描述了該Class是類還是接口,以及是否被public、abstart、final等修飾符修飾。
2.5.索引
字節(jié)碼文件中的索引分為三類:
- 類索引
- 父類索引
- 接口索引集合
類索引:
訪問標志后的兩個字節(jié),描述的是當前類的全限定名,這兩個字節(jié)保存的值為常量池中的索引值,根據(jù)索引值就能在常量池中找到這個類的全限定名
父類索引:
當前類名后的兩個字節(jié)描述父類的全限定名,同上,保存的也是常量池中的索引值。
接口索引集合:
父類名稱后為兩字節(jié)的接口計數(shù)器,描述了該類或父類實現(xiàn)的接口數(shù)量。緊接著的n個字節(jié)是所有接口名稱的字符串常量的索引值。
2.6.字段表
用來記錄成員變量,分為兩部分:
- 計數(shù)器,記錄成員變量的個數(shù)。
- 數(shù)據(jù)區(qū),記錄變量的具體內容。
2.7.方法表
記錄方法,分為兩部分:
- 計數(shù)器,記錄方法的個數(shù)
- ?數(shù)據(jù)區(qū),記錄方法的具體內容
屬性列表中記錄了方法的具體內容:
- Code,源代碼對應的]VM指令操作碼
- LineNumberTable,行號表,將Code區(qū)的操作碼和源代碼中的行號對應
3.字節(jié)碼指令
3.1.概述
字節(jié)碼指令在棧里面的每個棧幀中操作局部變量表進行對操作數(shù)棧的壓棧出棧,從而完成方法中編寫的一系列內容,變量表是“菜籃”;操作數(shù)棧是“砧板”。?
3.2.指令分類
3.2.1.加載存儲指令
用于變量對于操作數(shù)棧的壓棧、出棧。
- 用于將數(shù)據(jù)在棧幀中的局部變量表和操作數(shù)棧之間來回傳輸
- 將一個局部變量加載到操作棧: iload、lload、fload、dload、aload等
- 將一個數(shù)值從操作數(shù)棧存儲到局部變量表: istore、Istore、fstore、dstore、astore等
- 將一個常量加載到操作數(shù)棧: bipush、sipush、ldc、ldc_w、ldc2 w、aconst null、iconst m1等
3.2.2.運算指令
用于進行運算的指令。
- ?iadd、isub、imul、idiv等類型轉換指令
- i2b、i2l、i2s等對象/數(shù)組創(chuàng)建與訪問指令
- new、newarray、getfield等操作數(shù)棧管理指令
- pop、dup等
3.2.3.其他指令
- ?lfeq、goto等方法調用和返回指令
- ?invokevirtual、ireturn等異常處理指令
- athrow同步控制指令
- ?monitorenter、monitorexit
3.3.完整指令工作流程
以下面代碼為例展示一個完整的指令工作流程:
public class Sum {private int base = 10;public long add(int toAdd){this .base += toAdd;return this.base;}
}
反編譯查看源碼:
?
整體步驟如下:
- aload 0 將索引為0的局部變量壓棧
- dup 復制棧頂值并壓棧
- getfeld 取出棧頂對象引用,獲取其成員變量并壓棧
- iload_1 將索引為1 int型局部變量壓棧
- iacd 取出棧頂兩個int型值相加,結來壓棧
- puteld 將棧頂兩個元素取出 (值和對象引用) ,將值賦給該對象宇段
- i2l 將棧頂int型值取出轉為long型壓棧lreturn 將棧頂long型元素取出返回
4.字節(jié)碼保護
防止字節(jié)碼文件被反編譯為.java文件,一般有兩種手段:
- 字節(jié)碼保護
- 字節(jié)碼混淆
字節(jié)碼保護:
對字節(jié)碼進行加密使其不再遵循JVM的規(guī)范,JVM加載之前先解密。
字節(jié)碼混淆:
被混淆代碼依然遵循JVM制定的規(guī)范,變量命名和程序流程上進行等效替換,使得程序的可讀性變差,使得代碼難以被理解和重用,達到保護代碼的效果。
常用的字節(jié)碼混淆器:ProGuard
下載地址:https://www.guardsquare.com/en/products/proguard