读书笔记之java反编译(1)

 此读书笔记根据《深入java虚拟机第二版》所得,由于能力问题只能从能看懂的地方(第六章)开

始阅读,如有疑问请查阅《深入java虚拟机第二版》。
读书笔记之java反编译
1.java class文件是什么,请百度或者google搜索或者参看《深入java虚拟机第二版》第六章第

一节
2.class文件的内容以及格式
  介绍三个概念 (以下概念均出自

http://www.blogjava.net/javafan/archive/2008/02/01/jclass_ana.html)

1)数据类型:《JVM Spec》(2nded)中指出,Java Class文件的数据用自己定义的一个数据类型集

来表示,即u1,u2,u4,分别用于表示一个无符号类型的、占1,2,4个字节的数据。在《Inside

JVM》(2nded)一书中,作者把这个数据类型集称之为Class文件的基本类型,本人觉得比较形象,

便于理解。所以,在本文中,我们也用基本类型来表示Java Class文件的数据。

2)表:根据《JVM Spec》(2nded)中的定义,表(table)由项(定义见3)组成,用于几种Class文

件结构中。《JVM Spec》(2nded)中指出,Java Class文件格式用一个类似于C结构的记号编写的

伪结构来表示。这个伪结构指的就是这里的表,例如下面的ClassFile表就是这种伪结构的一个典

型例子,下文中所有的表都是指这种伪结构的表。表的大小是可变的,这是因为它的组成部分项

是可变的。注意;这里的可变是针对Class层次而言的,即在不同的Class文件中该项的大小可能

不一样的,但是对于每一个具体的Class文件来说,这个项的大小又是一定的,因而这个表的大小

也是一定的。那么,项为什么是可变的呢?请看下面的分析。

3)项:描述Java Class文件格式的结构的内容称为项(items)。每个项都有自己的类型和名称。项

的类型可能是基本类型,也可能是一个表的名字,这种项都是一些数组项。数组项的每一个元素

都是一个表,这个表同顶层的ClassFile表一样,也都是一种伪结构,也都是由一些项构成的,而

且这些表不一定是同一种格式的,因此数组项也可以看作一个可变大小的结构流J。这些表对于该

数组项来说就是子项,当然子项可能还有子项(目前子项的深度最多就两层)。项的名称,没有什

么好说的,就是《JVM Spec》(2nded)中指定的一些名称。另外,项也是有大小的,对于没有子项

的项来说,其大小是固定的;对于有子项的项来说,其大小是可变的。在一个具体的Class文件中

,一个可变项(数组)的大小都会在其前一项中指定,为什么会是这样的呢?因为《JVM Spec》

(2nded)中就是这么定义的!在Class文件中,每个项按规范中定义好的顺序存储在Class文件中,

相邻的项之间没有任何间隔,连续的项(数组)也是按顺序存储,不进行填充或者对齐,这样可

以使Class文件紧凑。

首先要来解析一下ClassFile表结构,这是《JVM Spec》(2nded)中定义的Class文件最外层的结构

,换言之,就是Class文件的格式。
ClassFile表结构
    ClassFile {
        u4 magic;

        u2 minor_version;

        u2 major_version;

        u2 constant_pool_count;

        cp_info constant_pool[constant_pool_count-1];

        u2 access_flags;

        u2 this_Class;

        u2 super_Class;

        u2 interfaces_count;

        u2 interfaces[interfaces_count];

        u2 fields_count;

        field_info fields[fields_count];

        u2 methods_count;

        method_info methods[methods_count];

        u2 attributes_count;

        attribute_info attributes[attributes_count];


    }

ClassFile表结构由16个不同的项组成,其中的各项可以简要地分析如下:

(1) magic

每个Class文件的前4个字节被称为它的魔数(magic number): 0xCAFEBABE。魔数的作用在于:

可以轻松地分辨出Java Class文件和非Java Class文件。(如果一个文件不是以0xCAFEBABE开头

,它就肯定不是Java Class文件,因为它不符合规范J)。当Java还称为“Oak”的时候,这个魔

数就已经定下来了,它预示了Java这个名字的出现。魔数的来历请大家自己查阅J

(2) minor_version和major_version

Class文件的下面4个字节包含了次、主版本号。通常只有给定主版本号和一系列次版本号后,

Java虚拟机才能够读取Class文件。如果Class文件的版本号超出了Java虚拟机所能够处理的有效

范围,Java虚拟机将不会处理该Class文件。例如J2SE5.0版本的虚拟机就不能执行由J2SE6.0版本

的编译器编译出来的Class文件。

(3) constant_pool_count

版本号后面的项是constant_pool_count即常量池计数项,该项的值必须大于零,它给出该Class

文件中常量池列表项的元素个数,这个计数项包括了索引为0的constant_pool表项,但是该表项

不出现在Class文件的constant_pool列表中,因为它被保留为Java虚拟机内部实现使用了,因此

常量池列表的元素个数constant_pool_count-1,各个常量池表项的索引值分别为1到

constant_pool_count-1。

注:在这里,有几个术语需要解释一下,常量池即为constant_pool,常量池列表就是指

constant_pool[ ],常量池表项即指常量池列表中的某一个具体的表项(元素)。这些常量池表项

的可能类型如下述的cp_type表所示:

cp_type
入口类型                                标志值

CONSTANT_Class                           7

CONSTANT_Fieldref                        9

CONSTANT_Methodref                       10

CONSTANT_InterfaceMethodref              11

CONSTANT_String                           8

CONSTANT_Integer                          3

CONSTANT_Float                            4

CONSTANT_Long                             5

CONSTANT_Double                           6

CONSTANT_NameAndType                      12

CONSTANT_Utf8                              1
 

(4) constant_pool[ ]

constant_pool_count项下面是constant_pool[ ]项,即常量池列表,其中存储了该ClassFile结

构及其子结构中引用的各种常量,诸如文字字符串、final变量值、类名和方法名等等。在Java

Class文件中,常量池表项是用一个cp_info结构来描述的,常量池列表就是由

constant_pool_count-1个连续的、可变长度的cp_info表结构构成的constant_pool[ ]数组。为

什么是constant_pool_count-1个constant_pool的原因,在上面已经解释了。每一个常量池表项

都是一个变长结构,其通常格式如下所示:

cp_info
cp_info {
        u1 tag;
        u1 info[];
    }

cp_info表的tag项是一个无符号的byte类型值,它表明了cp_info表的类型和格式,具体的tag类

型见上表。

需要说明的是,cp_info只是一个抽象的概念,在Class文件中,它表现为一系列具体的、形如

CONSTANT_Xxxx_info的constant_pool结构,其具体的格式由cp_info表的tag项(即第一个字节)

来确定。不同的cp_info表,其info[]项也是不一样的,例如,CONSTANT_Class_info表的info[]

项为“u2 name_index”,而CONSTANT_Utf8_info表的info[]项为“u2 length; u1 bytes

[length];”,显然,这两个cp_info表是不一样的,大小更是不一样的,因而常量池表项的大小

是可变的。由于常量池列表中的每个常量池表项的结构是不一样,因此常量池列表的大小也是可

变的。在Class文件中,常量池列表项是一个可变长度的结构流。

由cp_info表以及cp_type表我们可以知道,若cp_info表中tag(标志)项的值为1时,当前的

cp_info就是一个CONSTANT_Utf8_info表结构,若cp_info表中tag项的值为3,当前的cp_info就是

一个CONSTANT_Integer_info表结构,其它情况类推。这些表的结构可以查阅《JVM Spec》

(2nded)的第四章或者《Inside JVM》(2nded)的第六章。

(5) access_flags

紧接常量池后的两个字节称为access_flags,access_flags项描述了该Java类型的一些访问标志

信息。例如,访问标志指明文件中定义的是类还是接口;访问标志还定义了在类或接口的声明中

,使用了哪些修饰符;类和接口是抽象的还是公共的等等。实际上,access_flags项的值是Java

类型声明中使用的访问标志符的掩码(mask,这里掩码指的是access_flags的值是所有访问标志值

的总和,当然,未被使用的标志位在Class文件中都被设置为0。例如,若access_flags的值就是

0x0001,就表示该Java类型的访问标志符是ACC_PUBLIC;若access_flags的值是0x0011,就表示

该Java类型的访问标志符是ACC_PUBLIC和ACC_FINAL,因为只有这两个标志位的和才可能是0x0011

;其它情况类推)。

一个Java类型的所有access_flags标志符如下表所示:

 

access_flags
标志名称         值           含义

ACC_PUBLIC     0x0001   声明为public,可以从它的包外访问

ACC_FINAL      0x0010   声明为final,不允许有子类

ACC_SUPER      0x0020   用invokespecial指令处理超类的调用

ACC_INTERFACE  0x0200   表明是一个接口,而不是一个类

ACC_ABSTRACT   0x0400   声明为abstract,不能被实例化
 
需要说明的是,这是针对一个Java类型的访问标志符列表,有的标志符只有类可以使用,有的标

志符只有接口才可以使用,详情请查阅《JVM Spec》(2nded)。

(6) this_class

接下来的两个字节为this_class项,其值为一个对常量池表项的索引,即它指向一个常量池表项,

而且该常量池表项必须为CONSTANT_Class_info表的结构。该表有一个name_index项,该项将指向

另一个常量池表项,该表项包含了该类或者接口的完全限定名称。

(7) super_class

紧接着this_class之后的两个字节是super_class项,该项必须是对常量池表项的一个有效索引或

者值为0。如果super_class项的值为0,则该Class文件必须表示java.lang.Object类。如果

super_class项的值不为0,则又分为两种情况,若该Class文件表示一个类,则super_class项必

须是对常量池中该类的超类的CONSTANT_Class_info表项的索引,这个超类和它的任何超类都不能

是一个final类;若该Class文件表示一个接口,则super_class项必须是对常量池中表示

java.lang.Object类的一个CONSTANT_Class_info表项的索引。

(8) interfaces_count和interfaces[ ]

紧接着super_class项后面的两个字节是interfaces_count项,此项表示由该类直接实现或者由该

接口所扩展的超接口的数量。

紧接着interfaces_count项后面的是interfaces列表项,它包含了由该类直接实现或者由该接口

所扩展的超接口的常量池索引,共计interfaces_count个索引。interfaces列表中的常量池索引

按照该类型在源代码中给定的从左到右的顺序排列。

(9) fields_count和fields[ ]

接下来的是fields_count项,该项的值给出了fields列表项中的field_info表结构的数量,即表

示了该Java类型声明的类变量和实例变量的个数总和。

fields列表项包含了在该Java类型中声明的所有字段的完整描述。fields列表中的每个

field_info表项都完整地表示了一个字段的信息,包括该字段的名称、描述符和修饰符等。这些

信息有的放在field_info表中,如修饰符;有的则放在field_info表所指向的常量池中,如名字

和描述符。同前面的分析,fields列表项也是一个变长结构。   

需要说明的是,只有在该Java类型中声明的字段才可能在fields列表中列出,fields列表中不包

括从超类或者超接口中继承而来的字段信息。

(10) methods_count和methods[ ]

在Class文件中,紧接着fields后面的是对在该Java类型中所声明的方法的描述。首先是

methods_count项,它占两个字节长度,它的值表示对该Java类型中声明的所有方法的总计数。

methods_count项后面是methods列表项,它由methods_count个连续的method_info表构成。每个

method_info表都包含了与一个方法相关的信息,如方法名、描述符(即方法的返回值及参数类型

)以及一些其它信息。如果一个方法既非abstract也非native,那么该method_info表将包含该方

法局部变量所需的栈空间长度、为方法所捕获的异常表、字节码序列以及可选的行号表和局部变

量表等信息。

需要说明的是,只有在该Java类型中显式定义的方法才可能在fields列表中列出,fields列表中

不包括从超类或者超接口中继承而来的方法信息。

(11) attributes_count和attributes[ ]

Class文件中最后的部分是属性(attribute),它给出了在该Java类型中所定义的属性的基本信息

。首先是attributes_count项,它占两个字节长度,它的值表示在后续的attributes列表中的

attributes_info表的总个数。每个attributes_info表的第一项都是对常量池中

CONSTANT_Utf8_info表项的一个索引,该表给出了此属性的名称。

需要说明的是,属性有很多种,在Class文件中的很多地方都出现了属性这一项,在顶层

ClassFile表中有attributes属性项,在field_info表中也有attributes属性项,在method_info

中也有attributes属性项,但是它们各有各的功能,详见上述分析。在《JVM Spec》(2nded)中,

为ClassFile表结构的attributes列表项定义的唯一属性是SourceFile属性,为field_info表结构

的attributes列表项定义的唯一属性是ConstantValue属性,为method_info表结构的attributes

列表项定义的属性是Code属性和Exceptions属性。

总而言之,Class文件格式是一个规范性的格式。这个规范指的就是,上面提到的这些表结构本身

的规范性,以及这些表结构之间的包含关系的规范性。实际上,《JVM Spec》(2nded)中就是通过

表和项这两个概念来组织Class文件的格式的。首先,ClassFile表就是Class文件最外层的结构,

换言之,这就是Class文件的格式。其次,ClassFile表又是一些项组成的,这些项的内容都要符

合《JVM Spec》(2nded)中定义的规范,具体来说,若这个项的类型是基本类型,该项的值要符合

规范,例如magic项一定要是0xCAFEBABE,access_flags项的值一定要是有效的标志值等等;若这

个项的类型是一个表名,即该项是一个数组项,那么该数组项列表中的每一个表项都要是一个合

法的、规范的表,不能是一个规范中没有定义的新表,这就是包含关系的规范性,同样,列表项

中的每个表项本身也都要是符合其规范定义的表项,例如常量池列表中的某个

CONSTANT_Class_info表的name_index项不是对一个CONSTANT_Utf8_info表结构的索引,那么这个

常量池的表项就不是一个合法的表项,因而这个常量池列表项就是不符合规范的,因而整个文件

就是不符合规范的。

 

 

好了检查了一遍,也没费多大事儿,做了做ctl+c和ctrl+v,这是必要工作,要想反编译他,就得先了解他

 

 

 

 

你可能感兴趣的:(读书笔记之java反编译(1))