索引:
全文的分析都基于class字节码,它与android dex字节码有很大的区别。class字节码是以类为单位组织的,而dex是多个类的集合
一.class字节码是什么?
字节码是由jvm规范所定义的一套描述class内容的结构,由于它的描述与指令集的平台无关性,只要能够将自己的语言翻译成为字节码,就都能被虚拟机执行,这样的语言如java,groovy,kotlin等。hotpot虚拟机是jvm规范的标准实现。
二.class字节码的简单例子看全貌
眼见为实,我们定义一个Test.java类
public class Test {
static String a = "hucaihua";
}
复制代码
通过编译得到Test.class,通过010Editor查看它的内容,可以看出它是用字节为单位组织的二进制内容,也就是说它的本质是用01串存储的二进制流。,
在这里软件为了方便展示,以16进制来表示,一个16进制需要用4位二进制来表示,因此这里的每一个隔开的数据,代表的就是一个字节8位。
CA FE BA BE 00 00 00 37 00 14 0A 00 05 00 0F 08
00 10 09 00 04 00 11 07 00 12 07 00 13 01 00 01
61 01 00 12 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53
74 72 69 6E 67 3B 01 00 06 3C 69 6E 69 74 3E 01
00 03 28 29 56 01 00 04 43 6F 64 65 01 00 0F 4C
69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65 01 00
08 3C 63 6C 69 6E 69 74 3E 01 00 0A 53 6F 75 72
63 65 46 69 6C 65 01 00 09 54 65 73 74 2E 6A 61
76 61 0C 00 08 00 09 01 00 08 68 75 63 61 69 68
75 61 0C 00 06 00 07 01 00 04 54 65 73 74 01 00
10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63
74 00 21 00 04 00 05 00 00 00 01 00 08 00 06 00
07 00 00 00 02 00 01 00 08 00 09 00 01 00 0A 00
00 00 1D 00 01 00 01 00 00 00 05 2A B7 00 01 B1
00 00 00 01 00 0B 00 00 00 06 00 01 00 00 00 01
00 08 00 0C 00 09 00 01 00 0A 00 00 00 1E 00 01
00 00 00 00 00 06 12 02 B3 00 03 B1 00 00 00 01
00 0B 00 00 00 06 00 01 00 00 00 02 00 01 00 0D
00 00 00 02 00 0E
复制代码
三.class字节码的结构体
我们知道,class是格式化的二进制流,也就是它的二进制流是按照jvm虚拟机规范规定的格式来表示内容的,在jvm中class定义如下:
ClassFile {
u4 magic; //魔数,固定值0xCAFEBABE
u2 minor_version; //次版本号
u2 major_version; //主版本号
u2 constant_pool_count; //常量池的个数
cp_info constant_pool[constant_pool_count-1]; //常量池内容
u2 access_flags; //class 访问标识
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]; //属性内容
}
复制代码
其中 u4,u2都是固定长度的描述字段,u4长度为4个字节,u2长度为2个字节。
cp_info , field_info , method_info , attribute_info 为结构体,每个结构体有单独的定义,并且它们的长度都是变化的。
四.class结构体重点结构说明
4.1 cp_info(18种常量类型的描述)
cp_info是class中占用字节数最多的块,它用不同的结构体定义了18种常量类型,这18种常量类型都用tag来表示它的具体类型。
下图分析了CONSTANT_Class_info中的实际内容,其它内容的查找方式一样,不再继续分析。
4.2 field_info(字段描述)
field_info的定义如下:
field_info {
u2 access_flags; //访问标志
u2 name_index; //名字在常量中的索引
u2 descriptor_index; //描述在常量中的索引
u2 attributes_count; //属性的个数
attribute_info attributes[attributes_count]; //属性列表
}
复制代码
- access_flags 访问标志一共包含如下9类,常用的如public,static,private,protected等:
Flag Name Value Interpretation
ACC_PUBLIC 0x0001 Declared public; may be accessed from outside its package.
ACC_PRIVATE 0x0002 Declared private; usable only within the defining class.
ACC_PROTECTED 0x0004 Declared protected; may be accessed within subclasses.
ACC_STATIC 0x0008 Declared static.
ACC_FINAL 0x0010 Declared final; never directly assigned to after object construction (JLS §17.5).
ACC_VOLATILE 0x0040 Declared volatile; cannot be cached.
ACC_TRANSIENT 0x0080 Declared transient; not written or read by a persistent object manager.
ACC_SYNTHETIC 0x1000 Declared synthetic; not present in the source code.
ACC_ENUM 0x4000 Declared as an element of an enum.
复制代码
4.3 attribute_info (属性描述)
属性描述信息可以作用于ClassFile , field_info,method_info,cod_attribute的描述。
我们常见的例如泛型,注解等都属于attribute_info的范畴。
它的定义如下:
attribute_info {
u2 attribute_name_index; //属性名字如<Signature>表示泛型,<RuntimeVisibleAnnotations>表示注解等
u4 attribute_length; //属性长度
u1 info[attribute_length]; //属性信息
}
复制代码
下图用一个例子来说明属性
4.4 method_info (方法描述)
方法描述的定义与field_info一致,不再重复说明:
method_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
复制代码
方法描述(method_info)与字段描述(field_info)最大的区别在于attribute_info不一样,
所有method_info都包含一个attribute_info,它的名字叫code,用于包含方法的指令信息。
下图体现了我们定义的get方法中的指令信息