Java关于字节码文件的解析
字节码文件解析
最近有回过头看了一下jvm并且去看了一下他的一部分c语言的源码。打算尝试用Java写一个分析字节码文件的小程序。
这里写了一个class来手动分析一下字节码的解析过程
public class jvmclasstest { public int i = 1; public static Integer a = 2; private String s = "caohao"; public static void main(String[] args) { jvmclasstest test = new jvmclasstest(); test.test(10); } public void test(int a){ this.i = a; } }
这里选用了一个叫做Synalyze It! Pro的16进制查看工具
一个字节码文件大概是由以下几个部分构成
- 魔数
- 版本号
- 常量池
- 这个类的access_flag
- this_class
- super_class
- interfaces
- fileds
- methods
- attributes
字节码文件的内容如下:
HEX DUMP: [00000010] CA FE BA BE 00 00 00 34 00 2E 0A 00 0A 00 21 09 .......4 ........ [00000020] 00 05 00 22 08 00 23 09 00 05 00 24 07 00 25 0A ........ ........ [00000030] 00 05 00 21 0A 00 05 00 26 0A 00 27 00 28 09 00 ........ ........ [00000040] 05 00 29 07 00 2A 01 00 01 69 01 00 01 49 01 00 ........ .i...I.. [00000050] 01 61 01 00 13 4C 6A 61 76 61 2F 6C 61 6E 67 2F .a...Lja va.lang. [00000060] 49 6E 74 65 67 65 72 3B 01 00 01 73 01 00 12 4C Integer. ...s...L [00000070] 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 java.lan g.String [00000080] 3B 01 00 06 3C 69 6E 69 74 3E 01 00 03 28 29 56 .....ini t......V [00000090] 01 00 04 43 6F 64 65 01 00 0F 4C 69 6E 65 4E 75 ...Code. ..LineNu [000000a0] 6D 62 65 72 54 61 62 6C 65 01 00 12 4C 6F 63 61 mberTabl e...Loca [000000b0] 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65 01 00 lVariabl eTable.. [000000c0] 04 74 68 69 73 01 00 0E 4C 6A 76 6D 63 6C 61 73 .this... Ljvmclas [000000d0] 73 74 65 73 74 3B 01 00 04 6D 61 69 6E 01 00 16 stest... .main... [000000e0] 28 5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 ..Ljava. lang.Str [000000f0] 69 6E 67 3B 29 56 01 00 04 61 72 67 73 01 00 13 ing..V.. .args... [00000100] 5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 .Ljava.l ang.Stri [00000110] 6E 67 3B 01 00 04 74 65 73 74 01 00 04 28 49 29 ng....te st....I. [00000120] 56 01 00 08 3C 63 6C 69 6E 69 74 3E 01 00 0A 53 V....cli nit....S [00000130] 6F 75 72 63 65 46 69 6C 65 01 00 11 6A 76 6D 63 ourceFil e...jvmc [00000140] 6C 61 73 73 74 65 73 74 2E 6A 61 76 61 0C 00 11 lasstest .java... [00000150] 00 12 0C 00 0B 00 0C 01 00 06 63 61 6F 68 61 6F ........ ..caohao [00000160] 0C 00 0F 00 10 01 00 0C 6A 76 6D 63 6C 61 73 73 ........ jvmclass [00000170] 74 65 73 74 0C 00 1C 00 1D 07 00 2B 0C 00 2C 00 test.... ........ [00000180] 2D 0C 00 0D 00 0E 01 00 10 6A 61 76 61 2F 6C 61 ........ .java.la [00000190] 6E 67 2F 4F 62 6A 65 63 74 01 00 11 6A 61 76 61 ng.Objec t...java [000001a0] 2F 6C 61 6E 67 2F 49 6E 74 65 67 65 72 01 00 07 .lang.In teger... [000001b0] 76 61 6C 75 65 4F 66 01 00 16 28 49 29 4C 6A 61 valueOf. ...I.Lja [000001c0] 76 61 2F 6C 61 6E 67 2F 49 6E 74 65 67 65 72 3B va.lang. Integer. [000001d0] 00 21 00 05 00 0A 00 00 00 03 00 01 00 0B 00 0C ........ ........ [000001e0] 00 00 00 09 00 0D 00 0E 00 00 00 02 00 0F 00 10 ........ ........ [000001f0] 00 00 00 04 00 01 00 11 00 12 00 01 00 13 00 00 ........ ........ [00000200] 00 42 00 02 00 01 00 00 00 10 2A B7 00 01 2A 04 .B...... ........ [00000210] B5 00 02 2A 12 03 B5 00 04 B1 00 00 00 02 00 14 ........ ........ [00000220] 00 00 00 0E 00 03 00 00 00 01 00 04 00 02 00 09 ........ ........ [00000230] 00 04 00 15 00 00 00 0C 00 01 00 00 00 10 00 16 ........ ........ [00000240] 00 17 00 00 00 09 00 18 00 19 00 01 00 13 00 00 ........ ........ [00000250] 00 4B 00 02 00 02 00 00 00 0F BB 00 05 59 B7 00 .K...... .....Y.. [00000260] 06 4C 2B 10 0A B6 00 07 B1 00 00 00 02 00 14 00 .L...... ........ [00000270] 00 00 0E 00 03 00 00 00 07 00 08 00 08 00 0E 00 ........ ........ [00000280] 09 00 15 00 00 00 16 00 02 00 00 00 0F 00 1A 00 ........ ........ [00000290] 1B 00 00 00 08 00 07 00 1C 00 17 00 01 00 01 00 ........ ........ [000002a0] 1C 00 1D 00 01 00 13 00 00 00 3E 00 02 00 02 00 ........ ........ [000002b0] 00 00 06 2A 1B B5 00 02 B1 00 00 00 02 00 14 00 ........ ........ [000002c0] 00 00 0A 00 02 00 00 00 0C 00 05 00 0D 00 15 00 ........ ........ [000002d0] 00 00 16 00 02 00 00 00 06 00 16 00 17 00 00 00 ........ ........ [000002e0] 00 00 06 00 0D 00 0C 00 01 00 08 00 1E 00 12 00 ........ ........ [000002f0] 01 00 13 00 00 00 20 00 01 00 00 00 00 00 08 05 ........ ........ [00000300] B8 00 08 B3 00 09 B1 00 00 00 01 00 14 00 00 00 ........ ........ [00000310] 06 00 01 00 00 00 03 00 01 00 1F 00 00 00 02 00 ........ ........ [00000310] 20 .
下面来依次分析内容
魔数与版本号
第一个看到的就是传说中的cafebaby他占据了开始的四个字节并且内容一定是0xCAFEBABY
接下来就是主版本号和次版本号了,JVM的版本是向下兼容的也就是说如果当前我们的JDK是1.8的话运行起来的JVM进程会兼容1.7版本JDK所写的字节码文件而这里的检查就是看魔数后面的这两个版本号来检查的
版本号依旧占据了四个字节前两个是主版本号后两个字节是次版本号,咱们这里是00 00 00 34对应的数字就是52也就是JDK1.8 Java的版本号是从45开始计算作为JDK1.1随后依次加1,比如说JDK1.2就是46这样的
常量池
接下来就是重中之重常量池了,其实JVM内部使用C的一系列oop-klass模型的实例对象来储存这些信息,不过这里不做介绍就简单的来分析一下常量池的这部分字节码
首先一定是一个常量池中的常量个数随后跟着一系列的常量元素的信息而每一个元素都是由一个tag和一个date组成
tag作为标签标定了当前元素是那种类型的常量,JVM定义了11中常量如下
这里的tag占据了一个字节
名称 | tag标记 | 含义 |
---|---|---|
1 | CONSTANT_utf8_Info | utf8编码的字符串 |
3 | CONSTANT_Integer_Info | 整形字面量 |
4 | CONSTANT_Float_Info | 浮点型 |
5 | CONSTANT_Long_Info | 长整型 |
6 | CONSTANT_Double_Info | 双精度型 |
7 | CONSTANT_Class_Info | 类或者接口的引用 |
8 | CONSTANT_String_Info | 字符串类型 |
9 | CONSTANT_Fieldref_Info | 字段的引用 |
10 | CONSTANT_Methodref_Info | 方法的引用 |
11 | CONSTANT_InterfaceMrthodref_Info | 接口中方法的引用 |
12 | CONSTANT_NameAndType_Info | 字段和方法名称以及类型的符号引用 |
上面的11种元素类型都在JVM内部以某种C类的结构来储存信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1O8fnB8g-1597565623257)(img/JVM常量池复合元素.png)]
除了utf8以外的结构都没有length这个属性,其他的都是tag+index而这个是tag+byte+length
length的含义就是表示bytes的长度而bytes则是他的内容数据所以说他的长度是1+2+length
而其他的都是1+2或者1+2+2
在JVM内用U2来描述contentpool的元素个数也就是两个字节,我们这里他是0x00 2E也就是46个元素JVM规定0这个位置不能使用可能是为了方便吧。
这里不会全部解析拿出一两个看一下就行
第一个元素
根据上面的分析首先会是一个占据一个字节大小的tag 0X0A这里的tag是10也就是CONSTANT_Methodref_Info
他的结构是 tag index index这样的结构
随后就是两个U2的索引分别是00 0A和 00 21也就是10和33分别代表了常量池中的第十个和第33个常量
第二个元素
随后就是第二个元素了首先tag是09代表了CONSTANT_Fieldref_Info字段信息
他的结构是tag加两个索引所以还是往后读两个U2是00 05和 00 22
其他的也都是这样的元素
访问标记和继承信息
接下来就是access_flag,this_class,super_class和interface了
首先就是U2的access_flag
他也是在JVM内部规定好了的格式
名称 | 标记 | 含义 |
---|---|---|
ACC_PUBLIC | 0x0001 | 是否为public |
ACC_FINAL | 0x0010 | 是否被声明了final |
ACC_SUPER | 0x0020 | 是否允许invokespecial,JDK1.2后编译出来的类这个都为真 |
ACC_INTERFACE | 0x0200 | 标记是一个接口 |
ACC_ABSTRACT | 0x0400 | 标记抽象类或者接口 |
ACC_SYNTHETIC | 0x1000 | 标记这个类并非是用户代码所产生的 |
ACC_ANNOTATION | 0x2000 | 标记是一个注解 |
ACC_ENUM | 0x4000 | 一个枚举 |
我们这里这一项是0x00 21也就是super+public是一个public修饰的class
然后是this_class标记,是一个U2的索引指向常量池中˙的一个元素,我们这里是0x00 05也就是常量池中的第5个元素这个元素是一个classinfo他的index是37在去看37元素是一个Utf8他的bytes是jvmclasstest也就是我们的类名
随后就是superclass了这个和thisclass基本一致的解析也是一个index,我们这里是0x00 0A也就是索引10这里的10是一个classinfo他的index是42我们的42是utf8他的bytes是java/lang/Object
在后面就是我们的interface了我们的类是可以实现很多接口的所以,不出意外和constantpool一样会先有一个interface的count这个byte之后就是一系列的interface了这里的count也是一个u2的数据我们这里是0x00 00因为我们并没有实现接口所以是这样的不过就算有后面的也都是一些指向常量池的u2索引而已。
字段信息
接下来就是我们的field了。其实上面分析了这么多应该有经验了像是这种多个元素的都会是数量+元素数组的形式存在。
那么首先就是一个u2的count,我们这里是0x00 03也就是三个field,和我们的类是完全一致的。
随后就是一系列的field_info了这个fieldinfo也是JVM内部定义的一种结构它由以下几种内容组成
- u2的accessflag这个内容有一个
- u2的fieldname的索引指向constantpool
- u2的描述信息也是一个index
- u2的field的attribute数量
- 一系列的attribute_info
这就是field的结构,下面来分析一下我们的例子类
我们拿出个field来看,我们在类里定义的第一个field是 public int i = 1;
首先是accessflag:00 01是一个public
fieldname:00 0B index11->utf8的i
描述:00 0C。index12是一个utf8的大写的I代表了int
count:00 00也就是没有内部attribute
方法信息
method和field的内容基本上是一样的首先也是一个u2的count
随后就是一系列的method_info了甚至methodinfo的结构和fieldinfo也是一样的感兴趣的可以自己分析一下这个16进制文件
本文地址:https://blog.csdn.net/qq_43147121/article/details/108035464