class文件分析

本文通过分析一个简单java类文件的字节码,希望借此能快速了解java类文件格式

为了分析字节码,必须有一个整体的格式如下:



以上面的表作为分析的基础,开始行动!


一段简单的java代码

1
2
3
4
5
6
7
8
9
package org.kaka.clazz;
                                                                                                                                           
public class TestClass {
         private int m;
                                                                                                                                                    
         public int inc(){
                 return m+1;
         }
}


查看字节码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
00000000  ca fe ba be 00 00 00 32  00 13 0a 00 04 00 0f 09  |.......2........|
00000010  00 03 00 10 07 00 11 07  00 12 01 00 01 6d 01 00  |.............m..|
00000020  01 49 01 00 06 3c 69 6e  69 74 3e 01 00 03 28 29  |.I...<init>...()|
00000030  56 01 00 04 43 6f 64 65  01 00 0f 4c 69 6e 65 4e  |V...Code...LineN|
00000040  75 6d 62 65 72 54 61 62  6c 65 01 00 03 69 6e 63  |umberTable...inc|
00000050  01 00 03 28 29 49 01 00  0a 53 6f 75 72 63 65 46  |...()I...SourceF|
00000060  69 6c 65 01 00 0e 54 65  73 74 43 6c 61 73 73 2e  |ile...TestClass.|
00000070  6a 61 76 61 0c 00 07 00  08 0c 00 05 00 06 01 00  |java............|
00000080  18 6f 72 67 2f 6b 61 6b  61 2f 63 6c 61 7a 7a 2f  |.org/kaka/clazz/|
00000090  54 65 73 74 43 6c 61 73  73 01 00 10 6a 61 76 61  |TestClass...java|
000000a0  2f 6c 61 6e 67 2f 4f 62  6a 65 63 74 00 21 00 03  |/lang/Object.!..|
000000b0  00 04 00 00 00 01 00 02  00 05 00 06 00 00 00 02  |................|
000000c0  00 01 00 07 00 08 00 01  00 09 00 00 00 1d 00 01  |................|
000000d0  00 01 00 00 00 05 2a b7  00 01 b1 00 00 00 01 00  |......*.........|
000000e0  0a 00 00 00 06 00 01 00  00 00 03 00 01 00 0b 00  |................|
000000f0  0c 00 01 00 09 00 00 00  1f 00 02 00 01 00 00 00  |................|
00000100  07 2a b4 00 02 04 60 ac  00 00 00 01 00 0a 00 00  |.*....`.........|
00000110  00 06 00 01 00 00 00 07  00 01 00 0d 00 00 00 02  |................|
00000120  00 0e                                             |..|
00000122

step 1) java magic number

    • 首先的四个字节0x ca fe ba be 即class文件的magic number


step2) java version

    • 接下来的四个字节0x 00 00 00 32 即class文件的版本号(可参看class文件的版本号列表)


step3) 常量池

        接下来是描述常量表的长度0x 00 13,一共是(19-1)项,人肉分析如下

    • 第1项

      • tag: 0x 0a ,CONSTANT_Methodref_info即方法声明

      • index: 0x 00 04,指向常量池中CONSTANT_Class_info,见常量池第4项

      • index: 0x 00 0f,指向常量池中CONSTANT_NameAndType_info见常量池第15项

    • 第2项

      • tag 0x 09,CONSTANT_Fieldref_info即字段声明

      • index: 0x 00 03,指向常量池中CONSTANT_Class_info,见常量池第3项

      • index: 0x 00 10,指向常量池中CONSTANT_NameAndType_info见常量池第16项

    • 第3项

      • tag:0x 07,CONSTANT_Class_info即类声明

      • index: 0x 00 11,指向常量池中的CONSTANT_Utf8_info,第17项

    • 第4项

      • tag:0x 07,CONSTANT_Class_info即类声明

      • index: 0x 00 12,指向常量池中的CONSTANT_Utf8_info,第18项

    • 第5项

      • tag:0x 01,CONSTANT_Utf8_info即字符串说明

      • length: 0x 00 01 ,长度为1个字节

      • bytes: 0x 6d, 内容为"m"

    • 第6项

      • tag:0x 01,CONSTANT_Utf8_info即字符串说明

      • length: 0x 00 01 ,长度为1个字节

      • bytes: 0x 49, 内容为"I"

    • 第7项

      • tag:0x 01,CONSTANT_Utf8_info即字符串说明

      • length: 0x 00 06 ,长度为6个字节

      • bytes: 0x 3c 69 6e  69 74 3e, 内容为"<init>"

    • 第8项

      • tag:0x 01,CONSTANT_Utf8_info即字符串说明

      • length: 0x 00 03 ,长度为3个字节 

      • bytes: 0x 28 29 56 内容为"()V"

    • 第9项

      • tag:0x 01,CONSTANT_Utf8_info即字符串说明

      • length: 0x 00 04 ,长度为4个字节 

      • bytes: 0x 43 6f 64 65 内容为"Code"

    • 第10项

      • tag:0x 01,CONSTANT_Utf8_info即字符串说明  

      • length: 0x 00 0f ,长度为15个字节 

      • bytes: 0x 4c 69 6e 65 4e 75 6d 62 65 72 54 61 62  6c 65内容为"LineNumberTable"  

    • 第11项

      • tag:0x 01,CONSTANT_Utf8_info即字符串说明

      • length: 0x 00 03,长度为3个字节

      • bytes: 0x 69 6e 63 内容为"inc"

    • 第12项

      • tag:0x 01,CONSTANT_Utf8_info即字符串说明

      • length: 0x 00 03,长度为3个字节

      • bytes: 0x 28 29 49 内容为"()I"

    • 第13项

      • tag:0x 01,CONSTANT_Utf8_info即字符串说明

      • length: 0x 00 0a,长度为10个字节

      • bytes: 0x 53 6f 75 72 63 65 46  69 6c 65内容为"SourceFile"

    • 第14项

      • tag:0x 01,CONSTANT_Utf8_info即字符串说明

      • length: 0x 00 04,长度为14个字节

      • bytes: 0x 54 65  73 74 43 6c 61 73 73 2e  6a 61 76 61内容为"TestClass.java"

    • 第15项

      • tag:0x 0c,CONSTANT_NameAndType_info即字段或者方法说明

      • index:0x 00 07 ,字段或者方法常量索引,见常量池第7项

      • index:0x 00 08,字段或者方法常量索引,见常量池第8项

    • 第16项

      • tag:0x 0c,CONSTANT_NameAndType_info即字段或者方法说明

      • index:0x 00 05 ,字段或者方法常量索引,见常量池第5项

      • index:0x 00 06,字段或者方法常量索引,见常量池第6项

    • 第17项

      • tag:0x 01,CONSTANT_Utf8_info即字符串说明

      • length: 0x 00 18,长度为24个字节

      • bytes: 0x 6f 72 67 2f 6b 61 6b  61 2f 63 6c 61 7a 7a 2f 54 65 73 74 43 6c 61 73  73内容为"org/kaka/clazz/TestClass"

    • 第18项

      • tag:0x 01,CONSTANT_Utf8_info即字符串说明

      • length: 0x 00 10,长度为16个字节

      • bytes: 0x 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62  6a 65 63 74 内容为"java/lang/Object"


可以看出

  • CONSTANT_Methodref_info、CONSTANT_Fieldref_info都 依赖于CONSTANT_Class_info、CONSTANT_NameAndType_info

  • CONSTANT_Class_info依赖于CONSTANT_Utf8_info,后者用于声明类的名字

  • CONSTANT_NameAndType_info依赖于两个CONSTANT_Utf8_info

    • 第一个CONSTANT_Utf8_info用于说明方法或者字段的名字

    • 第一个CONSTANT_Utf8_info用于说明方法的入参以及返回类型或者字段的类型

  • 列举常量池结构




可以对比下javap的结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
Compiled from "TestClass.java"
public class org.kaka.clazz.TestClass extends java.lang.Object
   SourceFile: "TestClass.java"
   minor version: 0
   major version: 50
   Constant pool:
const #1 = Method   #4.#15; //  java/lang/Object."<init>":()V
const #2 = Field    #3.#16; //  org/kaka/clazz/TestClass.m:I
const #3 = class    #17;    //  org/kaka/clazz/TestClass
const #4 = class    #18;    //  java/lang/Object
const #5 = Asciz    m;
const #6 = Asciz    I;
const #7 = Asciz    <init>;
const #8 = Asciz    ()V;
const #9 = Asciz    Code;
const #10 = Asciz   LineNumberTable;
const #11 = Asciz   inc;
const #12 = Asciz   ()I;
const #13 = Asciz   SourceFile;
const #14 = Asciz   TestClass.java;
const #15 = NameAndType #7:#8;//  "<init>":()V
const #16 = NameAndType #5:#6;//  m:I
const #17 = Asciz   org/kaka/clazz/TestClass;
const #18 = Asciz   java/lang/Object;
                                                                                           
{
public org.kaka.clazz.TestClass();
   Code:
    Stack=1, Locals=1, Args_size=1
    0:   aload_0
    1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
    4:   return
   LineNumberTable:
    line 3: 0
                                                                                           
                                                                                           
public int inc();
   Code:
    Stack=2, Locals=1, Args_size=1
    0:   aload_0
    1:   getfield    #2; //Field m:I
    4:   iconst_1
    5:   iadd
    6:   ireturn
   LineNumberTable:
    line 7: 0
                                                                                           
                                                                                           
}

两者是一致的 



step4) 访问标志

    接下来的两个字节0x 00 21是由下标中的0x 00 01 | 0x 0020计算出来的,表类是public 的,且是jdk1.2以后的编译器编译出来的



访问标志  
标志名称标志值含义
ACC_PUBLIC0x0001是否为public类型
ACC_FINAL0x0010是否被声明为final,只有类可以设置,接口不能设置该标志
ACC_SUPER0x0020

是否允许使用invokespecial字节码指令(查了一下该命令的作用为"调用超类的构造

方法,实例的构造方法,私有方法"),JDK1.2以后的编译器编译出来的class文件

该标志都为真

ACC_INTERFACE0x0200标识这是一个接口 
ACC_ABSTRACT0x0400

 是否被声明为abstract类型,对于接口和抽象类来说此标志

为真,其他类为假

ACC_SYNTHETIC0x1000 标识这个类并非由用户代码生成
ACC_ANNOTATION0x2000 标识这是一个注解
ACC_ENUM0x4000 标识这是一个枚举


step5)  类信息


  • 头两个字节0x 00 03,指向常量池中的第3项,即类名

  • 接下来的两个字节 0x 00 04 指向常量池中的第4项,即父类名

  • 接下来的两个字节 0x 00 00 表示类实现的接口的个数,本例子中为0,如果有的n个话,后面还会有2n个字节的常量池索引

step 6)字段信息

  •  头两个字节 0x 00 01 字段的个数,本例中为1

  •   接下来就是1个field_info结构

    • 头两个字节 0x 00 02 表示accss_flags,即private

    • 接下来0x 00 05表示字段名,指向常量池的第5项,即本例中"m"

    • 接下来的0x 00 06 表示字段的描述信息,指向常量池的第6项,即本例中"I" ,表示是int类型

    • 接下来的0x 00 00 表示字段的属性信息(用于扩展或者补充说明字段信息)的个数,本例中为0,如果有n个的话,后面还会有n个attribute_info结构

step 7) 方法信息

  • 头两个字节为0x 00 02,表示方法的个数,本例中为2

  • 接下来是两个method_info结构

    • 第1个method_info结构

      • 头两个字节0x 00 01 表示accss_flags,即public 方法

      • 接下来0x 00 07表示方法名,指向常量池的第7项,即本例中"<init>"

      • 接下来的0x 00 08 表示方法的描述信息,指向常量池的第8项,即本例中"()V" ,表示是一个没有参数,返回类型为void的方法

      • 接下来的0x 00 01表示方法的属性信息的个数,本例中为1

      • 接下是1个attribute_info结构

        • 0x 00 09表示属性名,指向常量池的第9项,即Code,表示方法的代码

        • 0x 00 00 00 1d 表示该属性的长度,即29个字节

        • 0x 00 01 表示max_stack 

        • 0x 00 01 表示max_locals

        • 0x 00 00 00 05 表示code_length, 即该方法的代码为编译后为5个字节

        • 0x 2a b7  00 01 b1 即代码

        • 0x 00 00 表示 没有异常信息

        • 0x 00 01 表示有一个属性信息

          • 接下是1个attribute_info结构

            • 0x 00 0a 表示属性名,指向常量池的第10项,即LineNumberTable,表示方法的行号

            • 0x 00 00 00 06 表示该属性的长度,即6个字节

            • 0x 00 01 表示有1行

            • 0x 00  00 表示字节码为第0行

            • 0x 00 03 表示上面的字节码为第0行对应源码中的第3行

    • 第2个method_info结构

      • 头两个字节00 01 表示accss_flags,即public 方法

      • 接下来0x 00 0b表示方法名,指向常量池的第11项,即本例中"inc"

      • 接下来的0x 00 0c 表示方法的描述信息,指向常量池的第12项,即本例中"()I" ,表示是一个没有参数,返回类型为int的方法

      • 接下来的0x 00 01表示方法的属性信息的个数,本例中为1

      • 接下是1个attribute_info结构

        • 0x 00 09表示属性名,指向常量池的第9项,即Code,表示方法的代码

        • 0x 00 00 00 1f 表示该属性的长度,即31个字节

        • 0x 00 02 表示max_stack

        • 0x 00 01 表示max_locals

        • 0x 00 00 00 07 表示code_length, 即该方法的代码为编译后为7个字节

        • 0x 2a b4 00 02 04 60 ac 即代码

        • 0x 00 00 表示 没有异常信息

        • 0x 00 01 表示有一个属性信息

          • 接下是1个attribute_info结构

            • 0x 00 0a 表示属性名,指向常量池的第10项,即LineNumberTable,表示方法的行号

            • 0x 00 00 00 06 表示该属性的长度,即6个字节

            • 0x 00 01 表示有1行

            • 0x 00  00 表示字节码为第0行

            • 0x 00 07 表示上面的字节码为第0行对应源码中的第7行

step 8) 属性信息

  • 头两个字节为0x 00 01,表示有一个属性

  • 接下是1个attribute_info结构

    • 0x 00 0d表示属性名,指向常量池的第13项,即SourceFile,表示类的源文件

    • 0x 00 00 00 02,属性长度,即接下来的字节个数

    • 0x 00 0e 表示源文件名,指向常量池的第14项,即"TestClass.java"



小结   


  • 如果想分析类文件,大部分情况下按字节分析,直接使用javap即可,但如果知道字节码也可以更好的了解javap的结果 

  • 几个种要的结构需要了解,本文就不一一列举具体信息(这个网上到处都是)

    • method_info

      • 会包含attribute_info

    • filed_info

      • 会包含attribute_info

    • attribute_info

      • 共有好几种attribute,本例中就用到了Code,LineNumberTable,SourceFile,每种不同的属性结构都不一样

      • 特别Code类型的attribute_info,编译过后的字令码就存放在里面

      • 另外还有LineNumberTable以及本例中未提到的LocalVariableTable都是和调试息息相关的

      • 有可能会嵌套attribute_info

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值