用一个例子来做说明字节码文件的解析

字节码文件解析

最近有回过头看了一下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进制查看工具

一个字节码文件大概是由以下几个部分构成

  1. 魔数
  2. 版本号
  3. 常量池
  4. 这个类的access_flag
  5. this_class
  6. super_class
  7. interfaces
  8. fileds
  9. methods
  10. 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标记含义
1CONSTANT_utf8_Infoutf8编码的字符串
3CONSTANT_Integer_Info整形字面量
4CONSTANT_Float_Info浮点型
5CONSTANT_Long_Info长整型
6CONSTANT_Double_Info双精度型
7CONSTANT_Class_Info类或者接口的引用
8CONSTANT_String_Info字符串类型
9CONSTANT_Fieldref_Info字段的引用
10CONSTANT_Methodref_Info方法的引用
11CONSTANT_InterfaceMrthodref_Info接口中方法的引用
12CONSTANT_NameAndType_Info字段和方法名称以及类型的符号引用

上面的11种元素类型都在JVM内部以某种C类的结构来储存信息

除了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_PUBLIC0x0001是否为public
ACC_FINAL0x0010是否被声明了final
ACC_SUPER0x0020是否允许invokespecial,JDK1.2后编译出来的类这个都为真
ACC_INTERFACE0x0200标记是一个接口
ACC_ABSTRACT0x0400标记抽象类或者接口
ACC_SYNTHETIC0x1000标记这个类并非是用户代码所产生的
ACC_ANNOTATION0x2000标记是一个注解
ACC_ENUM0x4000一个枚举

我们这里这一项是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内部定义的一种结构它由以下几种内容组成

  1. u2的accessflag这个内容有一个
  2. u2的fieldname的索引指向constantpool
  3. u2的描述信息也是一个index
  4. u2的field的attribute数量
  5. 一系列的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进制文件

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值