`
xitong
  • 浏览: 6197411 次
文章分类
社区版块
存档分类
最新评论

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

step1)javamagicnumber

    • 首先的四个字节0xcafebabe即class文件的magicnumber


step2)javaversion

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


step3)常量池

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

    • 第1项

      • tag:0x0a,CONSTANT_Methodref_info即方法声明

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

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

    • 第2项

      • tag0x09,CONSTANT_Fieldref_info即字段声明

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

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

    • 第3项

      • tag:0x07,CONSTANT_Class_info即类声明

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

    • 第4项

      • tag:0x07,CONSTANT_Class_info即类声明

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

    • 第5项

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

      • length:0x0001,长度为1个字节

      • bytes:0x6d,内容为"m"

    • 第6项

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

      • length:0x0001,长度为1个字节

      • bytes:0x49,内容为"I"

    • 第7项

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

      • length:0x0006,长度为6个字节

      • bytes:0x3c696e69743e,内容为"<init>"

    • 第8项

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

      • length:0x0003,长度为3个字节

      • bytes:0x282956内容为"()V"

    • 第9项

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

      • length:0x0004,长度为4个字节

      • bytes:0x436f6465内容为"Code"

    • 第10项

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

      • length:0x000f,长度为15个字节

      • bytes:0x4c696e654e756d6265725461626c65内容为"LineNumberTable"

    • 第11项

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

      • length:0x0003,长度为3个字节

      • bytes:0x696e63内容为"inc"

    • 第12项

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

      • length:0x0003,长度为3个字节

      • bytes:0x282949内容为"()I"

    • 第13项

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

      • length:0x000a,长度为10个字节

      • bytes:0x536f7572636546696c65内容为"SourceFile"

    • 第14项

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

      • length:0x0004,长度为14个字节

      • bytes:0x54657374436c6173732e6a617661内容为"TestClass.java"

    • 第15项

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

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

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

    • 第16项

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

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

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

    • 第17项

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

      • length:0x0018,长度为24个字节

      • bytes:0x6f72672f6b616b612f636c617a7a2f54657374436c617373内容为"org/kaka/clazz/TestClass"

    • 第18项

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

      • length:0x0010,长度为16个字节

      • bytes:0x6a6176612f6c616e672f4f626a656374内容为"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)访问标志

接下来的两个字节0x0021是由下标中的0x0001|0x0020计算出来的,表类是public的,且是jdk1.2以后的编译器编译出来的



访问标志
标志名称 标志值 含义
ACC_PUBLIC 0x0001 是否为public类型
ACC_FINAL 0x0010 是否被声明为final,只有类可以设置,接口不能设置该标志
ACC_SUPER 0x0020

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

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

该标志都为真

ACC_INTERFACE 0x0200 标识这是一个接口
ACC_ABSTRACT 0x0400

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

为真,其他类为假

ACC_SYNTHETIC 0x1000 标识这个类并非由用户代码生成
ACC_ANNOTATION 0x2000 标识这是一个注解
ACC_ENUM 0x4000 标识这是一个枚举


step5)类信息


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

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

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

step6)字段信息

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

  • 接下来就是1个field_info结构

    • 头两个字节0x0002表示accss_flags,即private

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

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

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

step7)方法信息

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

  • 接下来是两个method_info结构

    • 第1个method_info结构

      • 头两个字节0x0001表示accss_flags,即public方法

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

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

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

      • 接下是1个attribute_info结构

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

        • 0x0000001d表示该属性的长度,即29个字节

        • 0x0001表示max_stack

        • 0x0001表示max_locals

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

        • 0x2ab70001b1即代码

        • 0x0000表示没有异常信息

        • 0x0001表示有一个属性信息

          • 接下是1个attribute_info结构

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

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

            • 0x0001表示有1行

            • 0x0000表示字节码为第0行

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

    • 第2个method_info结构

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

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

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

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

      • 接下是1个attribute_info结构

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

        • 0x0000001f表示该属性的长度,即31个字节

        • 0x0002表示max_stack

        • 0x0001表示max_locals

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

        • 0x2ab400020460ac即代码

        • 0x0000表示没有异常信息

        • 0x0001表示有一个属性信息

          • 接下是1个attribute_info结构

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

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

            • 0x0001表示有1行

            • 0x0000表示字节码为第0行

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

step8)属性信息

  • 头两个字节为0x0001,表示有一个属性

  • 接下是1个attribute_info结构

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

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

    • 0x000e表示源文件名,指向常量池的第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

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics