部分内容摘自:android软件安全权威指南:丰生强
从根源上搞懂基础的原理是很有必要的,这样有助于我们更方便的利用它的特性,达到我们的目的。
我把内容主要分为二个部分。原理探索、案例分析
我们在正向开发app编译时,编写的java代码,会编译成java字节码保存在.class后缀的文件中。然后再用dx工具将java字节码转换成dex文件(Dalvik字节码)。在转换的过程中,会将所有java字节码中的所有冗余信息组成一个常量池。例如多个class文件中都存在的字符串"hello world"。转换后将单独存放在一个地方,并且所有类共享。包括方法的签名也会组成常量池。我们将编译好的apk文件解压后就能拿到classes.dex文件。
上面拿到的classes.dex文件包含了apk的可执行代码。Dalvik虚拟机会解析加载文件并执行代码。只要我们了解这个文件格式的组成,那么就可以自己解析这个文件获取到想要的数据。
首先是安卓源码中的dalvik/libdex/DexFile.h
这里可以找到dex文件的数据结构。下面贴上源码部分
为了更好的理解。dex文件格式,我们可以用010编辑器打开一个dex文件对照这个结构体来观察一下。
可以看到,这个dex文件由这8个部分组成。
dex_header:dex文件头,指定了dex文件的一些数据,记录了其他数据结构在dex文件中的物理偏移
string_ids:字符串列表(前面说的去掉冗余信息组成的常量池,全局共享使用的)
type_ids:类型签名列表(去掉冗余信息组成的常量池)
proto_ids:方法声明列表(去掉冗余信息组成的常量池)
field_ids:字段列表(去掉冗余信息组成的常量池)
method_ids:方法列表(去掉冗余信息组成的常量池)
class_def:类型结构体列表(去掉冗余信息组成的常量池)
map_list:这里记录了前面7个部分的偏移和大小。
然后我们开始逐个的看各个部分的结构。
先是贴上源码看看这个部分的结构体
这里可以看到,如果在DexHeader中,可以找到其他部分的偏移和大小,以及整个文件的大小,解析出这块数据,其他部分的任意数据,我们都可以获取到。然后再使用对应的结构体来解析。另外留意这里DexHeader的结构体的大小是固定0x70字节的。所以有的脱壳工具中会将70 00 00 00
来作为特征在内存中查找dex进行脱壳(比如FRIDA-DEXDump的深度检索)
然后我们贴一下真实classes.dex文件的DexHeader数据是什么样的。
先看看字符串列表的结构体,非常简单,就是字符串的偏移,但是并不是普通的ascii字符串,而是MUTF-8编码的。这个是一个经过修改的UTF-8编码。和传统的UTF-8相似
类型签名列表的结构体也是非常简单,和上面字符串列表差不多
真实数据图如下,可以看到值类型签名都在前面,后面都是引用类型签名。
方法声明的列表的结构体较为复杂,因为方法签名必然是有几点信息构成:返回值类型、参数类型列表(就是每个参数是什么类型)。方法声明的结构体如下
同样看看这个结构的真实数据
字段描述的结构体,我们可以先想象一下,要找一个字段,我们需要些什么:字段所属的类,字段的类型,字段名称。有这些信息,就可以找到各自对应的字段了。接下来看看定义的结构体
然后看一段真实数据
方法描述的结构体,同样先了解找一个方法的几个必须项:方法所属的类,方法的签名(签名中有方法的返回值和方法的参数,也就是上面的proto_ids中记录的),方法的名称。然后下面看结构体
看看一组方法的真实数据
类定义的结构体,这个比较复杂。直接贴上结构体和原文的说明。这里大致可以看出来,和上面的原理差不多,通过这个结构体来描述类的内容。
下面同样展示一组真实数据
上面数据看到里面的class_data也是一个结构体,然后继续看这个类数据的结构体
到这里我们基本看到了在开发中,一个类的所有特征。完整的描述出了一个类的所有信息。
9、DexCode上面最后看到方法的代码是通过上面的DexCode结构体来找到的。最后看下这个结构体
到了这里,存储的就是执行的指令集了。通过执行指令来跑这个方法。下面看一组真实数据
这里看到这个类的具体描述字段和函数,还有访问标志等等信息。然后我们继续看里面函数的执行代码部分。看下面一组数据
观察的函数是MainActivity类的DoTcp函数。DexCode(也就是code_item,也叫codeOff)的偏移地址是0x127ec。
下面观察到DexCode结构体的偏移到指令集insns字段的偏移是codeOff+2+2+2+2+4+4=0x127fc(这里+的2和4是看下面结构体insns前面的字段占了多少个字节计算的,可以当做固定+16个字节)。指令集的长度是0x93。
最后看看指令集的开始数据是0x62、0xe26、0x11a、0x232。但是我们要注意前面有说明,这里是两字节空间对齐。所以,这里的值我们应该前面填充一下。
前面四个字节我们要看做0x0062、0x0e26、0x011a、0x0232。但是我们还要注意,还有个端序问题会影响字节的顺序,这里是小端序,所以我们再调整下
前面四个字节我们要看做0x6200、0x260e、0x1a01、0x3202。把这段指令集的数据看明白后,我们用gda打开这个dex文件。然后找到对应的方法,查看一下。
然后发现数据对上了。这里存储的果然就是我们dex分析方法的字节码了。
在书中的意思是,Dalvik虚拟机解析Dex后,将其映射成DexMapList的数据结构,然后在里面可以找到前面8个部分的偏移和大小。先看看结构体
每个DexMapItem对应了一块数据,例如type=kDexTypeHeaderItem则对应DexHeader的偏移地址和大小。下面看真实数据
这里就能看到string_ids的偏移和大小。如此,根据这个map_list就能找到所有块的数据了。
那么在源码中是如何使用这个数据的呢,我好奇的翻了一下。然后再dex文件优化的流程中dexSwapAndVerify函数找到了使用的地方。
这个函数中获取了DexMapList。然后交给swapMap函数来处理。
通过这里的例子。我们可以看到他是如何使用这个map_list来访问所有的部分的。到这里dex文件的格式基本差不多了。
然后我们看看一个实战的项目。大佬写的fart中的py部分就的运用了dex文件格式相关的知识。
FART
这是一个脱壳工具,使用主动调用的方式来解决二代抽取壳。脱出来的数据不止是dex。还有一种.bin的数据,这种数据可以用来辅助我们修复dex的一些没有脱出来的函数。我们先看下.bin数据是什么,下面是.bin中的一组数据
name:是随意填的,因为并不是使用name来找对应函数,而是通过method_idx
method_idx:函数的索引。
offset:函数的偏移
code_item_len:code_item的大小
ins:code_item结构体这段数据的base64编码
另外贴一下这几个数据的dump来源的代码,以防有人把ins当成指令集了。
这几个数据是一个函数最关键的片段。拿到就可以还原出最关键的code_item了。
然后看看fart.py的使用./fart.py -d 431528_29868.dex -i 431528_29868.bin
。下面是执行结果,基本对这个dex进行完整的解析,打印出来大多数的数据。
我们可以通过对这个项目的阅读,直观的了解到是如何进行dex文件格式进行解析的。下面开始看看具体的实现流程
然后看看是如何加载.bin文件的
可以看到就是遍历,组装好那些数据,最后保存到一个methodTable里面,索引就是method_id。
再看看最关键的dex_parse
这里看到是通过init_header来解析dex文件中的dexHeader的。我就不贴代码了,整体就是偏移然后取数据。
这里有一点要说的是get_uleb128函数,uleb128在android里面是一个特殊的类型。是一个可变长度的类型。大致意思就是例如第一个字节的最高位,如果是1,则第二个字节也是有效数据,如果第二个字节的最高位也是1,下一个字节也是有效数据,如果最高位不是1,就结束了。最后左移拼接就ok了。
这个类型有没有很眼熟?没错,特别像是protobuf里面的varint编码。所以我觉得完全可以用protobuf包自带的varint解码来获取这个数据。也可以用这种自己写的函数来处理。
继续看后面的关键函数dex_class,这里来处理每一个类的打印
先看看初始化函数,基本和之前的initHeader的差不多,就是偏移取数据,然后保存下来。把classDef相关的数据都读取出来了。
最后就是print函数,这里比较大,就只挑最关键的部分出来说下
最后我们看下怎么获取的bin文件数据的函数
最后还有个打印指令集的部分就不贴了。感兴趣的可以自己看一看
dex2jar
这个工具基本大家都用过。功能非常强大,不过这里只分析下d2j_dex2jar功能。也就是把dex给转换成jar文件。
下载release版本直接./d2j-dex2jar.sh classes.dex
,就生成出了对应的classes-dex2jar.jar文件。然后我们直接用其他分析工具就能愉快的看java代码了。那么神奇的事情是如何做到的呢。下面跟踪分析一下大佬的作品。
第一步是找到入口,根据我们上面的使用例子,先搜索下d2j-dex2jar。然后找到了下面的文件
./dex2jar/dex-tools/src/main/java/com/googlecode/dex2jar/tools/Dex2jarCmd.java
先简单的看一下这里的代码,看来这里就是入口函数了。
然后看看doMain的实现
这里调用了抽象方法,doCommandLine,所以继续看看实现
到这里就可以看出来两个最重要的部分了
1、解析dex的 MultiDexFileReader.open方法
2、执行转换的Dex2jar的to方法
下面先看看解析的处理
我们直接看参数为dex文件的情况就好了,所以继续看DexFileReader的构造函数
上面看了dexHeader的解析的核心部分,接下来我们看看转换实现to方法
继续看看doTranslate方法
我们主要观察的是dex的结构和解析,所以就不详细看转换部分了,继续看解析部分的后续accept方法
这里最关键的就是acceptClass来解析具体的类的内容
对类数据进行详细解析后,接着是对字段和方法的解析填充。先看看字段的解析处理
字段填充完毕,然后看看方法是怎么解析填充的。
继续看code_item的解析
最后的acceptInsn指令集的解析方法太大了,我就不放上来了。整个解析填充的流程就完成了。后面就使用解析好的数据进行转换的操作。
可以看到两个案例的解析的方式差不多,总体都是根据结构体的大小偏移来取得想要的数据。最后一层一层的处理。
为了方便使用和测试,我将fart的解析整理了一下,改到了python3运行的。然后整合到了我的整合怪里面。感兴趣的可以看看。刚跑通,不知道有没啥问题,后面我再慢慢修复把。
github:fridaUiTools
贴上效果图
fart的修复方案仅仅是打印出了保存的指令数据。如果我们是想要直接转成.class文件或者是jar文件。是否可行呢。
我设想了两种方案来做,但是最后都被自己给否定了。
1、bin文件中的指令数据直接插入到dex文件的对应数据中,最后保存为一个新的文件。但是这样中间插一段数据,会有大量的偏移数据要修改。
2、和dex2jar的模式一样。构造好一个fileNode对象,里面就不存在偏移的问题了。最后把里面的类数据导出成.class文件。
疑问中的目的主要是想要fart.py跑完后直接输出一个新的dex文件,里面把bin文件的函数都填充到新的dex了。然后可以用jadx之类的工具直接打开查看
最早先从修复的角度想,简单的处理就是把bin中的内容解析成codeitem后,插到method_idx的里面对应的函数去。但是由于指令集的大小和原来不一样。肯定会导致后面数据的偏移全部发生变化。
于是我分析了一下dex2jar。然后发现确实有更好的办法
和原来的目前一样。但是把修复两个字调整一下说法。具体应该叫dex转换dex
先看看dex2jar的做法是:解析dex,dex数据全部展开解析为java结构体,与偏移无关了,然后转换.class的结构,写入文件,最后打包成jar
学习到人家的思路之后。我们可以变化下自己的思路,下面是我设想的两个解决方案。目前还没有做哦。但是感觉可行性非常高
一:fart修改的解决方案
fart中定义dex的完整结构体,fart解析完dex后,直接将数据保存在结构体中,达到了偏移无关了。然后在遍历bin的数据。将code_item数据替换。然后再将整个dex结构体重新解析生成一个新的dex。
二:dex2jar的功能新增解决方案
就是给dex2jar新增一个功能。前面完全按照他的方式解析出fileNode,然后读取bin文件解析出code_item。然后替换fileNode中对应的code_item数据。然后再重新生成回dex文件。
struct DexFile {
/
*
odex的头
*
/
const DexOptHeader
*
pOptHeader;
/
*
dex文件头,指定了dex文件的一些数据,记录了其他数据结构在dex文件中的物理偏移
*
/
const DexHeader
*
pHeader;
/
*
索引结构区
*
/
const DexStringId
*
pStringIds;
const DexTypeId
*
pTypeIds;
const DexFieldId
*
pFieldIds;
const DexMethodId
*
pMethodIds;
const DexProtoId
*
pProtoIds;
/
*
真实的数据存放
*
/
const DexClassDef
*
pClassDefs;
/
*
静态链接数据区
*
/
const DexLink
*
pLinkData;
/
*
*
These are mapped out of the
"auxillary"
section,
and
may
not
be
*
included
in
the
file
.
*
/
const DexClassLookup
*
pClassLookup;
const void
*
pRegisterMapPool;
/
/
RegisterMapClassPool
/
*
points to start of DEX
file
data
*
/
const u1
*
baseAddr;
/
*
track memory overhead
for
auxillary structures
*
/
int
overhead;
/
*
additional app
-
specific data structures associated with the DEX
*
/
/
/
void
*
auxData;
};
struct DexFile {
/
*
odex的头
*
/
const DexOptHeader
*
pOptHeader;
/
*
dex文件头,指定了dex文件的一些数据,记录了其他数据结构在dex文件中的物理偏移
*
/
const DexHeader
*
pHeader;
/
*
索引结构区
*
/
const DexStringId
*
pStringIds;
const DexTypeId
*
pTypeIds;
const DexFieldId
*
pFieldIds;
const DexMethodId
*
pMethodIds;
const DexProtoId
*
pProtoIds;
/
*
真实的数据存放
*
/
const DexClassDef
*
pClassDefs;
/
*
静态链接数据区
*
/
const DexLink
*
pLinkData;
/
*
*
These are mapped out of the
"auxillary"
section,
and
may
not
be
*
included
in
the
file
.
*
/
const DexClassLookup
*
pClassLookup;
const void
*
pRegisterMapPool;
/
/
RegisterMapClassPool
/
*
points to start of DEX
file
data
*
/
const u1
*
baseAddr;
/
*
track memory overhead
for
auxillary structures
*
/
int
overhead;
/
*
additional app
-
specific data structures associated with the DEX
*
/
/
/
void
*
auxData;
};
struct DexHeader {
u1 magic[
8
];
/
*
表示是一个有效的dex文件。值一般固定为
64
65
78
0A
30
33
35
00
(dex.
035
)
*
/
u4 checksum;
/
*
adler32 checksum dex文件的校验和,用来判断文件是否已经损坏或者篡改
*
/
u1 signature[kSHA1DigestLen];
/
*
SHA
-
1
hash
用来识别未经dexopt优化的dex文件
*
/
u4 fileSize;
/
*
length of entire
file
记录了包括dexHeader在内的整个dex文件的大小
*
/
u4 headerSize;
/
*
offset to start of
next
section dexHeader占用的字节数,一般都是
0x70
*
/
u4 endianTag;
/
*
指定dex运行环境的cpu字节序。预设是ENDIAN_CONSTANT等于
0x12345678
,也就是默认小端字节序
*
/
u4 linkSize;
/
*
链接段的大小
*
/
u4 linkOff;
/
*
链接段的偏移
*
/
u4 mapOff;
/
*
DexMapList结构的文件偏移
*
/
u4 stringIdsSize;
/
*
下面都是数据段的大小和文件偏移
*
/
u4 stringIdsOff;
u4 typeIdsSize;
u4 typeIdsOff;
u4 protoIdsSize;
u4 protoIdsOff;
u4 fieldIdsSize;
u4 fieldIdsOff;
u4 methodIdsSize;
u4 methodIdsOff;
u4 classDefsSize;
u4 classDefsOff;
u4 dataSize;
u4 dataOff;
};
struct DexHeader {
u1 magic[
8
];
/
*
表示是一个有效的dex文件。值一般固定为
64
65
78
0A
30
33
35
00
(dex.
035
)
*
/
u4 checksum;
/
*
adler32 checksum dex文件的校验和,用来判断文件是否已经损坏或者篡改
*
/
u1 signature[kSHA1DigestLen];
/
*
SHA
-
1
hash
用来识别未经dexopt优化的dex文件
*
/
u4 fileSize;
/
*
length of entire
file
记录了包括dexHeader在内的整个dex文件的大小
*
/
u4 headerSize;
/
*
offset to start of
next
section dexHeader占用的字节数,一般都是
0x70
*
/
u4 endianTag;
/
*
指定dex运行环境的cpu字节序。预设是ENDIAN_CONSTANT等于
0x12345678
,也就是默认小端字节序
*
/
u4 linkSize;
/
*
链接段的大小
*
/
u4 linkOff;
/
*
链接段的偏移
*
/
u4 mapOff;
/
*
DexMapList结构的文件偏移
*
/
u4 stringIdsSize;
/
*
下面都是数据段的大小和文件偏移
*
/
u4 stringIdsOff;
u4 typeIdsSize;
u4 typeIdsOff;
u4 protoIdsSize;
u4 protoIdsOff;
u4 fieldIdsSize;
u4 fieldIdsOff;
u4 methodIdsSize;
u4 methodIdsOff;
u4 classDefsSize;
u4 classDefsOff;
u4 dataSize;
u4 dataOff;
};
struct DexStringId {
u4 stringDataOff;
/
*
字符串数据偏移
*
/
};
struct DexStringId {
u4 stringDataOff;
/
*
字符串数据偏移
*
/
};
struct DexTypeId {
u4 descriptorIdx;
/
*
index into stringIds
list
for
type
descriptor
*
/
};
struct DexTypeId {
u4 descriptorIdx;
/
*
index into stringIds
list
for
type
descriptor
*
/
};
struct DexTypeList {
u4 size;
/
*
dexTypeItem的个数
*
/
DexTypeItem
list
[
1
];
/
*
entries
*
/
};
struct DexTypeItem {
u2 typeIdx;
/
*
DexTypeId的索引
*
/
};
struct DexProtoId {
u4 shortyIdx;
/
*
DexStringId列表的索引,方法签名字符串,由返回值和参数类型列表组合
*
/
u4 returnTypeIdx;
/
*
DexTypeId的索引,返回值的类型
*
/
u4 parametersOff;
/
*
指向DexTypeList的偏移,参数类型列表
*
/
};
struct DexTypeList {
u4 size;
/
*
dexTypeItem的个数
*
/
DexTypeItem
list
[
1
];
/
*
entries
*
/
};
struct DexTypeItem {
u2 typeIdx;
/
*
DexTypeId的索引
*
/
};
struct DexProtoId {
u4 shortyIdx;
/
*
DexStringId列表的索引,方法签名字符串,由返回值和参数类型列表组合
*
/
u4 returnTypeIdx;
/
*
DexTypeId的索引,返回值的类型
*
/
u4 parametersOff;
/
*
指向DexTypeList的偏移,参数类型列表
*
/
};
struct DexFieldId {
u2 classIdx;
/
*
类的类型,指向DexTypeId的索引,字段所属的类
*
/
u2 typeIdx;
/
*
字段类型,指向DexTypeId的索引,字段的类型
*
/
u4 nameIdx;
/
*
字段名,指向DexStringId的索引,字段的名称
*
/
};
struct DexFieldId {
u2 classIdx;
/
*
类的类型,指向DexTypeId的索引,字段所属的类
*
/
u2 typeIdx;
/
*
字段类型,指向DexTypeId的索引,字段的类型
*
/
u4 nameIdx;
/
*
字段名,指向DexStringId的索引,字段的名称
*
/
};
struct DexMethodId {
u2 classIdx;
/
*
类的类型,指向DexTypeId的索引,方法所属的类
*
/
u2 protoIdx;
/
*
声明类型,指向DexProtoId的索引,方法的签名
*
/
u4 nameIdx;
/
*
方法名,指向DexStringId索引,方法的名称
*
/
};
struct DexMethodId {
u2 classIdx;
/
*
类的类型,指向DexTypeId的索引,方法所属的类
*
/
u2 protoIdx;
/
*
声明类型,指向DexProtoId的索引,方法的签名
*
/
u4 nameIdx;
/
*
方法名,指向DexStringId索引,方法的名称
*
/
};
struct DexClassDef {
u4 classIdx;
/
*
类的类型,指向DexTypeId的索引
*
/
u4 accessFlags;
/
*
访问标志
*
/
u4 superclassIdx;
/
*
父类的类型,指向DexTypeId的索引
*
/
u4 interfacesOff;
/
*
接口,指向DexTypeList的偏移,如果没有接口的声明和实现,值为
0
*
/
u4 sourceFileIdx;
/
*
类所在的源文件名,指向DexStringId的索引
*
/
u4 annotationsOff;
/
*
注释,根据类型不同会有注解类,注解字段,注解方法,注解参数,没有注解值就是
0
,指向DexAnnotationsDirectoryItem的结构体
*
/
u4 classDataOff;
/
*
类的数据部分,指向DexClassData结构的偏移
*
/
u4 staticValuesOff;
/
*
类中的静态数据,指向DexEncodeArray结构的偏移
*
/
};
struct DexClassDef {
u4 classIdx;
/
*
类的类型,指向DexTypeId的索引
*
/
u4 accessFlags;
/
*
访问标志
*
/
u4 superclassIdx;
/
*
父类的类型,指向DexTypeId的索引
*
/
u4 interfacesOff;
/
*
接口,指向DexTypeList的偏移,如果没有接口的声明和实现,值为
0
*
/
u4 sourceFileIdx;
/
*
类所在的源文件名,指向DexStringId的索引
*
/
u4 annotationsOff;
/
*
注释,根据类型不同会有注解类,注解字段,注解方法,注解参数,没有注解值就是
0
,指向DexAnnotationsDirectoryItem的结构体
*
/
u4 classDataOff;
/
*
类的数据部分,指向DexClassData结构的偏移
*
/
u4 staticValuesOff;
/
*
类中的静态数据,指向DexEncodeArray结构的偏移
*
/
};
/
*
expanded form of a class_data_item header
*
/
struct DexClassDataHeader {
u4 staticFieldsSize;
/
*
静态字段的个数
*
/
u4 instanceFieldsSize;
/
*
实例字段的个数
*
/
u4 directMethodsSize;
/
*
直接方法的个数
*
/
u4 virtualMethodsSize;
/
*
虚方法的个数
*
/
};
/
*
expanded form of encoded_field
*
/
struct DexField {
u4 fieldIdx;
/
*
指向DexFieldId的索引
*
/
u4 accessFlags;
/
*
访问标志
*
/
};
/
*
expanded form of encoded_method
*
/
struct DexMethod {
u4 methodIdx;
/
*
指向DexMethodId的索引
*
/
u4 accessFlags;
/
*
访问标志
*
/
u4 codeOff;
/
*
指向DexCode结构的偏移
*
/
};
struct DexClassData {
DexClassDataHeader header;
/
*
指定字段和方法的个数
*
/
DexField
*
staticFields;
/
*
静态字段
*
/
DexField
*
instanceFields;
/
*
实例字段
*
/
DexMethod
*
directMethods;
/
*
直接方法
*
/
DexMethod
*
virtualMethods;
/
*
虚方法
*
/
};
/
*
expanded form of a class_data_item header
*
/
struct DexClassDataHeader {
u4 staticFieldsSize;
/
*
静态字段的个数
*
/
u4 instanceFieldsSize;
/
*
实例字段的个数
*
/
u4 directMethodsSize;
/
*
直接方法的个数
*
/
u4 virtualMethodsSize;
/
*
虚方法的个数
*
/
};
/
*
expanded form of encoded_field
*
/
struct DexField {
u4 fieldIdx;
/
*
指向DexFieldId的索引
*
/
u4 accessFlags;
/
*
访问标志
*
/
};
/
*
expanded form of encoded_method
*
/
struct DexMethod {
u4 methodIdx;
/
*
指向DexMethodId的索引
*
/
u4 accessFlags;
/
*
访问标志
*
/
u4 codeOff;
/
*
指向DexCode结构的偏移
*
/
};
struct DexClassData {
DexClassDataHeader header;
/
*
指定字段和方法的个数
*
/
DexField
*
staticFields;
/
*
静态字段
*
/
DexField
*
instanceFields;
/
*
实例字段
*
/
DexMethod
*
directMethods;
/
*
直接方法
*
/
DexMethod
*
virtualMethods;
/
*
虚方法
*
/
};
struct DexCode {
u2 registersSize;
/
*
使用寄存器的个数
*
/
u2 insSize;
/
*
参数的个数
*
/
u2 outsSize;
/
*
调用其他方法时使用的寄存器个数
*
/
u2 triesSize;
/
*
try
/
catch语句的个数
*
/
u4 debugInfoOff;
/
*
指向调试信息的偏移
*
/
u4 insnsSize;
/
*
指令集的个数,以
2
字节为单位
*
/
u2 insns[
1
];
/
*
指令集
*
/
/
*
2
字节空间用于对齐
*
/
/
*
followed by try_item[triesSize] DexTry结构体
*
/
/
*
followed by uleb128 handlersSize
*
/
/
*
followed by catch_handler_item[handlersSize] DexCatchHandler结构体
*
/
};
struct DexCode {
u2 registersSize;
/
*
使用寄存器的个数
*
/
u2 insSize;
/
*
参数的个数
*
/
u2 outsSize;
/
*
调用其他方法时使用的寄存器个数
*
/
u2 triesSize;
/
*
try
/
catch语句的个数
*
/
u4 debugInfoOff;
/
*
指向调试信息的偏移
*
/
u4 insnsSize;
/
*
指令集的个数,以
2
字节为单位
*
/
u2 insns[
1
];
/
*
指令集
*
/
/
*
2
字节空间用于对齐
*
/
/
*
followed by try_item[triesSize] DexTry结构体
*
/
/
*
followed by uleb128 handlersSize
*
/
/
*
followed by catch_handler_item[handlersSize] DexCatchHandler结构体
*
/
};
struct DexMapItem {
u2
type
;
/
*
kDexType开头的类型
*
/
u2 unused;
/
*
未使用,用于字节对齐
*
/
u4 size;
/
*
数据的大小
*
/
u4 offset;
/
*
指定类型数据的文件偏移
*
/
};
/
*
*
Direct
-
mapped
"map_list"
.
*
/
struct DexMapList {
u4 size;
/
*
有多少个DexMapItem
*
/
DexMapItem
list
[
1
];
/
*
entries
*
/
};
enum {
kDexTypeHeaderItem
=
0x0000
,
kDexTypeStringIdItem
=
0x0001
,
kDexTypeTypeIdItem
=
0x0002
,
kDexTypeProtoIdItem
=
0x0003
,
kDexTypeFieldIdItem
=
0x0004
,
kDexTypeMethodIdItem
=
0x0005
,
kDexTypeClassDefItem
=
0x0006
,
kDexTypeCallSiteIdItem
=
0x0007
,
kDexTypeMethodHandleItem
=
0x0008
,
kDexTypeMapList
=
0x1000
,
kDexTypeTypeList
=
0x1001
,
kDexTypeAnnotationSetRefList
=
0x1002
,
kDexTypeAnnotationSetItem
=
0x1003
,
kDexTypeClassDataItem
=
0x2000
,
kDexTypeCodeItem
=
0x2001
,
kDexTypeStringDataItem
=
0x2002
,
kDexTypeDebugInfoItem
=
0x2003
,
kDexTypeAnnotationItem
=
0x2004
,
kDexTypeEncodedArrayItem
=
0x2005
,
kDexTypeAnnotationsDirectoryItem
=
0x2006
,
};
struct DexMapItem {
u2
type
;
/
*
kDexType开头的类型
*
/
u2 unused;
/
*
未使用,用于字节对齐
*
/
u4 size;
/
*
数据的大小
*
/
u4 offset;
/
*
指定类型数据的文件偏移
*
/
};
/
*
*
Direct
-
mapped
"map_list"
.
*
/
struct DexMapList {
u4 size;
/
*
有多少个DexMapItem
*
/
DexMapItem
list
[
1
];
/
*
entries
*
/
};
enum {
kDexTypeHeaderItem
=
0x0000
,
kDexTypeStringIdItem
=
0x0001
,
kDexTypeTypeIdItem
=
0x0002
,
kDexTypeProtoIdItem
=
0x0003
,
kDexTypeFieldIdItem
=
0x0004
,
kDexTypeMethodIdItem
=
0x0005
,
kDexTypeClassDefItem
=
0x0006
,
kDexTypeCallSiteIdItem
=
0x0007
,
kDexTypeMethodHandleItem
=
0x0008
,
kDexTypeMapList
=
0x1000
,
kDexTypeTypeList
=
0x1001
,
kDexTypeAnnotationSetRefList
=
0x1002
,
kDexTypeAnnotationSetItem
=
0x1003
,
kDexTypeClassDataItem
=
0x2000
,
kDexTypeCodeItem
=
0x2001
,
kDexTypeStringDataItem
=
0x2002
,
kDexTypeDebugInfoItem
=
0x2003
,
kDexTypeAnnotationItem
=
0x2004
,
kDexTypeEncodedArrayItem
=
0x2005
,
kDexTypeAnnotationsDirectoryItem
=
0x2006
,
};
/
/
字节排序优化
int
dexSwapAndVerify(u1
*
addr, size_t
len
)
{
...
if
(okay) {
/
*
*
Look
for
the
map
. Swap it
and
then use it to find
and
swap
*
everything
else
.
*
/
if
(pHeader
-
>mapOff !
=
0
) {
DexFile dexFile;
DexMapList
*
pDexMap
=
(DexMapList
*
) (addr
+
pHeader
-
>mapOff);
okay
=
okay && swapMap(&state, pDexMap);
okay
=
okay && swapEverythingButHeaderAndMap(&state, pDexMap);
dexFileSetupBasicPointers(&dexFile, addr);
state.pDexFile
=
&dexFile;
okay
=
okay && crossVerifyEverything(&state, pDexMap);
}
else
{
ALOGE(
"ERROR: No map found; impossible to byte-swap and verify"
);
okay
=
false;
}
}
...
return
!okay;
/
/
0
=
=
success
}
/
/
字节排序优化
int
dexSwapAndVerify(u1
*
addr, size_t
len
)
{
...
if
(okay) {
/
*
*
Look
for
the
map
. Swap it
and
then use it to find
and
swap
*
everything
else
.
*
/
if
(pHeader
-
>mapOff !
=
0
) {
DexFile dexFile;
DexMapList
*
pDexMap
=
(DexMapList
*
) (addr
+
pHeader
-
>mapOff);
okay
=
okay && swapMap(&state, pDexMap);
okay
=
okay && swapEverythingButHeaderAndMap(&state, pDexMap);
dexFileSetupBasicPointers(&dexFile, addr);
state.pDexFile
=
&dexFile;
okay
=
okay && crossVerifyEverything(&state, pDexMap);
}
else
{
ALOGE(
"ERROR: No map found; impossible to byte-swap and verify"
);
okay
=
false;
}
}
...
return
!okay;
/
/
0
=
=
success
}
static
bool
swapMap(CheckState
*
state, DexMapList
*
pMap)
{
DexMapItem
*
item
=
pMap
-
>
list
;
u4 count;
u4 dataItemCount
=
0
;
/
/
Total count of items
in
the data section.
u4 dataItemsLeft
=
state
-
>pHeader
-
>dataSize;
/
/
See use below.
u4 usedBits
=
0
;
/
/
Bit
set
: one bit per section
bool
first
=
true;
u4 lastOffset
=
0
;
SWAP_FIELD4(pMap
-
>size);
count
=
pMap
-
>size;
const u4 sizeOfItem
=
(u4) sizeof(DexMapItem);
CHECK_LIST_SIZE(item, count, sizeOfItem);
while
(count
-
-
) {
SWAP_FIELD2(item
-
>
type
);
SWAP_FIELD2(item
-
>unused);
SWAP_FIELD4(item
-
>size);
SWAP_OFFSET4(item
-
>offset);
if
(first) {
first
=
false;
}
else
if
(lastOffset >
=
item
-
>offset) {
ALOGE(
"Out-of-order map item: %#x then %#x"
,
lastOffset, item
-
>offset);
return
false;
}
if
(item
-
>offset >
=
state
-
>pHeader
-
>fileSize) {
ALOGE(
"Map item after end of file: %x, size %#x"
,
item
-
>offset, state
-
>pHeader
-
>fileSize);
return
false;
}
if
(isDataSectionType(item
-
>
type
)) {
u4 icount
=
item
-
>size;
/
*
*
This sanity check on the data section items ensures that
*
there are no more items than the number of bytes
in
*
the data section.
*
/
if
(icount > dataItemsLeft) {
ALOGE(
"Unrealistically many items in the data section: "
"at least %d"
, dataItemCount
+
icount);
return
false;
}
dataItemsLeft
-
=
icount;
dataItemCount
+
=
icount;
}
u4 bit
=
mapTypeToBitMask(item
-
>
type
);
if
(bit
=
=
0
) {
return
false;
}
if
((usedBits & bit) !
=
0
) {
ALOGE(
"Duplicate map section of type %#x"
, item
-
>
type
);
return
false;
}
if
(item
-
>
type
=
=
kDexTypeCallSiteIdItem) {
state
-
>pCallSiteIds
=
item;
}
else
if
(item
-
>
type
=
=
kDexTypeMethodHandleItem) {
state
-
>pMethodHandleItems
=
item;
}
usedBits |
=
bit;
lastOffset
=
item
-
>offset;
item
+
+
;
}
if
((usedBits & mapTypeToBitMask(kDexTypeHeaderItem))
=
=
0
) {
ALOGE(
"Map is missing header entry"
);
return
false;
}
if
((usedBits & mapTypeToBitMask(kDexTypeMapList))
=
=
0
) {
ALOGE(
"Map is missing map_list entry"
);
return
false;
}
if
(((usedBits & mapTypeToBitMask(kDexTypeStringIdItem))
=
=
0
)
&& ((state
-
>pHeader
-
>stringIdsOff !
=
0
)
|| (state
-
>pHeader
-
>stringIdsSize !
=
0
))) {
ALOGE(
"Map is missing string_ids entry"
);
return
false;
}
if
(((usedBits & mapTypeToBitMask(kDexTypeTypeIdItem))
=
=
0
)
&& ((state
-
>pHeader
-
>typeIdsOff !
=
0
)
|| (state
-
>pHeader
-
>typeIdsSize !
=
0
))) {
ALOGE(
"Map is missing type_ids entry"
);
return
false;
}
if
(((usedBits & mapTypeToBitMask(kDexTypeProtoIdItem))
=
=
0
)
&& ((state
-
>pHeader
-
>protoIdsOff !
=
0
)
|| (state
-
>pHeader
-
>protoIdsSize !
=
0
))) {
ALOGE(
"Map is missing proto_ids entry"
);
return
false;
}
if
(((usedBits & mapTypeToBitMask(kDexTypeFieldIdItem))
=
=
0
)
&& ((state
-
>pHeader
-
>fieldIdsOff !
=
0
)
|| (state
-
>pHeader
-
>fieldIdsSize !
=
0
))) {
ALOGE(
"Map is missing field_ids entry"
);
return
false;
}
if
(((usedBits & mapTypeToBitMask(kDexTypeMethodIdItem))
=
=
0
)
&& ((state
-
>pHeader
-
>methodIdsOff !
=
0
)
|| (state
-
>pHeader
-
>methodIdsSize !
=
0
))) {
ALOGE(
"Map is missing method_ids entry"
);
return
false;
}
if
(((usedBits & mapTypeToBitMask(kDexTypeClassDefItem))
=
=
0
)
&& ((state
-
>pHeader
-
>classDefsOff !
=
0
)
|| (state
-
>pHeader
-
>classDefsSize !
=
0
))) {
ALOGE(
"Map is missing class_defs entry"
);
return
false;
}
state
-
>pDataMap
=
dexDataMapAlloc(dataItemCount);
if
(state
-
>pDataMap
=
=
NULL) {
ALOGE(
"Unable to allocate data map (size %#x)"
, dataItemCount);
return
false;
}
return
true;
}
static
bool
swapMap(CheckState
*
state, DexMapList
*
pMap)
{
DexMapItem
*
item
=
pMap
-
>
list
;
u4 count;
u4 dataItemCount
=
0
;
/
/
Total count of items
in
the data section.
u4 dataItemsLeft
=
state
-
>pHeader
-
>dataSize;
/
/
See use below.
u4 usedBits
=
0
;
/
/
Bit
set
: one bit per section
bool
first
=
true;
u4 lastOffset
=
0
;
SWAP_FIELD4(pMap
-
>size);
count
=
pMap
-
>size;
const u4 sizeOfItem
=
(u4) sizeof(DexMapItem);
CHECK_LIST_SIZE(item, count, sizeOfItem);
while
(count
-
-
) {
SWAP_FIELD2(item
-
>
type
);
SWAP_FIELD2(item
-
>unused);
SWAP_FIELD4(item
-
>size);
SWAP_OFFSET4(item
-
>offset);
if
(first) {
first
=
false;
}
else
if
(lastOffset >
=
item
-
>offset) {
ALOGE(
"Out-of-order map item: %#x then %#x"
,
lastOffset, item
-
>offset);
return
false;
}
if
(item
-
>offset >
=
state
-
>pHeader
-
>fileSize) {
ALOGE(
"Map item after end of file: %x, size %#x"
,
item
-
>offset, state
-
>pHeader
-
>fileSize);
return
false;
}
if
(isDataSectionType(item
-
>
type
)) {
u4 icount
=
item
-
>size;
/
*
*
This sanity check on the data section items ensures that
*
there are no more items than the number of bytes
in
*
the data section.
*
/
if
(icount > dataItemsLeft) {
ALOGE(
"Unrealistically many items in the data section: "
"at least %d"
, dataItemCount
+
icount);
return
false;
}
dataItemsLeft
-
=
icount;
dataItemCount
+
=
icount;
}
u4 bit
=
mapTypeToBitMask(item
-
>
type
);
if
(bit
=
=
0
) {
return
false;
}
if
((usedBits & bit) !
=
0
) {
ALOGE(
"Duplicate map section of type %#x"
, item
-
>
type
);
return
false;
}
if
(item
-
>
type
=
=
kDexTypeCallSiteIdItem) {
state
-
>pCallSiteIds
=
item;
}
else
if
(item
-
>
type
=
=
kDexTypeMethodHandleItem) {
state
-
>pMethodHandleItems
=
item;
}
usedBits |
=
bit;
lastOffset
=
item
-
>offset;
item
+
+
;
}
if
((usedBits & mapTypeToBitMask(kDexTypeHeaderItem))
=
=
0
) {
ALOGE(
"Map is missing header entry"
);
return
false;
}
if
((usedBits & mapTypeToBitMask(kDexTypeMapList))
=
=
0
) {
ALOGE(
"Map is missing map_list entry"
);
return
false;
}
if
(((usedBits & mapTypeToBitMask(kDexTypeStringIdItem))
=
=
0
)
&& ((state
-
>pHeader
-
>stringIdsOff !
=
0
)
|| (state
-
>pHeader
-
>stringIdsSize !
=
0
))) {
ALOGE(
"Map is missing string_ids entry"
);
return
false;
}
if
(((usedBits & mapTypeToBitMask(kDexTypeTypeIdItem))
=
=
0
)
&& ((state
-
>pHeader
-
>typeIdsOff !
=
0
)
|| (state
-
>pHeader
-
>typeIdsSize !
=
0
))) {
ALOGE(
"Map is missing type_ids entry"
);
return
false;
}
if
(((usedBits & mapTypeToBitMask(kDexTypeProtoIdItem))
=
=
0
)
&& ((state
-
>pHeader
-
>protoIdsOff !
=
0
)
|| (state
-
>pHeader
-
>protoIdsSize !
=
0
))) {
ALOGE(
"Map is missing proto_ids entry"
);
return
false;
}
if
(((usedBits & mapTypeToBitMask(kDexTypeFieldIdItem))
=
=
0
)
&& ((state
-
>pHeader
-
>fieldIdsOff !
=
0
)
|| (state
-
>pHeader
-
>fieldIdsSize !
=
0
))) {
ALOGE(
"Map is missing field_ids entry"
);
return
false;
}
if
(((usedBits & mapTypeToBitMask(kDexTypeMethodIdItem))
=
=
0
)
&& ((state
-
>pHeader
-
>methodIdsOff !
=
0
)
|| (state
-
>pHeader
-
>methodIdsSize !
=
0
))) {
ALOGE(
"Map is missing method_ids entry"
);
return
false;
}
if
(((usedBits & mapTypeToBitMask(kDexTypeClassDefItem))
=
=
0
)
&& ((state
-
>pHeader
-
>classDefsOff !
=
0
)
|| (state
-
>pHeader
-
>classDefsSize !
=
0
))) {
ALOGE(
"Map is missing class_defs entry"
);
return
false;
}
state
-
>pDataMap
=
dexDataMapAlloc(dataItemCount);
if
(state
-
>pDataMap
=
=
NULL) {
ALOGE(
"Unable to allocate data map (size %#x)"
, dataItemCount);
return
false;
}
return
true;
}
{name:ooxx,method_idx:
1830
,offset:
180516
,code_item_len:
24
,ins:AQABAAEAAAB
+
oAMABAAAAHAQwAsAAA4A};
{name:ooxx,method_idx:
1830
,offset:
180516
,code_item_len:
24
,ins:AQABAAEAAAB
+
oAMABAAAAHAQwAsAAA4A};
var base64ptr
=
funcBase64_encode(ptr(codeitemstartaddr), codeitemlength, ptr(base64lengthptr));
var b64content
=
ptr(base64ptr).readCString(base64lengthptr.readInt());
funcFreeptr(ptr(base64ptr));
var content
=
"{name:ooxx,method_idx:"
+
dex_method_index_
+
",offset:"
+
dex_code_item_offset_
+
",code_item_len:"
+
codeitemlength
+
",ins:"
+
b64content
+
"};"
;
var base64ptr
=
funcBase64_encode(ptr(codeitemstartaddr), codeitemlength, ptr(base64lengthptr));
var b64content
=
ptr(base64ptr).readCString(base64lengthptr.readInt());
funcFreeptr(ptr(base64ptr));
var content
=
"{name:ooxx,method_idx:"
+
dex_method_index_
+
",offset:"
+
dex_code_item_offset_
+
",code_item_len:"
+
codeitemlength
+
",ins:"
+
b64content
+
"};"
;
def
main():
dex
=
dex_parser(filename)
if
__name__
=
=
"__main__"
:
init()
methodTable.clear()
parseinsfile()
print
"methodTable length:"
+
str
(
len
(methodTable))
main()
def
main():
dex
=
dex_parser(filename)
if
__name__
=
=
"__main__"
:
init()
methodTable.clear()
parseinsfile()
print
"methodTable length:"
+
str
(
len
(methodTable))
main()
def
parseinsfile():
global
insfilename
insfile
=
open
(insfilename)
content
=
insfile.read()
insfile.close()
insarray
=
re.findall(r
"{name:(.*?),method_idx:(.*?),offset:(.*?),code_item_len:(.*?),ins:(.*?)}"
,content)
for
eachins
in
insarray:
methodname
=
eachins[
0
].replace(
" "
,"")
number
=
(
int
)(eachins[
1
])
offset
=
(
int
)(eachins[
2
])
inssize
=
int
(eachins[
3
])
ins
=
eachins[
4
]
tempmethod
=
CodeItem(number,methodname,inssize,ins)
methodTable[number]
=
tempmethod
def
parseinsfile():
global
insfilename
insfile
=
open
(insfilename)
content
=
insfile.read()
insfile.close()
insarray
=
re.findall(r
"{name:(.*?),method_idx:(.*?),offset:(.*?),code_item_len:(.*?),ins:(.*?)}"
,content)
for
eachins
in
insarray:
methodname
=
eachins[
0
].replace(
" "
,"")
number
=
(
int
)(eachins[
1
])
offset
=
(
int
)(eachins[
2
])
inssize
=
int
(eachins[
3
])
ins
=
eachins[
4
]
tempmethod
=
CodeItem(number,methodname,inssize,ins)
methodTable[number]
=
tempmethod
class
dex_parser:
def
__init__(
self
,filename):
global
DEX_MAGIC
global
DEX_OPT_MAGIC
self
.m_javaobject_id
=
0
self
.m_filename
=
filename
self
.m_fd
=
open
(filename,
"rb"
)
self
.m_content
=
self
.m_fd.read()
self
.m_fd.close()
self
.m_dex_optheader
=
None
self
.m_class_name_id
=
{}
self
.string_table
=
[]
if
self
.m_content[
0
:
4
]
=
=
DEX_OPT_MAGIC:
self
.init_optheader(
self
.m_content)
self
.init_header(
self
.m_content,
0x40
)
elif
self
.m_content[
0
:
4
]
=
=
DEX_MAGIC:
self
.init_header(
self
.m_content,
0
)
bOffset
=
self
.m_stringIdsOff
if
self
.m_stringIdsSize >
0
:
for
i
in
xrange
(
0
,
self
.m_stringIdsSize):
offset,
=
struct.unpack_from(
"I"
,
self
.m_content,bOffset
+
i
*
4
)
if
i
=
=
0
:
start
=
offset
else
:
skip, length
=
get_uleb128(
self
.m_content[start:start
+
5
])
self
.string_table.append(
self
.m_content[start
+
skip:offset
-
1
])
start
=
offset
for
i
in
xrange
(start,
len
(
self
.m_content)):
if
self
.m_content[i]
=
=
chr
(
0
):
self
.string_table.append(
self
.m_content[start
+
1
:i])
break
for
i
in
xrange
(
0
,
self
.m_classDefSize):
str1
=
self
.getclassname(i)
self
.m_class_name_id[str1]
=
i
for
i
in
xrange
(
0
,
self
.m_classDefSize):
str1
=
self
.getclassname(i)
dex_class(
self
,i).printf(
self
)
pass
class
dex_parser:
def
__init__(
self
,filename):
global
DEX_MAGIC
global
DEX_OPT_MAGIC
self
.m_javaobject_id
=
0
self
.m_filename
=
filename
self
.m_fd
=
open
(filename,
"rb"
)
self
.m_content
=
self
.m_fd.read()
self
.m_fd.close()
self
.m_dex_optheader
=
None
self
.m_class_name_id
=
{}
self
.string_table
=
[]
if
self
.m_content[
0
:
4
]
=
=
DEX_OPT_MAGIC:
self
.init_optheader(
self
.m_content)
self
.init_header(
self
.m_content,
0x40
)
elif
self
.m_content[
0
:
4
]
=
=
DEX_MAGIC:
self
.init_header(
self
.m_content,
0
)
bOffset
=
self
.m_stringIdsOff
if
self
.m_stringIdsSize >
0
:
for
i
in
xrange
(
0
,
self
.m_stringIdsSize):
offset,
=
struct.unpack_from(
"I"
,
self
.m_content,bOffset
+
i
*
4
)
if
i
=
=
0
:
start
=
offset
else
:
skip, length
=
get_uleb128(
self
.m_content[start:start
+
5
])
self
.string_table.append(
self
.m_content[start
+
skip:offset
-
1
])
start
=
offset
for
i
in
xrange
(start,
len
(
self
.m_content)):
if
self
.m_content[i]
=
=
chr
(
0
):
self
.string_table.append(
self
.m_content[start
+
1
:i])
break
for
i
in
xrange
(
0
,
self
.m_classDefSize):
str1
=
self
.getclassname(i)
self
.m_class_name_id[str1]
=
i
for
i
in
xrange
(
0
,
self
.m_classDefSize):
str1
=
self
.getclassname(i)
dex_class(
self
,i).printf(
self
)
pass
def
varint_encode(number):
buf
=
b''
while
True
:
towrite
=
number &
0x7f
number >>
=
7
if
number:
buf
+
=
struct.pack(
"B"
,(towrite |
0x80
))
else
:
buf
+
=
struct.pack(
"B"
,towrite)
break
return
buf
def
varint_decode(buff):
shift
=
0
result
=
0
idx
=
0
while
True
:
if
idx>
len
(buff):
return
""
i
=
buff[idx]
idx
+
=
1
result |
=
(i &
0x7f
) << shift
shift
+
=
7
if
not
(i &
0x80
):
break
return
result
def
varint_encode(number):
buf
=
b''
while
True
:
towrite
=
number &
0x7f
number >>
=
7
if
number:
buf
+
=
struct.pack(
"B"
,(towrite |
0x80
))
else
:
buf
+
=
struct.pack(
"B"
,towrite)
break
return
buf
def
varint_decode(buff):
shift
=
0
result
=
0
idx
=
0
while
True
:
if
idx>
len
(buff):
return
""
i
=
buff[idx]
idx
+
=
1
result |
=
(i &
0x7f
) << shift
shift
+
=
7
if
not
(i &
0x80
):
break
return
result
class
dex_class:
def
__init__(
self
,dex_object,classid):
if
classid >
=
dex_object.m_classDefSize:
return
""
offset
=
dex_object.m_classDefOffset
+
classid
*
struct.calcsize(
"8I"
)
self
.offset
=
offset
format
=
"I"
self
.thisClass,
=
struct.unpack_from(
format
,dex_object.m_content,offset)
offset
+
=
struct.calcsize(
format
)
self
.modifiers,
=
struct.unpack_from(
format
,dex_object.m_content,offset)
offset
+
=
struct.calcsize(
format
)
self
.superClass,
=
struct.unpack_from(
format
,dex_object.m_content,offset)
offset
+
=
struct.calcsize(
format
)
self
.interfacesOff,
=
struct.unpack_from(
format
,dex_object.m_content,offset)
offset
+
=
struct.calcsize(
format
)
self
.sourceFileIdx,
=
struct.unpack_from(
format
,dex_object.m_content,offset)
offset
+
=
struct.calcsize(
format
)
self
.annotationsOff,
=
struct.unpack_from(
format
,dex_object.m_content,offset)
offset
+
=
struct.calcsize(
format
)
self
.classDataOff,
=
struct.unpack_from(
format
,dex_object.m_content,offset)
offset
+
=
struct.calcsize(
format
)
self
.staticValuesOff,
=
struct.unpack_from(
format
,dex_object.m_content,offset)
offset
+
=
struct.calcsize(
format
)
self
.index
=
classid
self
.interfacesSize
=
0
if
self
.interfacesOff !
=
0
:
self
.interfacesSize,
=
struct.unpack_from(
"I"
,dex_object.m_content,
self
.interfacesOff)
if
self
.classDataOff !
=
0
:
offset
=
self
.classDataOff
count,
self
.numStaticFields
=
get_uleb128(dex_object.m_content[offset:])
offset
+
=
count
count,
self
.numInstanceFields
=
get_uleb128(dex_object.m_content[offset:])
offset
+
=
count
count,
self
.numDirectMethods
=
get_uleb128(dex_object.m_content[offset:])
offset
+
=
count
count,
self
.numVirtualMethods
=
get_uleb128(dex_object.m_content[offset:])
else
:
self
.numStaticFields
=
0
self
.numInstanceFields
=
0
self
.numDirectMethods
=
0
self
.numVirtualMethods
=
0
class
dex_class:
def
__init__(
self
,dex_object,classid):
if
classid >
=
dex_object.m_classDefSize:
return
""
offset
=
dex_object.m_classDefOffset
+
classid
*
struct.calcsize(
"8I"
)
self
.offset
=
offset
format
=
"I"
self
.thisClass,
=
struct.unpack_from(
format
,dex_object.m_content,offset)
offset
+
=
struct.calcsize(
format
)
self
.modifiers,
=
struct.unpack_from(
format
,dex_object.m_content,offset)
offset
+
=
struct.calcsize(
format
)
self
.superClass,
=
struct.unpack_from(
format
,dex_object.m_content,offset)
offset
+
=
struct.calcsize(
format
)
self
.interfacesOff,
=
struct.unpack_from(
format
,dex_object.m_content,offset)
offset
+
=
struct.calcsize(
format
)
self
.sourceFileIdx,
=
struct.unpack_from(
format
,dex_object.m_content,offset)
offset
+
=
struct.calcsize(
format
)
self
.annotationsOff,
=
struct.unpack_from(
format
,dex_object.m_content,offset)
offset
+
=
struct.calcsize(
format
)
self
.classDataOff,
=
struct.unpack_from(
format
,dex_object.m_content,offset)
offset
+
=
struct.calcsize(
format
)
self
.staticValuesOff,
=
struct.unpack_from(
format
,dex_object.m_content,offset)
offset
+
=
struct.calcsize(
format
)
self
.index
=
classid
self
.interfacesSize
=
0
if
self
.interfacesOff !
=
0
:
self
.interfacesSize,
=
struct.unpack_from(
"I"
,dex_object.m_content,
self
.interfacesOff)
if
self
.classDataOff !
=
0
:
offset
=
self
.classDataOff
count,
self
.numStaticFields
=
get_uleb128(dex_object.m_content[offset:])
offset
+
=
count
count,
self
.numInstanceFields
=
get_uleb128(dex_object.m_content[offset:])
offset
+
=
count
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2021-7-16 09:22
被misskings编辑
,原因: 修改细节