java 字节码分析_手把手带你分析Java中的Class字节码文件
分析Class文件的源碼如下:
package org.yuequan.klass;
public private int m;
public int inc(){
return m + 1;
}
}
筆者使用JDK1.8將源文件編譯成class文件,為了更好的手動分析這個class文件,筆者將使用現有的Class分析工具去打開這個class文件。
打開后的Class文件:
Java虛擬機規范對Class文件結構做了嚴格的規定,其中的字節嚴格的按順序緊密的排在一起,中間沒有任何的分隔符和其它數據
分析之前先把Java虛擬機規范對Class文件結構的定義先列出來
根據Java虛擬機規范所描述的Class文件結構,我們大致的可以理解類型分別為u1、u2、u4、cp_info、field_info、method_info、attribute_info,其中u1、u2、u4分別為1、2、4無符號整型字節,_info后綴結尾的都是表結構類型
基本的介紹的差不多了,那就正式開始分析吧。
根據ClassFile文件結構來分析,前面4個字節是魔數,魔數為固定的0xCAFEBABE,魔數可以作為一個文件的特殊識別,很多類型的文件,都會在文件前面加幾個字節填充魔數,利用魔數來識別文件類型,根據Java虛擬機規范也只有0xFAFEBABE魔數的文件才能被Java虛擬機所接受。
再緊跟著的是2個字節的次版本號
次版本號為0,次版本號從JDK1.2以后次版本號就沒有再被使用而是一直使用著主版本號。
再緊跟著2個字節的主版本號
我的主版本號為0x34為52,正好是JDK1.8的版本號,為了方便觀看,筆者將JDK的版本號整理了一個列表供參考
再接下來是2個字節的常量池數量
我的常量池數量是19個0x13 = 19,常量池元素索引是從1開始而不是0這點要注意,所以常量池可用數量為1 – count-1,第0項是為了某些指向常量池的索引值的數據在特殊情況下不需要引用任何一個常量池項目的時候就可以將索引置為0
接下來介紹ClassFile的cp_info也就是常量池,常量池中主要存儲字面量和符號引用,字面量的意義比較接近于常量的概念,如字符串、final后的常量值等,這里的符號引用主要還是包括:類和接口的全限定名、字段的名稱和描述符、方法的名稱和描述符,接下來再看下cp_info的結構定義,常量池中每一項都是一個表,到目前JDK8版本共有14個常量類型的結構,為了區分這些常量類型,常量數據的第一字節是tag分別對應著這14個常量類型
概念介紹到這里差不多了,那么開始正式的分析
第一個常量的tag是0x0A對應著10也就是Constant_Methodref_info
根據結構后面還有2個u2類型的class_index和name_and_type_index
class_index 是指常量池中索引為class_index的常量項,name_and_type_index同理,由于后面的常量項還沒分析,無法得知index所指的具體值,所以先把值分析出來放在這里吧,不用擔心,我在文章的該列出來的地方還會在列一遍,常量池第一項的數據
// index 1
tag = 10
class_index = 4
name_and_type_index = 15
接著我們來分析第二項
第二項tag為9對應著CONSTANT_Fieldref_info
// index 2
tag = 9
class_index = 3
name_and_type_index = 16
第三項
第三項tag為7對應著CONSTANT_Class_info
// index 3
tag = 7
name_index = 17
第四項
第四項tag為7對應著CONSTANT_Class_info
// index 4
tag = 7
name_index = 18
第五項
第五項的tag為1對應著CONSTANT_Utf8
長度為1
長度為1那bytes也就只有一個
// index 5
tag = 1
length = 1
bytes = ['m'] // bytes = [6D] 6D = 109 => UTF8 => m
第六項
嗯哼tag還是01
長度為1
// index 6
tag = 1
length = 1
bytes = ['I'] // bytes = ['49'] 49 => 73 => UTF8 => I
第七項
tag還是01
長度為6
// index 6
tag = 1
length = 6
// 23333 很長于是注釋寫這里
// [0x3C, 0x69, 0x6E, 0x69, 0x74, 0x3E]
// to UTF8
// ['']
// bytes.toString() => ""
bytes = ['']
第八項
tag仍為01 ,😊2333333
長度為3
// index 8
tag = 1
length = 3
bytes = ['(', ')', 'V'] //不再重復轉換步驟拉
第九項
tag還是01哦
長度是4
// index 9
tag = 1
length = 4
bytes = ['C', 'o', 'd', 'e']
第十項
tag還是01哦
長度是15
// index 10
tag = 1
length = 15
bytes = ['L', 'i', 'n', 'e', 'N', 'u', 'm', 'b', 'e', 'r', 'T', 'a', 'b', 'l', 'e']
第十一項
長度為3
// index 11
tag = 1
length = 3
bytes = ['i', 'n', 'c']
第十二項
tag任然是1
長度為3
// index 12
tag = 1
length = 3
bytes = ['(', ')', 'I']
第十三項
長度為10
// index 13
tag = 1
length = 10
bytes = ['S', 'o', 'u', 'r', 'c', 'e', 'F', 'i', 'l', 'e'] //SourceFile
第十四項
長度為8
// index 14
tag = 1
length = 8
bytes = ['F', 'o', 'o', '.', 'j', 'a', 'v', 'a'] // Foo.java
第十五項
tag為12,12對應著CONSTANT_NameAndType
// index 15
tag = 12
name_index = 7
descriptor_index = 8
第十六項
// index 16
tag = 12
name_index = 5
descriptor_index = 6
第十七項
長度為21
// index 17
tag = 1
length = 21
bytes = ['o', 'r', 'g', '/', 'y', 'u', 'e', 'q', 'u', 'a', 'n', '/', 'k', 'l', 'a', 's', 's', '/', 'F', 'o', 'o']
哇,最后一項,由此可見常量池占了我們字節碼文件的大部分內容
第十八項
長度為16
// index 18
tag = 1
length = 16
bytes = ['j', 'a', 'v', 'a', '/', 'l', 'a', 'n', 'g', '/', 'O', 'b', 'j', 'e', 'c', 't']
至此,常量池已經分析完了,是時候整理一波了。
大致分析出來的內容用思維導圖整理了一下,最終整合一波
// index 1
// class_index = 4 name_and_type = 15
Methodref java/lang/Object.
// index 2
// class_index = 3 name_and_type = 16
Fieldref org/yuequan/klass/Foo.m
// index 3
// name_index = 17
Class org/yuequan/klass/Foo
// index 4
// name_index = 18
Class java/lang/Object
// index 5
Utf8 m
// index 6
Utf8 I
// index 7
Utf8
// index 8
Utf8 ()V
// index 9
Utf8 Code
// index 10
Utf8 LineNumberTable
// index 11
Utf8 inc
// index 12
Utf8 ()I
// index 13
Utf8 SourceFile
// index 14
Utf8 Foo.java
// index 15
// name_index = 7 descriptor_index = 8
NameAndType &()V
// index 16
// name_index = 5 descriptor_index = 6
NameAndType m&I
// index 17
Utf8 org/yuequan/klass/Foo
// index 18
Utf8 java/lang/Object
終極整理已完成~,常量池分析就先到這里,由于文章篇幅好像有點長了23333,還有些許知識點我放在下一篇《升級版》中去探討
接下來,該分析啥,掏出ClassFile結構來看看
常量池結束后,緊跟著的是2個字節的訪問標識符
我們來看看對應著啥?
一眼看上去感覺沒有對的上的? 但使用了bitmask的思路所以對應的應該是
access_flag = ACC_PUBLIC, ACC_SUPER
在接著往下分析
訪問標識符完了后,根據結構是2個字節的this_class 這個值也是指向的常量池的索引
對應著常量池第三個索引(從1開始數)也就是org/yuequan/klass/Foo
接下來是父類
除java.lang.Object以外所有類都有一個默認父類,那便是java.lang.Object,可以理解為父要么為0要么就是指向常量池中存在的Class,而為0的根據目前來看應該只有java.lang.Object如果還有其它的歡迎在Blog的Github上提issue大家一起分享。
常量池索引為4的是java.lang.Object,所以該類的父類應是java.lang.Object
接下來是接口
此類沒有實現接口數量為0
接下來是fields_count,它的數量為1
接著是field_info
先把結構掏出來看下
根據結構先是一個u2的訪問標識符
0x0002對應著ACC_PRIVATE
接著是一個u2類型的name_index,0x0005對應著常量池中cp_info[5]的m
接著是u2類型的descriptor_index,0x0006對應著常量池中cp_info[6]的I(int的描述形式)
接著也是一個u2類型的attributes_count,但數量為0
接下來的方法的數量
數量表示有2個方法
接著是method_info,還是先把結構拿出來
先分析第一個access_flags
0x0001對應著ACC_PUBLIC
第二個是name_index,指向著常量池的一個索引
指向常量池第7個索引
接著是descriptor_index
指向常量池第8個索引()V,由于文章篇幅太長了,剩下的attribute改天分解或者說不會再讀分解,我感覺已經夠了,如果大家想要我寫完的話,那我就寫完吧
總結
以上是生活随笔為你收集整理的java 字节码分析_手把手带你分析Java中的Class字节码文件的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: APP测试之安全机制问题及Bypass
- 下一篇: java颜色gui_Java gui颜色