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文件:

e31bc3747b195ac7fbc0f954777a60ed.png

Java虚拟机规范对Class文件结构做了严格的规定,其中的字节严格的按顺序紧密的排在一起,中间没有任何的分隔符和其它数据

分析之前先把Java虚拟机规范对Class文件结构的定义先列出来

d0ddc440ddc207b393caa7edf9c448f9.png

根据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虚拟机所接受。

1f54fd7255506c02f41ba31ab74a0f49.png

54b2fac3d0e1dece2a7d449a426b73f0.png

再紧跟着的是2个字节的次版本号

19196ac29d2d35ddc3624cc5bb02e488.png

698303ac6cd84053d23d83e4106ce10b.png次版本号为0,次版本号从JDK1.2以后次版本号就没有再被使用而是一直使用着主版本号。

再紧跟着2个字节的主版本号

36a672b770fd0649212091538f2581ab.png

ad2cfb1b0e645b2b7df3c95929b15c3c.png我的主版本号为0x34为52,正好是JDK1.8的版本号,为了方便观看,笔者将JDK的版本号整理了一个列表供参考

再接下来是2个字节的常量池数量

9d98d68f911fbc6d153182d3759d45c6.png

a4988a6e91202fd01fcde12bb600881a.png我的常量池数量是19个0x13 = 19,常量池元素索引是从1开始而不是0这点要注意,所以常量池可用数量为1 – count-1,第0项是为了某些指向常量池的索引值的数据在特殊情况下不需要引用任何一个常量池项目的时候就可以将索引置为0

接下来介绍ClassFile的cp_info也就是常量池,常量池中主要存储字面量和符号引用,字面量的意义比较接近于常量的概念,如字符串、final后的常量值等,这里的符号引用主要还是包括:类和接口的全限定名、字段的名称和描述符、方法的名称和描述符,接下来再看下cp_info的结构定义,常量池中每一项都是一个表,到目前JDK8版本共有14个常量类型的结构,为了区分这些常量类型,常量数据的第一字节是tag分别对应着这14个常量类型

5280c536724b2dbd4061e268d4903041.png

概念介绍到这里差不多了,那么开始正式的分析

38b7f0dd0dcf3ec137a99a9105c68215.png第一个常量的tag是0x0A对应着10也就是Constant_Methodref_info

792f7b7708e75b94554b6f3d6900d0c8.png根据结构后面还有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

接着我们来分析第二项

3bf0f0b25c391172bc6705e414c2ebbf.png第二项tag为9对应着CONSTANT_Fieldref_info

5649cde79460f01ff0a01bf81b3c0388.png

// index 2

tag = 9

class_index = 3

name_and_type_index = 16

第三项

96abb9fb17171ead2e755a122a78d617.png第三项tag为7对应着CONSTANT_Class_info

597192d064a00c7a3f409c7d60c82f79.png

// index 3

tag = 7

name_index = 17

第四项

309783f0fa6d71a53d6e05b30c7c1d63.png第四项tag为7对应着CONSTANT_Class_info

d7ebc0959344e53594f8f5fec5b2d565.png

// index 4

tag = 7

name_index = 18

第五项

6ca303dd98824232f6fb996d5f477646.png第五项的tag为1对应着CONSTANT_Utf8

5455f80bf215076872dd540d208d2fac.png长度为1

2540fbb2b7f7aa9be92df29746b8eeed.png长度为1那bytes也就只有一个

7eeb399cb7d9a37823b424d9979271b3.png

// index 5

tag = 1

length = 1

bytes = ['m'] // bytes = [6D] 6D = 109 => UTF8 => m

第六项

ad5489b56ba2c0847522c6878fabbe1a.png嗯哼tag还是01

c7f1454d8604d3f409f2ee930fc663e4.png长度为1

c5b29ec84be45629af264af2257fa6ad.png

// index 6

tag = 1

length = 1

bytes = ['I'] // bytes = ['49'] 49 => 73 => UTF8 => I

第七项

ab878f0179c3946b8b0e4c1e6d5bc567.pngtag还是01

f0db222b2420561e670589d541f6e7cd.png长度为6

086f936489d6e49156e69297786de7c9.png

// index 6

tag = 1

length = 6

// 23333 很长于是注释写这里

// [0x3C, 0x69, 0x6E, 0x69, 0x74, 0x3E]

// to UTF8

// ['']

// bytes.toString() => ""

bytes = ['']

第八项

c14facf86d490fd6111f75b52c17c440.pngtag仍为01 ,😊2333333

5f955d42b05bb888c64b630d5f40c199.png长度为3

9e99250275b89fdf7c3b7cf88c366c2d.png

// index 8

tag = 1

length = 3

bytes = ['(', ')', 'V'] //不再重复转换步骤拉

第九项

ac49aa5e250bd7321bef492da81b66ac.pngtag还是01哦

b60f9d5adde34009acf14c4098f3ae0f.png长度是4

29bbaab3cc5b840418173850fcebc3f6.png

// index 9

tag = 1

length = 4

bytes = ['C', 'o', 'd', 'e']

第十项

205aec97089a909fc7cf54079bd2c2a0.pngtag还是01哦

99ebfc18452d771116540d5f98d7d6b0.png长度是15

63089674e796c860a7d8ee26118e55e4.png

// index 10

tag = 1

length = 15

bytes = ['L', 'i', 'n', 'e', 'N', 'u', 'm', 'b', 'e', 'r', 'T', 'a', 'b', 'l', 'e']

第十一项

c65d77a29864aa7331fd38cff16d2484.png长度为3

6306e42298d3c3ff533da05f04cea9c0.png

// index 11

tag = 1

length = 3

bytes = ['i', 'n', 'c']

第十二项

52c4e09328dcf504b565d05d84254a0f.pngtag任然是1

长度为3

5475baee78e8a4642dff4b1b0dcf70ef.png

// index 12

tag = 1

length = 3

bytes = ['(', ')', 'I']

第十三项

91f13ab8fa1434b852744f0340e00729.png长度为10

26cec894fdd939809d74108dd6fa63b7.png

// index 13

tag = 1

length = 10

bytes = ['S', 'o', 'u', 'r', 'c', 'e', 'F', 'i', 'l', 'e'] //SourceFile

第十四项

3c80ad2c1c1eddb9867f409a4c33ae44.png长度为8

12ac90568cd7220c1b0f92b3debd2ea6.png

// index 14

tag = 1

length = 8

bytes = ['F', 'o', 'o', '.', 'j', 'a', 'v', 'a'] // Foo.java

第十五项

368df9eaa25403168cb88904b8447e77.pngtag为12,12对应着CONSTANT_NameAndType

8d50250c4b8b59bf03352c7b3291988c.png

// index 15

tag = 12

name_index = 7

descriptor_index = 8

第十六项

ee1baf6dcc4b83f4715beb89f8b0fd44.png

// index 16

tag = 12

name_index = 5

descriptor_index = 6

第十七项

24fdff58df7517aca78a8cbf2eb51380.png长度为21

58b1ab95e60d803735f7c4a301b4fac2.png

// 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']

哇,最后一项,由此可见常量池占了我们字节码文件的大部分内容

第十八项

fbbf1e4b272ba2c4e3659f97e0677d7f.png长度为16

699686bf300c94195649d4e0a12fb67a.png

// index 18

tag = 1

length = 16

bytes = ['j', 'a', 'v', 'a', '/', 'l', 'a', 'n', 'g', '/', 'O', 'b', 'j', 'e', 'c', 't']

至此,常量池已经分析完了,是时候整理一波了。

15e5a2185ab47434581b3b7e7b325b35.png大致分析出来的内容用思维导图整理了一下,最终整合一波

// 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结构来看看

dc074624cc0398b72cbd0e312d11320d.png常量池结束后,紧跟着的是2个字节的访问标识符

66f8dc8e7343ac263d63c6ac3253967b.png我们来看看对应着啥?

8407d9183d41ed14ca73711b7173d80a.png一眼看上去感觉没有对的上的? 但使用了bitmask的思路所以对应的应该是

access_flag = ACC_PUBLIC, ACC_SUPER

在接着往下分析

2b3dac3364e7f43a8085e768a0a1b962.png访问标识符完了后,根据结构是2个字节的this_class 这个值也是指向的常量池的索引

1df0ec815520089b0ef1c5e38690bc1e.png对应着常量池第三个索引(从1开始数)也就是org/yuequan/klass/Foo

接下来是父类

d3827edd5092a13d93728fdc32c4bab0.png除java.lang.Object以外所有类都有一个默认父类,那便是java.lang.Object,可以理解为父要么为0要么就是指向常量池中存在的Class,而为0的根据目前来看应该只有java.lang.Object如果还有其它的欢迎在Blog的Github上提issue大家一起分享。

baeb7549409a6a7370286112e160df48.png常量池索引为4的是java.lang.Object,所以该类的父类应是java.lang.Object

a39d15d0745c4a42819add803bd2906a.png接下来是接口

bd0fe51c7da79b772c74e6ee90d781b9.png此类没有实现接口数量为0

38a0059c8ac191a81ac507c93ab60234.png

接下来是fields_count,它的数量为1

4109585de0c533feee7d9e5fa642eff5.png接着是field_info

0f5046d95b2ea42b9171ded8d9b79736.png先把结构掏出来看下

044a2de857fda0262fdb4c3ff71220db.png根据结构先是一个u2的访问标识符

452f9482aaf8c0883482b08181c99f7e.png0x0002对应着ACC_PRIVATE

9148df87b2010fb4415709b7f72c3593.png接着是一个u2类型的name_index,0x0005对应着常量池中cp_info[5]的m

b3eab5f96468b4f6d27869a152b219d9.png接着是u2类型的descriptor_index,0x0006对应着常量池中cp_info[6]的I(int的描述形式)

8537e8503130c4169b4324318ed5dd5a.png接着也是一个u2类型的attributes_count,但数量为0

d11bbda1d9c1e4bc008598c1fc86f827.png

接下来的方法的数量

23cbe72a01c0a1593f9966fde3bccaf9.png数量表示有2个方法

9bea35fc978b598d38b2692f97c18186.png

接着是method_info,还是先把结构拿出来

49451f3408b06613318ea5499a7d1788.png先分析第一个access_flags

e08a97cefe55b65a1c93d82e231ed341.png0x0001对应着ACC_PUBLIC

4ae25e1108f593e8aace38ceadfe98ce.png第二个是name_index,指向着常量池的一个索引

56220a7be1f872d8970da8f3f97c3f47.png指向常量池第7个索引

接着是descriptor_index

a6237701cec4ac489d44c7ece22c698c.png指向常量池第8个索引()V,由于文章篇幅太长了,剩下的attribute改天分解或者说不会再读分解,我感觉已经够了,如果大家想要我写完的话,那我就写完吧

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值