class文件字节码解析

本篇文章将介绍 .class 文件的结构,通过一个简单的例子认识 .class 文件。
首先写一个java文件(本人选择在Android平台,主要是接下来一篇会讲到dex文件,方便做对比)

package com.example.liuxiaojie.smalietest; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.widget.TextView; public class MainActivity extends AppCompatActivity {private TextView textView; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = findViewById(R.id.tv); textView.setText("smali"); } }

【class文件字节码解析】然后运行,可以在对应的文件夹下得到class文件.放到Android Studio里面是这样的
package com.example.liuxiaojie.smalietest; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private TextView textView; public MainActivity() { }protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(2131296284); this.textView = (TextView)this.findViewById(2131165325); this.textView.setText("smali"); } }

其实这不能算class文件本来的样子.因为用Notepad++或者Sublime打开,内容大致如下
cafe babe 0000 0033 0037 0a00 0e00 200a 000e 0021 0700 2303 7f09 001c 0a00 0d00 2607 0027 037f 0700 8d0a 000d 0029 0700 2a09 000d 002b 0800 2c0a 0009 002d 0700 2e07 002f 0100 0874 6578 7456 6965 7701 0019 4c61 6e64 726f 6964 2f77 6964 6765 742f 5465 7874 5669 6577 3b01 0006 3c69 6e69 743e 0100 0328 2956 0100 0443 6f64 6501 000f 4c69 6e65 4e75 6d62 6572 5461 626c 6501 0012 4c6f 6361 6c56 6172 6961 626c 6554 6162 6c65 0100 0474 6869 7301 0030 4c63 6f6d 2f65 7861 6d70 6c65 2f6c 6975 7869 616f 6a69 652f 736d 616c 6965 7465 7374 2f4d 6169 6e41 6374 6976 6974 793b 0100 086f 6e43 7265 6174 6501 0016 284c 616e 6472 6f69 642f 6f73 2f42 756e 646c 653b 2956 0100 1273 6176 6564 496e 7374 616e 6365 5374 6174 6501 0013 4c61 6e64 726f 6964 2f6f 732f 4275 6e64 6c65 3b01 0024 5275 6e74 696d 6549 6e76 6973 6962 6c65 5061 7261 6d65 7465 7241 6e6e 6f74 6174 696f 6e73 0100 254c 616e 6472 6f69 642f 7375 7070 6f72 742f 616e 6e6f 7461 7469 6f6e 2f4e 756c 6c61 626c 653b 0100 0a53 6f75 7263 6546 696c 6501 0011 4d61 696e 4163 7469 7669 7479 2e6a 6176 610c 0011 0012 0c00 1800 1907 0030 0100 2a63 6f6d 2f65 7861 6d70 6c65 2f6c 6975 7869 616f 6a69 652f 736d 616c 6965 7465 7374 2f52 246c 6179 6f75 7401 0006 6c61 796f 7574 0100 0c49 6e6e 6572 436c 6173 7365 730c 0031 0032 0100 2663 6f6d 2f65 7861 6d70 6c65 2f6c 6975 7869 616f 6a69 652f 736d 616c 6965 7465 7374 2f52 2469 6401 0002 6964 0c00 3300 3401 0017 616e 6472 6f69 642f 7769 6467 6574 2f54 6578 7456 6965 770c 000f 0010 0100 0573 6d61 6c69 0c00 3500 3601 002e 636f 6d2f 6578 616d 706c 652f 6c69 7578 6961 6f6a 6965 2f73 6d61 6c69 6574 6573 742f 4d61 696e 4163 7469 7669 7479 0100 2861 6e64 726f 6964 2f73 7570 706f 7274 2f76 372f 6170 702f 4170 7043 6f6d 7061 7441 6374 6976 6974 7901 0023 636f 6d2f 6578 616d 706c 652f 6c69 7578 6961 6f6a 6965 2f73 6d61 6c69 6574 6573 742f 5201 000e 7365 7443 6f6e 7465 6e74 5669 6577 0100 0428 4929 5601 000c 6669 6e64 5669 6577 4279 4964 0100 1628 4929 4c61 6e64 726f 6964 2f76 6965 772f 5669 6577 3b01 0007 7365 7454 6578 7401 001b 284c 6a61 7661 2f6c 616e 672f 4368 6172 5365 7175 656e 6365 3b29 5600 2100 0d00 0e00 0000 0100 0200 0f00 1000 0000 0200 0100 1100 1200 0100 1300 0000 2f00 0100 0100 0000 052a b700 01b1 0000 0002 0014 0000 0006 0001 0000 0008 0015 0000 000c 0001 0000 0005 0016 0017 0000 0004 0018 0019 0002 0013 0000 0066 0003 0002 0000 0022 2a2b b700 022a 1204 b600 052a 2a12 07b6 0008 c000 09b5 000a 2ab4 000a 120b b600 0cb1 0000 0002 0014 0000 0016 0005 0000 000e 0005 000f 000b 0011 0018 0012 0021 0013 0015 0000 0016 0002 0000 0022 0016 0017 0000 0000 0022 001a 001b 0001 001c 0000 0007 0100 0100 1d00 0000 0200 1e00 0000 0200 1f00 2500 0000 1200 0200 0300 2200 2400 1900 0600 2200 2800 19

这里我全部贴出来了.可以看到都是由16进制数据组成.接下来,我们就可以根据一文让你明白Java字节码来解析整个文件
(整个过程其实挺累的,花了将近2个小时.难倒是不难,主要是一个一个要对照好挺困难的)
魔数(4)固定为 ca fe ba be 版本号(4)00 00 00 33,前面的0000是次版本号,后面的0033是主版本号.0x33=51(十进制)=jdk1.7。可用java --version验证 常量池(2+n)00 37,去除掉第0项,还有0x36=54个常量 Constant10a(10)MethodRef_info 000e(14)Class_info索引项#14 0020(32)NameAndType索引项#32 Constant20a(10)MethodRef_info 000e(14)Class_info索引项#14 0021(33)NameAndType索引项#33 Constant307(7)Class_info 0023(35)全局限定名常量索引为#35 Constant403(3)高位在前的Integer 7f09001c Constant50a(10)MethodRef_info 000d(13)Class_info索引项#13 0026(38)NameAndType索引项#38 Constant607(7)Class_info 0027(39)全局限定名常量索引为#39 Constant703(3)高位在前的Integer 7f07008d Constant80a(10)MethodRef_info 000d(13)Class_info索引项#13 0029(41)NameAndType索引项#41 Constant907(7)Class_info 002a(42)全局限定名常量索引为#42 Constant1009(9)FieldRef_info 000d(13)Class_info索引项#13 002b(43)NameAndType索引项#43 Constant1108(8)字符串索引 002c(44)索引项#44 Constant120a(10)MethodRef_info 0009(9)Class_info索引项#9 002d(45)NameAndType索引项#45 Constant1307(7)Class_info 002e(42)全局限定名常量索引为#46 Constant1407(7)Class_info 002f(47)全局限定名常量索引为#47 Constant1501(1)utf-8 info 0008(8)长度为8 7465787456696577textView Constant1601(1)utf-8 info 0019(25)长度为25 4c616e64726f69642f7769646765742f54657874566965773bLandroid/widget/TextView; Constant1701(1)utf-8 info 0006(6)长度为6 3c696e69743e Constant1801(1)utf-8 info 0003(3)长度为3 282956V() Constant1901(1)utf-8 info 0004(4) 436f6465Code Constant2001(1)utf-8 info 000f=15 4c696e654e756d6265725461626c65LineNumberTable Constant2101(1)utf-8 info 0012=18 4c6f63616c5661726961626c655461626c65LocalVariableTable Constant2201(1)utf-8 info 0004=4 74686973this Constant2301(1)utf-8 info 0030=48 4c63......793bLcom/example/liuxiaojie/smalietest/MainActivity; Constant2401(1)utf-8 info 0008=8 6f6e437265617465onCreate Constant2501(1)utf-8 info 0016=22 ......(Landroid/os/Bundle; )V Constant2601(1)utf-8 info 0012=18 ......saveInstanceState Constant2701(1)utf-8 info 0013=19 ......Landroid/os/Bundle; (接下来是一堆的string,我只写最终结果了) Constant28RuntimeInvisibleParameterAnnotations Constant29Landroid/support/annotation/Nullable; Constant30SourceFile Constant31MainActivity.javaConstant320c(12) 对应索引#17,#18V() Constant330c(12)NameAndType索引项#12 对应索引#24,#25Lcom/example/liuxiaojie/smalietest/MainActivity; onCreate Constant3407(7)Class_info 0030(48)全局限定名常量索引为#48 Constant35com/example/liuxiaojie/smalietest/R$layout Constant36layout Constant37InnerClasses Constant380c(12) 对应索引#49,#50 Constant39com/example/liuxiaojie/smalietest/R$id Constant40id Constant410c(12) 对应索引#51,#52 Constant42android/widget/TextView Constant430c(12) 对应索引#15,#16textViewLandroid/widget/TextView; Constant44smali Constant450c(12) 对应索引#53,#54 Constant46com/example/liuxiaojie/smalietest/MainActivity Constant47android/support/v7/app/AppCompatActivity Constant48com/example/liuxiaojie/smalietest/R Constant49setContentView Constant50(I)V Constant51findViewById Constant52(I)Landroid/view/View; Constant53setText Constant54(Ljava/lang/CharSequence; )V通过常量池的解析,基本有了java文件内容的轮廓Access_Flag 访问标志0021(0020和0001的并集) ------------------------------------------------------------- 类索引(用于确定类的全限定名)000d=13然后找到46(com/example/liuxiaojie/smalietest/MainActivity) 父类索引 000e=14找到47(android/support/v7/app/AppCompatActivity) 接口索引(这个java文件没有接口,应该是0000) 字段表集合(用于描述类和接口中声明的变量。这里的字段包含了类级别变量以及实例变量,但是不包括方法内部声明的局部变量。) 0001表示一共有1个变量。0002表示private。一个是000f=15,查到是textView0010=16(Landroid/widget/TextView; ) 0000表示没有属性表 方法(理论上我们只有一个onCreate。为啥会是2呢?因为init)0002表示两个方法 第一个方法0001(ACC_PUBLIC)0011()0012(V())0001(一个属性表) 这个属性表索引是0013(Code)length=0000002f(47) max_stack=0001 max_locals=0001 codelength=00000005 code=2ab70001b1具体可参考文档 exception_table_length=0000 attribute_count=0002 0014(LineNumberTable)00000006(长度为6)0001 0000 0008 0015(LocalVariableTable)0000000c(12)00010000 0005 0016 0017 0000 第二个方法0004(protected)0018(onCreate)0019((Landroid/os/Bundle; )V)0002(两个属性表) 第一个属性表索引是0013(Code)length=0000 0066(102) max_stack=0003 max_locals=0002 codelength=0000 0022(34) code=2a2b...... attribute_count=0002(2) 0014(LineNumberTable)0000 0016(长度为22)0005 (一共5个table_info,每个占两个u2类型) 0015(LocalVariableTable)0000 0016(22) 0002 (2个variable_info)

解析到这里其实已经差不多了,至少已经对class文件的字节码有所了解了.可以看到,整个class文件的字节码都是一段一段对应好的了,每一块数据开始都是数据量,然后跟着数据,相对于dex文件而言,不用去查找偏移地址.下一篇会解析一下dex文件,解析完了就可以看到两者的区别

    推荐阅读