本篇文章将介绍 .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文件.放到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个常量
Constant1 0a(10) MethodRef_info
000e(14) Class_info索引项#14
0020(32) NameAndType索引项#32
Constant2 0a(10) MethodRef_info
000e(14) Class_info索引项#14
0021(33) NameAndType索引项#33
Constant3 07(7) Class_info
0023(35) 全局限定名常量索引为#35
Constant4 03(3) 高位在前的Integer
7f09001c
Constant5 0a(10) MethodRef_info
000d(13) Class_info索引项#13
0026(38) NameAndType索引项#38
Constant6 07(7) Class_info
0027(39) 全局限定名常量索引为#39
Constant7 03(3) 高位在前的Integer
7f07008d
Constant8 0a(10) MethodRef_info
000d(13) Class_info索引项#13
0029(41) NameAndType索引项#41
Constant9 07(7) Class_info
002a(42) 全局限定名常量索引为#42
Constant10 09(9) FieldRef_info
000d(13) Class_info索引项#13
002b(43) NameAndType索引项#43
Constant11 08(8) 字符串索引
002c(44) 索引项#44
Constant12 0a(10) MethodRef_info
0009(9) Class_info索引项#9
002d(45) NameAndType索引项#45
Constant13 07(7) Class_info
002e(42) 全局限定名常量索引为#46
Constant14 07(7) Class_info
002f(47) 全局限定名常量索引为#47
Constant15 01(1) utf-8 info
0008(8) 长度为8
7465787456696577 textView
Constant16 01(1) utf-8 info
0019(25) 长度为25
4c616e64726f69642f7769646765742f54657874566965773b Landroid/widget/TextView;
Constant17 01(1) utf-8 info
0006(6) 长度为6
3c696e69743e
Constant18 01(1) utf-8 info
0003(3) 长度为3
282956 V()
Constant19 01(1) utf-8 info
0004(4)
436f6465 Code
Constant20 01(1) utf-8 info
000f=15
4c696e654e756d6265725461626c65 LineNumberTable
Constant21 01(1) utf-8 info
0012=18
4c6f63616c5661726961626c655461626c65 LocalVariableTable
Constant22 01(1) utf-8 info
0004=4
74686973 this
Constant23 01(1) utf-8 info
0030=48
4c63......793b Lcom/example/liuxiaojie/smalietest/MainActivity;
Constant24 01(1) utf-8 info
0008=8
6f6e437265617465 onCreate
Constant25 01(1) utf-8 info
0016=22
...... (Landroid/os/Bundle;)V
Constant26 01(1) utf-8 info
0012=18
...... saveInstanceState
Constant27 01(1) utf-8 info
0013=19
...... Landroid/os/Bundle;
(接下来是一堆的string,我只写最终结果了)
Constant28 RuntimeInvisibleParameterAnnotations
Constant29 Landroid/support/annotation/Nullable;
Constant30 SourceFile
Constant31 MainActivity.java
Constant32 0c(12)
对应索引#17,#18 V()
Constant33 0c(12) NameAndType索引项#12
对应索引#24,#25 Lcom/example/liuxiaojie/smalietest/MainActivity; onCreate
Constant34 07(7) Class_info
0030(48) 全局限定名常量索引为#48
Constant35 com/example/liuxiaojie/smalietest/R$layout
Constant36 layout
Constant37 InnerClasses
Constant38 0c(12)
对应索引#49,#50
Constant39 com/example/liuxiaojie/smalietest/R$id
Constant40 id
Constant41 0c(12)
对应索引#51,#52
Constant42 android/widget/TextView
Constant43 0c(12)
对应索引#15,#16 textView Landroid/widget/TextView;
Constant44 smali
Constant45 0c(12)
对应索引#53,#54
Constant46 com/example/liuxiaojie/smalietest/MainActivity
Constant47 android/support/v7/app/AppCompatActivity
Constant48 com/example/liuxiaojie/smalietest/R
Constant49 setContentView
Constant50 (I)V
Constant51 findViewById
Constant52 (I)Landroid/view/View;
Constant53 setText
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,查到是textView 0010=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)0001 0000 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文件,解析完了就可以看到两者的区别