Android_dex详解
来源:互联网 发布:java培训多少 编辑:程序博客网 时间:2024/06/18 07:11
DEX详解:
Dex文件分为dex和odex。odex是优化版的dex。先介绍dex:
DEX使用的数据结构为
U1:表示一位无符号位。
U2:表示两位无符号位。
U4:表示四位无符号位。
U8:表示八位无符号位。
sleb128:有符号,长度1~5字节。
uleb128:无符号,长度1~5符号。
uleb128p1:无符号uleb128值加一,长度1~5。
其中u1~u4没问题,但是后三个需要解释一下。
上图是两个字节的leb128,在实际使用的时候系统会去读取第一个字节的最高位,如果是1,表示这个长度不够需要在读取第二个字节,如果还是一则读取第三个,直到有一个的最高位是0则表示读取结束。但是最多读取五个字节,如果这五个都是一则表示apk出错,会停止安装。
下面实际解析两个,比如字符序列为C0 83 92 25为例子:
C0: 1 1000000 83: 1 0000011 92: 1 0010010 25: 0 0100101
可以看到前三个的最高位都为1,读到25最高位为0则停止读取。
在真正读取的时候把最高位去掉,倒叙连起来就是0100101 0010010 0000011 1000000就是4A481C0。所以起uleb128就是0x4A481C0,uleb128p1就是uleb128+1=0x4A481C1。Sleb128把其左移四位,最高位就是符号位,这里是0,就是正的。
再比如字符序列 d1 c2b3 40。计算leb为11010001 11000010 10110011 01000000
整理就是1000000 0110011 1000010 1010001=80CE151。左移四位最高位是1,为负数,就是0xF80CE151。
数据结构
字段名称
偏移值
长度
描述
U1
Magic[8]
0x0
8
'Magic'值,即魔数字段,格式如”dex/n035/0”,其中的035表示结构的版本。
U4
checksum
0x8
4
校验码。
U1
signature
0xC
20
SHA-1签名。
U4
file_size
0x20
4
Dex文件的总长度。
U4
header_size
0x24
4
文件头长度,009版本=0x5C,035版本=0x70。
U4
endian_tag
0x28
4
标识字节顺序的常量,根据这个常量可以判断文件是否交换了字节顺序,缺省情况下=0x78563412。
U4
link_size
0x2C
4
连接段的大小,如果为0就表示是静态连接。
U4
link_off
0x30
4
连接段的开始位置,从本文件头开始算起。如果连接段的大小为0,这里也是0。
U4
map_off
0x34
4
map数据基地址。
U4
string_ids_size
0x38
4
字符串列表的字符串个数。
U4
string_ids_off
0x3C
4
字符串列表表基地址。
U4
type_ids_size
0x40
4
类型列表里类型个数。
U4
type_ids_off
0x44
4
类型列表基地址。
U4
proto_ids_size
0x48
4
原型列表里原型个数。
U4
proto_ids_off
0x4C
4
原型列表基地址。
U4
field_ids_size
0x50
4
字段列表里字段个数。
U4
field_ids_off
0x54
4
字段列表基地址。
U4
method_ids_size
0x58
4
方法列表里方法个数。
U4
method_ids_off
0x5C
4
方法列表基地址。
U4
class_defs_size
0x60
4
类定义类表中类的个数。
U4
class_defs_off
0x64
4
类定义列表基地址。
U4
data_size
0x68
4
数据段的大小,必须以4字节对齐。
U4
data_off
0x6C
4
数据段基地址
上图和上表就是dex的文件头的结构和各个位置的意思。其中最开始的64 65 78 0A 30 33 3500(dex.035.)表示这是按照dex解析的。
string_ids_size和string_ids_off
这两个字段表示dex中用到的所有的字符串内容的大小和偏移值,我们需要解析完这部分,然后用一个字符串池存起来,后面有其他的数据结构会用索引值来访问字符串,这个池子也是非常重要的。
type_ids_size和type_ids_off
这两个字段表示dex中的类型数据结构的大小和偏移值,比如类类型,基本类型等信息
proto_ids_size和type_ids_off
这两个字段表示dex中的元数据信息数据结构的大小和偏移值,描述方法的元数据信息,比如方法的返回类型,参数类型等信息
field_ids_size和field_ids_off
这两个字段表示dex中的字段信息数据结构的大小和偏移值
method_ids_size和method_ids_off
这两个字段表示dex中的方法信息数据结构的大小和偏移值
class_defs_size和class_defs_off
这两个字段表示dex中的类信息数据结构的大小和偏移值,这个数据结构是整个dex中最复杂的数据结构,他内部层次很深,包含了很多其他的数据结构,所以解析起来也很麻烦。
data_size和data_off
这两个字段表示dex中数据区域的结构信息的大小和偏移值,这个结构中存放的是数据区域,比如我们定义的常量值等信息。
DEX虚拟机解析dex文件的内容,最终都将其映射成DexMapList数据结构。DexHeader结构的mapoff字段指明了DexMapList结构在dex文件中的偏移。
其中DexMapList的结构如下:
typedef structDexMapList {
u4 size; //表明接下来有多少个DexMapItem结构
DexMapItem list[1]; // DexMapItem结构
}DexMapList;
DexMapItem结构如下:
typedefstruct DexMapItem {
u2 type; //KDexType开头类型,枚举以下结构
u2 unused; //暂时未使用
u4 size; //制定类型个数
u4 offset; //指定类型数据的文件偏移
}DexMapItem;
DexMapItem结构定义如下:
enum{
kDexTypeHeaderItem = 0x0000, //头文件
kDexTypeStringIdItem = 0x0001,
kDexTypeTypeIdItem = 0x0002,
kDexTypeProtoIdItem = 0x0003,
kDexTypeFieldIdItem = 0x0004,
kDexTypeMethodIdItem =0x0005, //方法
kDexTypeClassDefItem = 0x0006, //类
kDexTypeMapList = 0x1000, //Map
kDexTypeTypeList = 0x1001,
kDexTypeAnnotationSetRefList = 0x1002,
kDexTypeAnnotationSetItem = 0x1003,
kDexTypeClassDataItem =0x2000,
kDexTypeCodeItem =0x2001,
kDexTypeStringDataItem = 0x2002, //字符
kDexTypeDebugInfoItem =0x2003,
kDexTypeAnnotationItem =0x2004,
kDexTypeEncodedArrayItem =0x2005,
kDexTypeAnnotationsDirectoryItem =0x2006,
};
以下新建一个安卓工程,自动生成helloworld为例:放在010中找到mapoff。
map_off
00 01 87 00
string_ids_size
00 00 15 66
string_ids_off
00 00 00 70
type_ids_size
00 00 02 F2
type_ids_off
00 00 56 08
proto_ids_size
00 00 03 F2
proto_ids_off
00 00 61 D0
field_ids_size
00 00 04 A9
field_ids_off
00 00 91 28
method_ids_size
00 00 12 D4
method_ids_off
00 00 B6 70
class_defs_size
00 00 01 C7
class_defs_off
00 01 4D 10
data_size
00 07 F4 44
data_off
00 01 87 00
可以看到mapoff=00018700。跳到00018700看到下图:
对照数据结构可以看到个数为0x11=17。则表示有17个DexMapItem。之后观察DexMapItem结构可以发现其占用三个字节。第一个表示其type,第二个表示个数,第三个表示偏移地址。所以可以得到下表:
Type
个数
偏移地址
kDexTypeHeaderItem
1
0x00000000
kDexTypeStringIdItem
5478
0x00000070
kDexTypeTypeIdItem
754
0x00005608
kDexTypeProtoIdItem
1010
0x000061D0
kDexTypeFieldIdItem
1193
0x00009128
kDexTypeMethodIdItem
4820
0x0000B670
kDexTypeClassDefItem
455
0x00014D10
kDexTypeMapList
1
0X00018700
kDexTypeTypeList
611
0X000187DC
kDexTypeAnnotationSetItem
486
0X00019EC8
kDexTypeClassDataItem
444
0X0001B2DC
kDexTypeCodeItem
3083
0X00022CDC
kDexTypeStringDataItem
5478
0X0005BD74
kDexTypeDebugInfoItem
3083
0X000786A4
kDexTypeAnnotationItem
559
0X00091824
kDexTypeEncodedArrayItem
65
0X000955D4
kDexTypeAnnotationsDirectoryItem
373
0X00095D44
两个表比较发现有些地址是相同的,这里为什么要重复的去保存相同的地址,是为了校验,如果比对发现不相同就停止安装。
下面开始逐步解析这个表,其结构虽然不复杂,但是调用有点乱。
kDexTypeHeaderItem就是之前的文件头,占0x70个字节。描述了整个DEX文件结构。
kDexTypeStringIdItem对应了string_ids_off和string_ids_size。在0x70的位置往下有5478个字符串地址如下图:
在对应的位置可以看到下图:
可以看到这一片往下保存的都是字符串。这里使用的解析方式是MUTF-8,其解析方式如下
最高位表示个数,之后按照最高位规定的个数读取,最后结尾为00。例如下:
我们随便找两个连续偏移为0005BD9F 0005BDC7
可以看到0005BD9F地址是0x26,往后读取38位到0005BDC6为00。下一个字符串的偏移为0005BDC7刚好在前一个字符串00后面。MUTF-8最开始的大小不包括最后的结束符。
kDexTypeTypeIdItem对应type_ids_size和type_ids_off。在0x00005608偏移的位置0x00005608,如图:
kDexTypeTypeIdItem结构指向的结构是DexTypeId,表示应用程序代码中使用到的具体类型。结构如下
struct DexTypeId{
u4 descriptorIdx; //指向DexStringId列表索引
};
这里需要注意:这里的不是地址,而是指向stringID的列表索引,说白了这里的0000 01 98,不是地址00000198,而是指kDexTypeStringIdItem的第0x198的那个字符串。因为在编译的时候已经将字符串统一存在了一个池子中,需要的时候按照ID去字符串池中取。
这里比如前三个:00000198,000001B6,00000350:对应的十进制为:408,438,848.
00000198对应的字符串地址为:,对应的字符串如下:
为:B
000001B6对应的字符串地址为:,对应的字符串如下:
为:C
00000350对应的字符串地址为:,对应的字符串如下:
为:Landroid/app/Activity;
以上三个大致可以发现这就是smali中的原始类型B,C和类Landroid/app/Activity。
kDexTypeProtoIdItem对应proto_ids_size和proto_ids_off字段,指向DexProtoId,表示方法声明的结构体,结构如下:
struct DexProtoId {
u4 shortyIdx; //指向DexStringId列表的索引
u4 returnTypeIdx; //指向DexTypeId列表的索引
u4 parametersOff; //指向DexTypeList的偏移
};
并且其中shortyIdx为方法声明字符串,returnTypeIdx为方法返回类型字符串,parametersOff为指向一个DexTypeList结构体,存放了方法的参数列表。声明如下:
StructDexTypeList{
U4 size; //DexTypeItrm的个数
DexTypeItrm list[1]; //DexTypeItrm结构
};
DexTypeItrm结构如下:
StructDexTypeItrm{
U2 typeIdx; //指向DexTypeId列表的索引
};
由上可知kDexTypeProtoIdItem的地址为 ,并且有1010个。下图就是指向的DexProtoId结构
在去DexTypeId索引的时候,起始位置是0,不是1,fuck!
可以看到每三个字节为一个结构体:我们随机提取两个:
BF 01 00 00 0100 00 00 4C 88 01 00;对应就是1BF,1,1884C
所以shortyIdx表示DexStringId的第447个,找到其地址为00 05E1 8C。在这个地址下找到字符串为:CI
returnTypeIdx 指向DexTypeId的第1个就是上面找到的C
parametersOff偏移地址为1884C,对应数据如图
Size=1,DexTypeItrm=0x4.指向DexTypeId的第四个地址是:BF 0200 00。找到对应的字符串地址为:4C FB 05 00。找到对应字符串为I
所以可以知道这个的方法有一个参数并且类型是I,返回值类型为C,方法声明为CI
5D 02 00 00 0300 00 00 EC 87 01 00;对应就是25D,3,187EC
所以shortyIdx表示DexStringId的第605个,找到其地址为00 05F2 63。在这个地址下找到字符串为:FF
returnTypeIdx 指向DexTypeId的第3个对应的字符串ID是593,对应的字符串地址为86F1 05 00,对应字符串为:F
parametersOff偏移地址为187EC,对应数据如图
Size=1,DexTypeItrm=0x3.指向DexTypeId的第三个地址找到对应的字符串地址为:86 F1 05 00。找到对应字符串为:F
所以可以知道这个的方法有一个参数并且类型是F,返回值类型为F, 方法声明为FF
kDexTypeFieldIdItem对应着field_ids_size和field_ids_off字段指向的结构为DexFieldId:表示代码中的字段。结构如下:
struct DexFieldId {
u2 classIdx; //指向DexTypeId列表索引,表示字段所属的类
u2 typeIdx; //指向DexTypeId列表索引,表示字段类型
u4 nameIdx; //指向DexStringId列表索引,表示字段名
};
找到偏移为0x00009128的DexFieldId结构体位置,有1193个结构体,如图:
这里一定要注意读取顺序,两位和四位的读取方式一定要注意,比如读取classldx和typeldx,是两位两位读取,如果按照四位如nameidx方式读取理解,就会把参数位置弄混。
根据结构体可以知道每两个字节代表一个结构体,我们随机选取几个来分析:
00 10 00 04 00 00 09 57:classIdx=16;typeIdx=4;nameIdx=0x957=2391
typeIdx地址为BF 02 00 00,对应string地址为4C FB 0500,字符串为:I
classIdx地址为57 03 00 00,对应string地址为B0 03 0600,字符串为:Landroid/app/Notification;
nameIdx字符串地址为42 D3 06 00,对应字符串为audioStreamType。
我们在smali中可以看到如下图:
看到上图就可以知道这个结构体包含的是什么了。
就是 classIdx->nameIdx: typeIdx.
kDexTypeMethodIdItem对应method_ids_size和method_ids_off,指向DexMethodId结构体,表示代码中使用的方法,结构体如下:
struct DexMethodId {
u2 classIdx; //指向DexTypeId列表索引,表示方法所属的类
u2 protoIdx; //指向DexProtoId列表索引,表示方法原型
u4 nameIdx; //指向DexStringId列表索引,表示方法名;
};
在偏移0x0000B670位置有4820 DexMethodId结构体个如下图:
选取第一个00 06 03 7E 00 00 0B DF为例:classIdx=0x6, protoIdx=0x37E,nameIdx=00000BDF
classIdx:对应的type地址为4D 03 0000,字符串地址为4E 02 06 00,字符串为Landroid/accessibilityservice/AccessibilityServiceInfo;
protoIdx:对应DexProtoId的第0x37E=894个,地址和内容如图。
根据DexProtoId结构解析:shortyIdx=0x857;returnTypeIdx=0x2D8;parametersOff=0
shortyIdx对应字符串为23 C306 00:Z
returnTypeIdx字符串为EB C3 0600:[B
parametersOff不存在
nameIdx:对应地址为E8 F6 06 00字符串为:getCanRetrieveWindowContent
对应smali可以看到下图
可以发现,实现了getCanRetrieveWindowContent方法。这里都是实现的各种方法。
kDexTypeClassDefItem对应着class_defs_size和class_defs_off字段。其指向的结构体为
typedefstruct DexClassDef {
u4 classIdx; //类的类型,指向DexTypeId列表
u4 accessFlags; //访问标志
u4 superclassIdx; //父类类型,指向DexTypeId列表
u4 interfacesOff; //接口,指向DexTypeList偏移
u4 sourceFileIdx; //源文件名,指向DexStringId列表索引
u4 annotationsOff; //注解,指向DexAnnotationsDirectoryItem结构
u4 classDataOff; //指向DexclassData结构
u4 staticValuesOff; //指向DexEncodedArray结构的偏移
}DexClassDef;
accessFlags字段是类的访问标志,是以ACC_开头的一个枚举值。如果存在接口interfacesOff就会指向一个DexTypeList结构,否则为0.
DexclassData结构如下:
typedefstruct DexClassData {
DexClassDataHeader header; //指定字段与方法的个数
DexField* staticFields; //静态字段,DexField结构
DexField* instanceFields; //实例字段,DexField结构
DexMethod* directMethods; //直接方法,DexMethod结构
DexMethod* virtualMethods; //虚方法,DexMethod结构
} DexClassData;
其中DexClassDataHeader结构记录了当前类中字段与方法的数目,声明如下:
typedefstruct DexClassDataHeader {
u4 staticFieldsSize; //静态字段个数
u4 instanceFieldsSize; //实例字段个数
u4 directMethodsSize; //直接方法个数
u4 virtualMethodsSize; //虚方法个数
} DexClassDataHeader;
DexField结构描述了字段的类型和访问标志,结构如下:
typedefstruct DexField {
u4 fieldIdx; //指向DexFieldId的索引
u4 accessFlags; //访问标志
} DexField;
DexMethod结构描述了方法的原型,名称,访问标志以及代码数据块
typedef structDexMethod {
u4 methodIdx; //指向DexMethodId索引
u4 accessFlags; //访问标志
u4 codeOff; //指向DexCode结构的偏移
} DexMethod;
DexCode结构详细的描述了方法的信息以及方法指令的内容
typedefstruct DexCode {
u2 registersSize; //使用的寄存器个数
u2 insSize; //参数个数
u2 outsSize; //调用其它方法时使用的寄存器个数
u2 triesSize; //Try/Catch个数
u4 debugInfoOff; //指向调试信息的偏移
u4 insnsSize; //指令集个数,以2字节为单位
u2 insns[1]; //指令集
//2字节对齐
//try_item[triesSize] DexTry结构
//Try/Catch中handler个数
//catch_handler_item[handlersSize],DexCatchHandler结构
}DexCode;
在0x00014D10偏移的位置找到DexClassDef结构起始位置如图:
这里我们选取如下的位置:
classIdx; =0000 00 71 DexTypeId=BB 03 00 00 stringoff=80 14 06 00 A9 14 06 00
string=Landroid/support/v4/app/DialogFragment;
Landroid/support/v4/app/Fragment$1
accessFlags; =00 00 00 10 对应下表:不生子类
superclassIdx; =00 00 02 A6 DexTypeId=18 06 00 00 stringoff= 36 92 06 00 4C 9206 00
string=Ljava/lang/Runnable;
Ljava/lang/RuntimeException;
interfacesOff; =00 01 90 58 DexTypeList=0100 00 00 52 00 00 00 size=1 typeid=0x52
DexTypeId=9C 03 00 00 stringoff= FF 0C 06 00
String=Landroid/os/Parcelable;
sourceFileIdx; =00 00 01 A0 stringoff=94 DE 05 00
string=BackStackRecord.java
annotationsOff; =00 09 5F 3C
classDataOff; =00 01 B5 57 header=01 09 03 03 这里采用的是uleb128的取值方式,所以对应的 staticFieldsSize; 0x1 静态字段个数
instanceFieldsSize; 0x9 实例字段个数
directMethodsSize; 0x3 直接方法个数
virtualMethodsSize; 0x3 虚方法个数
首先是DexField结构:fieldIdx =0x67 从0开始第103个 内容:71 00 04 00 84 0E00 00
classIdx=0x71 BB030000 Landroid/support/v4/app/BackStackState;
typeIdx=0x4 string=I
nameIdx=0XE84 string= mBreadCrumbShortTitleRes
accessFlags = 19 ACC_PUBLIC| ACC_STATIC|ACC_FINAL
staticValuesOff; =00 00 00 00
Name
Value
For Classes (and InnerClass annotations)
For Fields
For Methods
ACC_PUBLIC
0x1
public: visible everywhere
public: visible everywhere
public: visible everywhere
ACC_PRIVATE
0x2
* private: only visible to defining class
private: only visible to defining class
private: only visible to defining class
ACC_PROTECTED
0x4
* protected: visible to package and subclasses
protected: visible to package and subclasses
protected: visible to package and subclasses
ACC_STATIC
0x8
* static: is not constructed with an outer this reference
static: global to defining class
static: does not take a this argument
ACC_FINAL
0x10
final: not subclassable
final: immutable after construction
final: not overridable
ACC_SYNCHRONIZED
0x20
synchronized: associated lock automatically acquired around call to this method.Note: This is only valid to set when ACC_NATIVE is also set.
ACC_BRIDGE
0x40
bridge method, added automatically by compiler as a type-safe bridge
ACC_VOLATILE
0x40
volatile: special access rules to help with thread safety
ACC_TRANSIENT
0x80
transient: not to be saved by default serialization
ACC_VARARGS
0x80
last argument should be treated as a “rest” argument by compiler
ACC_NATIVE
0x100
native: implemented in native code
ACC_INTERFACE
0x200
interface: multiply-implementable abstract class
ACC_ABSTRACT
0x400
abstract: not directly instantiable
abstract: unimplemented by this class
ACC_STRICT
0x800
strictfp: strict rules for floating-point arithmetic
ACC_SYNTHETIC
0x1000
not directly defined in source code
not directly defined in source code
not directly defined in source code
ACC_ANNOTATION
0x2000
declared as an annotation class
ACC_ENUM
0x4000
declared as an enumerated type
declared as an enumerated value
(unused)
0x8000
ACC_CONSTRUCTOR
0x10000
constructor method (class or instance initializer)
ACC_DECLARED_SYNCHRONIZED
0x20000
declared synchronized. Note: This has no effect on execution (other than in reflection of this flag, per se).
1.<activityandroid:label="@string/app_name"android:name=".FileBrowser">
2.<intent-filter>
3.<actionandroid:name="android.intent.action.MAIN"/>
4.<categoryandroid:name="android.intent.category.LAUNCHER"/>
依次解释为:
1. android:label表示Activity的标题。android:name指定了Activity具体的类
2. <intent-filter>表示这个类的启动方式
3.当包含"android.intent.action.MAIN"的时候,这个表示上面的android:name=中保存的类是Activity的主类。
4.当包含"android.intent.category.LAUNCHER"的时候表示可以通过LAUNCHER来启动。
如果没有发现"android.intent.action.MAIN"和"android.intent.category.LAUNCHER"表示这个程序安装成功后不存在界面和图标,是隐藏的。
这个时候我们可以去Activity的主类中找到OnCreate()。这个位置就是程序的起始位置
- Android_dex详解
- 详解
- 详解
- 详解
- 详解
- &,&&,|,||详解
- 详解
- Scala详解--------基础知识详解
- Spring详解-----------事务详解
- github 详解详解
- InputFilter详解、TextWatcher详解
- Spring详解-----------事务详解
- 【词汇详解】事务详解
- Session 详解
- Session 详解
- sizeof详解
- 端口详解
- Meta详解~~~
- Guava学习——集合工具
- websocke笔记
- Android android:launchMode=“singleInstance”启动模式设置单例 页面跳转黑屏
- Xgboost推导及分析
- 数据结构与算法C++描述(7)---堆栈及其在“火车车厢重排问题”中的应用
- Android_dex详解
- 图片无限轮播框架Banner的使用
- 518抽奖软件源码之:使得除零异常可被捕获(VC++)
- Oracle数据库 总结
- JavaScript This指向
- 2015中国科技专利调查样本
- 队列优化多重背包
- java-随机点名器(Random )
- (C语言版)链表(四)——实现双向循环链表创建、插入、删除、释放内存等简单操作