分析Java .class 文件的字节码

源码:

package jvm;

public class One {
    public static void main(String[] args) {
        int ljj1 = 1;
        int ljj2 = 2;
        int ljjRst = ljj1 + ljj2;
    }
}

字节码:

CA FE BA BE 00 00 00 3F 00 0F 0A 00 02 00 03 07 00 04 0C 00 05 00 06 01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 01 00 06 3C 69 6E 69 74 3E 01 00 03 28 29 56 07 00 08 01 00 07 6A 76 6D 2F 4F 6E 65 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 04 6D 61 69 6E 01 00 16 28 5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29 56 01 00 0A 53 6F 75 72 63 65 46 69 6C 65 01 00 08 4F 6E 65 2E 6A 61 76 61 00 21 00 07 00 02 00 00 00 00 00 02 00 01 00 05 00 06 00 01 00 09 00 00 00 1D 00 01 00 01 00 00 00 05 2A B7 00 01 B1 00 00 00 01 00 0A 00 00 00 06 00 01 00 00 00 03 00 09 00 0B 00 0C 00 01 00 09 00 00 00 2D 00 02 00 04 00 00 00 09 04 3C 05 3D 1B 1C 60 3E B1 00 00 00 01 00 0A 00 00 00 12 00 04 00 00 00 05 00 02 00 06 00 04 00 07 00 08 00 08 00 01 00 0D 00 00 00 02 00 0E

人肉分解.class文件

注:下面提到的查表,表示查找oracle官网相关的表格 。并且每个链接都是带有网页内锚点的,也就是会直接跳到指定的表格处,如果你直接点击没有跳转到指定锚点,那么请复制链接查看锚点位置。

ClassFile Structure 分解

查表可得到如下的分解结果

CA FE BA BE//magic
00 00 00 3F//小大版本号
00 0F//constant pool length 15
0A 00 02 00 03//region constant pool ,池中数量为constant pool length-1,就是15-1=14个
07 00 04
0C 00 05 00 06
01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74
01 00 06 3C 69 6E 69 74 3E
01 00 03 28 29 56
07 00 08
01 00 07 6A 76 6D 2F 4F 6E 65
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 04 6D 61 69 6E
01 00 16 28 5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29 56
01 00 0A 53 6F 75 72 63 65 46 69 6C 65
01 00 08 4F 6E 65 2E 6A 61 76 61//endregion constant pool
00 21//access_flags
00 07//this class index
00 02//superclass index
00 00//interface count
00 00//fields count 为0 ,那么field_info就不存在了
00 02//methods count,那么后面有两个method_info    
//第一个方法↓
00 01 00 05 00 06 00 01 00 09 00 00 00 1D 00 01 00 01 00 00 00 05 2A B7 00 01 B1 00 00 00 01 00 0A 00 00 00 06 00 01 00 00 00 03
//第二个方法↓
00 09 00 0B 00 0C 00 01 00 09 00 00 00 2D 00 02 00 04 00 00 00 09 04 3C 05 3D 1B 1C 60 3E B1 00 00 00 01 00 0A 00 00 00 12 00 04 00 00 00 05 00 02 00 06 00 04 00 07 00 08 00 08
//类级别属性
00 01 00 0D 00 00 00 02 00 0E//

常量池解析

上述分解结果中,第3行是常量池长度15。4-17行是常量池,其中共有14个常量,常量池的实际数量是(常量池长度-1)也就是15-1=14个常量
查询常量池表可得,14个常量的第一个字节
0A 07 0C 01分别表示 表
CONSTANT_Methodref_info ,
CONSTANT_Class ,
CONSTANT_NameAndType ,
CONSTANT_Utf8
我们先分析下几个以01字节开头的CONSTANT_Utf8,其余几个常量类型0A 07 0c 无非是对 CONSTANT_Utf8的引用
为了可读性,这里从上一个分析结果中,提取了常量池的内容,如下:

0A 00 02 00 03
07 00 04
0C 00 05 00 06
01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74
01 00 06 3C 69 6E 69 74 3E
01 00 03 28 29 56
07 00 08
01 00 07 6A 76 6D 2F 4F 6E 65
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 04 6D 61 69 6E
01 00 16 28 5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29 56
01 00 0A 53 6F 75 72 63 65 46 69 6C 65
01 00 08 4F 6E 65 2E 6A 61 76 61

优先解析CONSTANT_Utf8表,解析结果用注释追加到字节码后面,如下:

01
00 10
6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74//java/lang/Object
01 00 06 
3C 69 6E 69 74 3E//<init>
01 00 03 
28 29 56//()V
01 00 07 
6A 76 6D 2F 4F 6E 65//jvm/One
01 00 04 
43 6F 64 65//Code
01 00 0F 
4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65//LineNumberTable
01 00 04 
6D 61 69 6E//main
01 00 16 
28 5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29 56//([Ljava/lang/String;)V
01 00 0A 
53 6F 75 72 63 65 46 69 6C 65//SourceFile
01 00 08 
4F 6E 65 2E 6A 61 76 61//One.java

接着解析剩余的几个常量类型0A 07 0c , 下面的片段是常量池完整的解析结果。为了方便阅读,在每行开始,人为加上行号(例如#1),并且以01开头的行,其解析结果用注释追加,而剩余类型的行则直接追加,不使用注释,因为这样可以高亮索引号,同样是为了方便阅读。

//0A 查表可得这个常量是CONSTANT_Methodref_info ,并由此可得后面两个字节的含义。每行以此类推。
#1 0A 00 02 00 03 CONSTANT_Methodref_info class_index 2, name_and_type_index 3 
#2 07 00 04 CONSTANT_Class_info name_index 4 
#3 0C 00 05 00 06 CONSTANT_NameAndType_info name_index=5 ,descriptor_index= 6 
#4 01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74 //java/lang/Object
#5 01 00 06 3C 69 6E 69 74 3E //<init>
#6 01 00 03 28 29 56 //()V
#7 07 00 08 CONSTANT_Class_info name_index 8 
#8 01 00 07 6A 76 6D 2F 4F 6E 65//jvm/One
#9 01 00 04 43 6F 64 65//Code
#10 01 00 0F 4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65//LineNumberTable
#11 01 00 04 6D 61 69 6E//main
#12 01 00 16 28 5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29 56//([Ljava/lang/String;)V
#13 01 00 0A 53 6F 75 72 63 65 46 69 6C 65//SourceFile
#14 01 00 08 4F 6E 65 2E 6A 61 76 61//One.java

分解函数表

第一个分解结果中,第23行的两个字节0002 表示后面有两个函数,我们依次解析。剧透下:第一个为构造函数,第二个为main函数

构造函数

这里需要查询两个表,一个是函数表,一个是属性表(Code属性),这个属性表(Code属性)是含在该函数表结构体内的,所以它是函数级别的属性表。分解结果如下:

//method_info { u2 access_flags; u2 name_index; u2 descriptor_index; u2 attributes_count; attribute_info attributes[attributes_count]; }
00 01//access_flags
00 05 //name_index
00 06 //descriptor
00 01//attributes_count
00 09 00 00 00 1D 00 01 00 01 00 00 00 05 2A B7 00 01 B1 00 00 00 01 00 0A 00 00 00 06 00 01 00 00 00 03 //查询Code属性表可得该行
Code属性分解

查询Code属性表,分解得到

00 09 // u2 attribute_name_index
00 00 00 1D     //u4 attribute_length;
00 01 //    u2 max_stack;
00 01 //    u2 max_locals;
00 00 00 05//    u4 code_length;
2A B7 00 01 B1 
00 00 //  u2 exception_table_length;
00 01 //    u2 attributes_count;
00 0A 00 00 00 06 00 01 00 00 00 03//查询常量池可得这是一个 LineNumberTable属性表
Code属性的Code字段解析

也就是上述Code属性分解中的第6行
这是Java 字节码的指令,需要查询的表是指令集表,与前述提到的表不同。
可以从Wiki List of Java bytecode instructions或者ORECLA The Java Virtual Machine Instruction Set进行查询。下面直接给出分析结果

2A //aload_0
B7 00 01 //invokespecial #1       
B1 //return
LineNumberTable属性分解

这个属性是含在Code属性表内部的,姑且归类为内嵌属性表,至此我们知道了,属性表是可以嵌套的。
查表 LineNumberTable属性表 可得

00 0A //LineNumberTable
00 00 00 06 //attribute_length
00 01 //line_number_table_length
00 00 00 03//u2 start_pc:u2 line_number;

到这里,构造函数分解完毕。
上述对构造函数的分解过程已经是完整的过程,main函数的分析过程不再赘述,大家可以动手分析下,加深印象,这里直接给出分析结果。

main函数

00 09 //access_flags
00 0B //name_index
00 0C //descriptor
00 01 //attributes_count
00 09 //Code 属性
00 00 00 2D //attribute_length
00 02 //max_stack
00 04 //max_locals
00 00 00 09 //code_length
04 3C 05 3D 1B 1C 60 3E B1 
00 00 //exception_table_length
00 01 //attributes_count
00 0A //attribute_name_index
00 00 00 12 //attribute_length
00 04 //line_number_table_length
00 00 00 05//start_pc,line_number
00 02 00 06 start_pc,line_number
00 04 00 07 //start_pc,line_number
00 08 00 08//start_pc,line_number
类属性

有了上面的经验,类级别属性的分析过程也是一样的。下面直接给出结果

00 01 //属性长度
00 0D//查询常量池分析结果可知,这是SourceFile属性
00 00 00 02 00 0E//查询SourceFile属性表可知
SourceFile属性

查询SourceFile属性表,可得:

00 0D//属性名:SourceFile
00 00 00 02 //属性长度2
00 0E//查询常量池可知:One.java

一个源码的各个项,在.class文件中的表示

看着源码的时候,能够在脑中,呈现出其.class文件形式的大致结构。

工具分析

我们已经进行了一次人肉分析,以后只需要借助工具javap,快速分解一个.class文件。有了前面的人肉分析,现在我们再去阅读javap的输出内容,就游刃有余了。

表格的树状关系

各个表格的一个大致的树状关系。
表格大致关系图
完整的xmind文件,在页头进行下载

  • 12
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值