Class文件结构详解,教你如何自己看懂二进制文件

您所在的位置:网站首页 su二进制文件是什么 Class文件结构详解,教你如何自己看懂二进制文件

Class文件结构详解,教你如何自己看懂二进制文件

2024-07-12 14:53| 来源: 网络整理| 查看: 265

什么是Class文件?

  Class文件是.java后缀文件通过编译生成.class后缀的文件(以下简称Class文件)

  Class文件内部本质上是二进制的,但是这个二进制串,计算机是不能够直接读取并且执行的。也就是说,计算机看不懂,而我们的JVM解决了这个问题,JVM可以看作是一个翻译官,它可以看懂,而且它也知道计算机想要什么样子的二进制,所以它可以把Class文件的二进制翻译成计算机需要的样子。

  ps: 二进制码只有0或1,每一个0或1代表一位(bit),八位就可以代表一个字节(byte)。这也是网上常说的Class文件是8位的二进制字节流的原因。

初探Class文件

  我们直接通过工具打开一个Class文件(具体工具在文章最下方),先看16进制的,因为2进制一眼下去肯定看懵了。我们先不用去管它.java文件具体是什么样子的。首先跟着下面的方法看完一遍,你也能轻而易举的去解读自己写的类的16进制源码。(下面需要频繁对照这个图,可以看截图出来再继续阅读)

       

Class文件结构概览

  Class文件主要包括以下内容(大概了解就可以,不用完全记住)

    magic_number

    minor_version、major_version

    constant_pool_length、constant_pool

    access_flag

    this_class、super_class

    interfaces_count、interfaces

    field_count、fields

    methods_count、methods

    attrbutes_count、attributes

Class文件结构详解

  接下来结合图片和具体结构对文件结果进行字符级别的详细解读(都是按照字符顺序依次解读)

MagicNumber

  第一个字节CA就代表了两个16进制的数也就是8位一个字节,同样FE、BA、BE也是。这四个字节联合起来称为MagicNumber(魔法数~咖啡宝贝~)

  意义就是表明了这个文件是一个符合class文件定义规范的文件。使用者可以用class文件特有的格式规范来解读它。

  接下来我们就用它特有的格式继续进行解读(其他的Class文件都是一样的,不同点也仅仅是可变内容不同,比如自己写的变量名、代码之类的)

MinorVersion & MajorVersion

  继续看000行的04和05位置上的两个字节,这两个字节联合起来被称为MinorVersion(小版本号)

  当前小版本号是 00 00也就是16位的二进制000...000(16个0),就表明了小版本号是0。

  聪明的同学已经想到了,有小版本,那肯定有大版本,没错紧接着还是000行的06和07位置上的两个字节联合被用作表示MajorVersion(大版本号),当前的大版本号是00 34(别忘了当前是16进制)换算成10进制就是52。

  综合这4个字节的版本号就出来了:52.0,是不是很眼熟?没错,就是你每次打开class文件上面会出现的那一行提示信息当前版本52.0(也就是JDK1.8,如果是1.9的话有可能是53.0,这个有兴趣可以自己去验证一下)

ConstantPoolLength

  继续,000行的08 09位置这两字节被联合起来表示常量池的大小。

  当前文件的常量池的大小是00 3E 换算成10进制就是62。(什么是常量池?这个先别担心,下面会讲到)当然虽然计算出来确实是62,但是实际上常量池内只保存了61个对象,原因是它预留了第0个位置不保存任何对象,也就用用作备用的一个位置。具体用来干什么,如果你仔细观察Object类的Class文件,你会发现Object这个顶级类的父类索引指向的是这个0的槽位。

  那么接下来常量池的大小既然表示完了,理论上就应该是常量池的具体实现了,

  没错,你猜对了。但是!要注意一点上面虽然计算出了常量池的大小实际是61,这可不代表接下来只有61个字符用来存储常量池。

ConstantPool

  常量池的表示十分复杂,如果把常量池弄明白,可以说Class文件已经懂了个大概了。因为常量池的对象类型有很多个,我们不一一解释,而是转为教你如何去自己看懂一个常量池里的对象!

  首先我们看000行0A列这个位置上的字节。好吧,好巧。。。它的值也是0A(不要被误导就好)转换成10进制就是10

  这个是什么呢?这个是常量池中第一个对象的tag(他的索引位置就是01),在后面的常量池对象类型表中可以找到tag为10的常量池对象,我们发现是CONSTANT_Methodref_info。

  继续在常量池对象类型表中看他的细化结构,我们发现它有三个属性,分别是tag、class_index、name_and_type_index。

  其中tag属性就是我们刚才看到的0A也就是十进制10这个字节。

  再看class_index这个属性类型是u2,同时故名思议,他是一个占用了2个字节的类索引。

    ps 一共有四种类型 u1 u2 u4 u8 分别代表了占用的字节数

  带着这个推断,我们去验证以下,0A这个字节代表了tag那么接下来就是class_index这个占用了两个字节的属性了,也就是0A(列)的下面0B、0C两列位置的两个字节,从上图可以发现,它们分别是00 0B,转换成10进制就是11这个数值,也就说明了class_index=11。有了这个结论,我们就可以知道它是需要索引位置为11的这个常量池对象了。

  其实这里可以借助插件工具可视化的看到所有的常量池对象的索引。

  这里先不用工具我们一个一个去数。最后我们可以得到第030行04列就是对应索引位置11的常量池对象,它的首个字节也就是它的tag是07,十进制7对应的正好是我们所期待的CONSTANT_Class_info这个类型。

  接下来就需要再看CONSTANT_Class_info这个类型的第二个属性name_index值为52,也就是索引为52的那个对象。

  照葫芦画瓢,同样通过工具或者直接挨个数,发现如下图,它藏在第2E0行的0B列这个位置。

  ok按照惯例,继续解析它的tag发现是01,tag1的类型是CONSTANT_Utf8_info,CONSTANT_Utf8_info第二个属性占用了两个字节,代表数组长度是十六进制的10,换算成10进制就是16。也就是说后面16个字节就是它所保存的内容!

  那么下面就是见证奇迹的时刻!我们看第一个字节6A,去对照ASCII表。发现它代表了" j "这个字母,再下面一个61代表字母" a ",继续往下面逐个翻译,最终这16个谜一般的16进制字节码,终于揭开了神秘的面纱,变成了java/lang/Object

  至此,对于上面我们看到的第一个常量池对象有个另一个初步的了解,他是一个类中的方法,是哪个类呢?哦!原来是Object类!

  这时候你可能有疑问,它是一个方法?那它究竟是哪个方法呢?这就就需要继续解读它的第二个属性name_and_type_index,通过如上步骤(再次强调,有可视化工具更方便查看)我们最终会发现它是一个方法,也就是Object类的初始化方法~

          

  一个常量池的对象就解析完毕了。剩下的可以继续自己一点一点去解析。

  关于剩下的一下比如access_flag、this_class的索引super_class的索引,都可以按照这个方法去计算观察。遇到疑惑的地方,除了去网上看,最好还是去官网找一些官方的资料。

  PS: 如果需要继续解析剩下的内容可以留言,下次可以出一篇关于剩下的内容的解析。

      

  表格列出了大部分内容,其他内容可以去官网找资料看。排版凌乱,请见谅。

  推荐相关辅助工具,可以更高效的阅读Class文件

  Idea(编译器),jclasslib(idea内的一款插件,可以将class文件可视化的展示出来)



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3