本文通過分析一個簡單java類文件的字節碼,希望借此能快速瞭解java類文件格式
為瞭分析字節碼,必須有一個整體的格式如下:
以上面的表作為分析的基礎,開始行動!
一段簡單的java代碼
9 package org.kaka.clazz;
public class TestClass {
private int m;
public int inc(){
return m+1;
}
}
查看字節碼
20 00000000 ca fe ba be 00 00 00 32 00 13 0a 00 04 00 0f 09 |…….2……..|
00000010 00 03 00 10 07 00 11 07 00 12 01 00 01 6d 01 00 |………….m..|
00000020 01 49 01 00 06 3c 69 6e 69 74 3e 01 00 03 28 29 |.I…<init>…()|
00000030 56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e |V…Code…LineN|
00000040 75 6d 62 65 72 54 61 62 6c 65 01 00 03 69 6e 63 |umberTable…inc|
00000050 01 00 03 28 29 49 01 00 0a 53 6f 75 72 63 65 46 |…()I…SourceF|
00000060 69 6c 65 01 00 0e 54 65 73 74 43 6c 61 73 73 2e |ile…TestClass.|
00000070 6a 61 76 61 0c 00 07 00 08 0c 00 05 00 06 01 00 |java…………|
00000080 18 6f 72 67 2f 6b 61 6b 61 2f 63 6c 61 7a 7a 2f |.org/kaka/clazz/|
00000090 54 65 73 74 43 6c 61 73 73 01 00 10 6a 61 76 61 |TestClass…java|
000000a0 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 00 21 00 03 |/lang/Object.!..|
000000b0 00 04 00 00 00 01 00 02 00 05 00 06 00 00 00 02 |…………….|
000000c0 00 01 00 07 00 08 00 01 00 09 00 00 00 1d 00 01 |…………….|
000000d0 00 01 00 00 00 05 2a b7 00 01 b1 00 00 00 01 00 |……*………|
000000e0 0a 00 00 00 06 00 01 00 00 00 03 00 01 00 0b 00 |…………….|
000000f0 0c 00 01 00 09 00 00 00 1f 00 02 00 01 00 00 00 |…………….|
00000100 07 2a b4 00 02 04 60 ac 00 00 00 01 00 0a 00 00 |.*….`………|
00000110 00 06 00 01 00 00 00 07 00 01 00 0d 00 00 00 02 |…………….|
00000120 00 0e |..|
00000122
step 1) java magic number
首先的四個字節0x ca fe ba be 即class文件的magic number
step2) java version
接下來的四個字節0x 00 00 00 32 即class文件的版本號(可參看class文件的版本號列表)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
step3) 常量池
接下來是描述常量表的長度0x 00 13,一共是(19-1)項,人肉分析如下
第1項
tag: 0x 0a ,CONSTANT_Methodref_info即方法聲明
index: 0x 00 04,指向常量池中CONSTANT_Class_info,見常量池第4項
index: 0x 00 0f,指向常量池中CONSTANT_NameAndType_info見常量池第15項
第2項
tag 0x 09,CONSTANT_Fieldref_info即字段聲明
index: 0x 00 03,指向常量池中CONSTANT_Class_info,見常量池第3項
index: 0x 00 10,指向常量池中CONSTANT_NameAndType_info見常量池第16項
第3項
tag:0x 07,CONSTANT_Class_info即類聲明
index: 0x 00 11,指向常量池中的CONSTANT_Utf8_info,第17項
第4項
tag:0x 07,CONSTANT_Class_info即類聲明
index: 0x 00 12,指向常量池中的CONSTANT_Utf8_info,第18項
第5項
tag:0x 01,CONSTANT_Utf8_info即字符串說明
length: 0x 00 01 ,長度為1個字節
bytes: 0x 6d, 內容為"m"
第6項
tag:0x 01,CONSTANT_Utf8_info即字符串說明
length: 0x 00 01 ,長度為1個字節
bytes: 0x 49, 內容為"I"
第7項
tag:0x 01,CONSTANT_Utf8_info即字符串說明
length: 0x 00 06 ,長度為6個字節
bytes: 0x 3c 69 6e 69 74 3e, 內容為"<init>"
第8項
tag:0x 01,CONSTANT_Utf8_info即字符串說明
length: 0x 00 03 ,長度為3個字節
bytes: 0x 28 29 56 內容為"()V"
第9項
tag:0x 01,CONSTANT_Utf8_info即字符串說明
length: 0x 00 04 ,長度為4個字節
bytes: 0x 43 6f 64 65 內容為"Code"
第10項
tag:0x 01,CONSTANT_Utf8_info即字符串說明
length: 0x 00 0f ,長度為15個字節
bytes: 0x 4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65內容為"LineNumberTable"
第11項
tag:0x 01,CONSTANT_Utf8_info即字符串說明
length: 0x 00 03,長度為3個字節
bytes: 0x 69 6e 63 內容為"inc"
第12項
tag:0x 01,CONSTANT_Utf8_info即字符串說明
length: 0x 00 03,長度為3個字節
bytes: 0x 28 29 49 內容為"()I"
第13項
tag:0x 01,CONSTANT_Utf8_info即字符串說明
length: 0x 00 0a,長度為10個字節
bytes: 0x 53 6f 75 72 63 65 46 69 6c 65內容為"SourceFile"
第14項
tag:0x 01,CONSTANT_Utf8_info即字符串說明
length: 0x 00 04,長度為14個字節
bytes: 0x 54 65 73 74 43 6c 61 73 73 2e 6a 61 76 61內容為"TestClass.java"
第15項
tag:0x 0c,CONSTANT_NameAndType_info即字段或者方法說明
index:0x 00 07 ,字段或者方法常量索引,見常量池第7項
index:0x 00 08,字段或者方法常量索引,見常量池第8項
第16項
tag:0x 0c,CONSTANT_NameAndType_info即字段或者方法說明
index:0x 00 05 ,字段或者方法常量索引,見常量池第5項
index:0x 00 06,字段或者方法常量索引,見常量池第6項
第17項
tag:0x 01,CONSTANT_Utf8_info即字符串說明
length: 0x 00 18,長度為24個字節
bytes: 0x 6f 72 67 2f 6b 61 6b 61 2f 63 6c 61 7a 7a 2f 54 65 73 74 43 6c 61 73 73內容為"org/kaka/clazz/TestClass"
第18項
tag:0x 01,CONSTANT_Utf8_info即字符串說明
length: 0x 00 10,長度為16個字節
bytes: 0x 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 內容為"java/lang/Object"
可以看出
CONSTANT_Methodref_info、CONSTANT_Fieldref_info都 依賴於CONSTANT_Class_info、CONSTANT_NameAndType_info
CONSTANT_Class_info依賴於CONSTANT_Utf8_info,後者用於聲明類的名字
CONSTANT_NameAndType_info依賴於兩個CONSTANT_Utf8_info
第一個CONSTANT_Utf8_info用於說明方法或者字段的名字
第一個CONSTANT_Utf8_info用於說明方法的入參以及返回類型或者字段的類型
列舉常量池結構
可以對比下javap的結果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 Compiled from "TestClass.java"
public class org.kaka.clazz.TestClass extends java.lang.Object
SourceFile: "TestClass.java"
minor version: 0
major version: 50
Constant pool:
const #1 = Method #4.#15; // java/lang/Object."<init>":()V
const #2 = Field #3.#16; // org/kaka/clazz/TestClass.m:I
const #3 = class #17; // org/kaka/clazz/TestClass
const #4 = class #18; // java/lang/Object
const #5 = Asciz m;
const #6 = Asciz I;
const #7 = Asciz <init>;
const #8 = Asciz ()V;
const #9 = Asciz Code;
const #10 = Asciz LineNumberTable;
const #11 = Asciz inc;
const #12 = Asciz ()I;
const #13 = Asciz SourceFile;
const #14 = Asciz TestClass.java;
const #15 = NameAndType #7:#8;// "<init>":()V
const #16 = NameAndType #5:#6;// m:I
const #17 = Asciz org/kaka/clazz/TestClass;
const #18 = Asciz java/lang/Object;
{
public org.kaka.clazz.TestClass();
Code:
Stack=1, Locals=1, Args_size=1
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
public int inc();
Code:
Stack=2, Locals=1, Args_size=1
0: aload_0
1: getfield #2; //Field m:I
4: iconst_1
5: iadd
6: ireturn
LineNumberTable:
line 7: 0
}
兩者是一致的
step4) 訪問標志
接下來的兩個字節0x 00 21是由下標中的0x 00 01 | 0x 0020計算出來的,表類是public 的,且是jdk1.2以後的編譯器編譯出來的
訪問標志 標志名稱 標志值 含義
ACC_PUBLIC 0x0001 是否為public類型
ACC_FINAL 0x0010 是否被聲明為final,隻有類可以設置,接口不能設置該標志
ACC_SUPER 0x0020 是否允許使用invokespecial字節碼指令(查瞭一下該命令的作用為"調用超類的構造
方法,實例的構造方法,私有方法"),JDK1.2以後的編譯器編譯出來的class文件
該標志都為真
ACC_INTERFACE 0x0200 標識這是一個接口
ACC_ABSTRACT 0x0400 是否被聲明為abstract類型,對於接口和抽象類來說此標志
為真,其他類為假
ACC_SYNTHETIC 0x1000 標識這個類並非由用戶代碼生成
ACC_ANNOTATION 0x2000 標識這是一個註解
ACC_ENUM 0x4000 標識這是一個枚舉
step5) 類信息
頭兩個字節0x 00 03,指向常量池中的第3項,即類名
接下來的兩個字節 0x 00 04 指向常量池中的第4項,即父類名
接下來的兩個字節 0x 00 00 表示類實現的接口的個數,本例子中為0,如果有的n個話,後面還會有2n個字節的常量池索引
step 6)字段信息
頭兩個字節 0x 00 01 字段的個數,本例中為1
接下來就是1個field_info結構
頭兩個字節 0x 00 02 表示accss_flags,即private
接下來0x 00 05表示字段名,指向常量池的第5項,即本例中"m"
接下來的0x 00 06 表示字段的描述信息,指向常量池的第6項,即本例中"I" ,表示是int類型
接下來的0x 00 00 表示字段的屬性信息(用於擴展或者補充說明字段信息)的個數,本例中為0,如果有n個的話,後面還會有n個attribute_info結構
step 7) 方法信息
頭兩個字節為0x 00 02,表示方法的個數,本例中為2
接下來是兩個method_info結構
第1個method_info結構
頭兩個字節0x 00 01 表示accss_flags,即public 方法
接下來0x 00 07表示方法名,指向常量池的第7項,即本例中"<init>"
接下來的0x 00 08 表示方法的描述信息,指向常量池的第8項,即本例中"()V" ,表示是一個沒有參數,返回類型為void的方法
接下來的0x 00 01表示方法的屬性信息的個數,本例中為1
接下是1個attribute_info結構
0x 00 09表示屬性名,指向常量池的第9項,即Code,表示方法的代碼
0x 00 00 00 1d 表示該屬性的長度,即29個字節
0x 00 01 表示max_stack
0x 00 01 表示max_locals
0x 00 00 00 05 表示code_length, 即該方法的代碼為編譯後為5個字節
0x 2a b7 00 01 b1 即代碼
0x 00 00 表示 沒有異常信息
0x 00 01 表示有一個屬性信息
接下是1個attribute_info結構
0x 00 0a 表示屬性名,指向常量池的第10項,即LineNumberTable,表示方法的行號
0x 00 00 00 06 表示該屬性的長度,即6個字節
0x 00 01 表示有1行
0x 00 00 表示字節碼為第0行
0x 00 03 表示上面的字節碼為第0行對應源碼中的第3行
第2個method_info結構
頭兩個字節00 01 表示accss_flags,即public 方法
接下來0x 00 0b表示方法名,指向常量池的第11項,即本例中"inc"
接下來的0x 00 0c 表示方法的描述信息,指向常量池的第12項,即本例中"()I" ,表示是一個沒有參數,返回類型為int的方法
接下來的0x 00 01表示方法的屬性信息的個數,本例中為1
接下是1個attribute_info結構
0x 00 09表示屬性名,指向常量池的第9項,即Code,表示方法的代碼
0x 00 00 00 1f 表示該屬性的長度,即31個字節
0x 00 02 表示max_stack
0x 00 01 表示max_locals
0x 00 00 00 07 表示code_length, 即該方法的代碼為編譯後為7個字節
0x 2a b4 00 02 04 60 ac 即代碼
0x 00 00 表示 沒有異常信息
0x 00 01 表示有一個屬性信息
接下是1個attribute_info結構
0x 00 0a 表示屬性名,指向常量池的第10項,即LineNumberTable,表示方法的行號
0x 00 00 00 06 表示該屬性的長度,即6個字節
0x 00 01 表示有1行
0x 00 00 表示字節碼為第0行
0x 00 07 表示上面的字節碼為第0行對應源碼中的第7行
step 8) 屬性信息
頭兩個字節為0x 00 01,表示有一個屬性
接下是1個attribute_info結構
0x 00 0d表示屬性名,指向常量池的第13項,即SourceFile,表示類的源文件
0x 00 00 00 02,屬性長度,即接下來的字節個數
0x 00 0e 表示源文件名,指向常量池的第14項,即"TestClass.java"
小結
如果想分析類文件,大部分情況下按字節分析,直接使用javap即可,但如果知道字節碼也可以更好的瞭解javap的結果
幾個種要的結構需要瞭解,本文就不一一列舉具體信息(這個網上到處都是)
method_info
會包含attribute_info
filed_info
會包含attribute_info
attribute_info
共有好幾種attribute,本例中就用到瞭Code,LineNumberTable,SourceFile,每種不同的屬性結構都不一樣
特別Code類型的attribute_info,編譯過後的字令碼就存放在裡面
另外還有LineNumberTable以及本例中未提到的LocalVariableTable都是和調試息息相關的
有可能會嵌套attribute_info