《Android软件安全权威指南》(android文件格式)阅读笔记

  • Post author:
  • Post category:其他




android文件格式



classes.dex



dex文件结构

在这里插入图片描述

无符号LEB128实现如下:

在这里插入图片描述

DEX文件由多个结构体组合而成。如下图,要给DEX由7个部分组成:dex header为Dex文件头,它指定了DEX文件的一些属性并记录了其他数据结构在DEX文件中的物理偏移。string_ids到class_def部分可以理解为“索引结构区”;真实的数据放在data数据区中;link_data为静态链接数据区。

在这里插入图片描述

Dex由DexFile结构体表示,定义如下:

在这里插入图片描述

DexOptHeader是ODEX头,DexHeader是DEX文件头部信息。

在这里插入图片描述

  • magic字段表示DEX文件,固定值为”“64 65 78 0a 30 33 35 00″即”“dex.035”。
  • chesum是校验和,用来判断dex是否被篡改。
  • signature用于识别未经dexopt优化的dex文件。
  • filesize字段记录包括Dexheader在内的整个DEX文件大小。
  • headerSize字段记录DexHeader结构体占用的字节数,值为0x70。
  • endiantag指定DEX运行的CPU字节序,预设值是ENDIAN_CONSTANT,等于0x12345678。
  • linkSize和linkoff字段分别指定链接段大小,文件偏移。
  • mapOff指定DexMapList结构的文件偏移。
  • 后面几个为一些数据的大小和偏移。

DexHeader结构下面的数据是索引结构区和数据区。各数据结构和偏移地址由DexHeader结构stringIdsOff~classDefOff指定。并非真正的类数据,而是指向DEX文件的data数据区的偏移量或数据结构索引。

Dalvik虚拟机解析DEX文件内容,最终映射成DexMapList数据结构。DexHeader结构的mapOff字段指明DexMapList结构在DEX文件中的偏移。

struct DexMapList{
    u4 size;            /*DexMapItem结构个数*/
    DexMapItem list[1]; /*DexMapItem结构*/
}

size表示接下来有多少个DexMapItem结构。此结构如下:

在这里插入图片描述

type字段是一个枚举常量。

在这里插入图片描述

size为指定类型个数,offset为偏移。就是从offset开始,有size个type。

在这里插入图片描述

比如这里类型是kDexTypeHeaderItem就代表整个DexHeader头,占据了0x70空间,从0开始。

紧接着的是字符串类型,就是KDEXTypeStringIdItem对应于DexHeader的stringIdsSize与stringIdsOff字段。表示从0x70开始连续0x10个DexStringId对象。

这里的字符串是MUTF-8编码表示。经过修改的UTF-8,有以下区别:

MUTF-8使用1~3字节编码长度。

对于大于16位的Unicode编码U+0x10000~U+0x10fffff,使用3字节来编码。

对于U+0x0000,采用2字节编码。

才有类似C语言中的空字符null作为结尾。

3. 接下来是KDexTypeTypeIdItem,对应于DexHeader中的typeIdsSize与typeIdsOff字段,指向的结构体为DexTypeId。声明如下:

在这里插入图片描述

descriptorIdx指向DexString列表索引,所对应的字符串代表了具体类的类型。

  1. 然后是kDexTypeProtoIdItem,对应于DexHeader中的protoIdsSize和protoIdsOff字段。指向结构体为DexProtoID,指向结构体是:

    在这里插入图片描述

    DexProtoId是一个方法声明结构体,shortyIdx为方法声明字符串,ReturnTypeIdx为方法返回类型字符串。parametersOff指向一个DexTypeList结构体,存放了方法参数列表。

    DexTypeList结构体如下:

    在这里插入图片描述
  2. 接下来KDexTypeFieldIdItem。对应DexHeader中fieldIdsSize与fieldIdsOff字段,指向结构体如下:

    在这里插入图片描述

DexFieldId结构体中的数据全部都是索引值,指明字段所在的类

在这里插入图片描述

6. 接下来是KDexTypeMethodIdItem,它对应于DexHeader中MethodIdsSize与methodIdsOff,指向DexFieldId,如下:

在这里插入图片描述

DexMethodId结构中数据也是索引,指明了方法所在的类,方法声明,方法名。

7. 接下来kDexTypeClassDefItem,对应DexHeader的classDefsSize与classDefsOff字段,指向的结构体为DexClassDef,如下:

在这里插入图片描述

DexClassDef。classIdx是一个索引值,表示类的类型。accessFlags字段是类的访问标志,是一个以ACC_开头的枚举值。superclassIdx字段是父类类型索引值,如果类中有含有接口声明或实现,interfacesOff会指向一个DexTypeList结构,否则这里是0.sourceFileIdx字段是字符串索引值,表示所在类源文件的名称。annotationsOff字段指向注释目录结构,根据类型不同,有注释类,注释方法,注释字段,注解参数,如果类中没有注释,字段值为0.classDataOff字段指向DexClassData结构,是类的数据部分。staticValueOff字段指向DexEncodedArray结构,记录类的静态数据。

DexClassData结构如下:

在这里插入图片描述

DexClassDataHeader结构记录了当前类中字段和方法数目,如下:

在这里插入图片描述

DexClassDataHeader就够和DexClassData都是在DexClass.h声明。

DexField结构描述了字段的类型与访问标志,如下:

在这里插入图片描述

fieldIdx字段为指向DexFieldId的索引,AccessFlags与DexClassDef中相应的字段类型相同。

DexMethod描述方法的原型,名称访问标志,及代码数据块。

在这里插入图片描述

methodIdx字段指向DexMethodId的索引,accessFlags字段为访问标志,codeOff字段指向一个DexCode结构体,描述方法详细信息及方法中指令内容,如下:

在这里插入图片描述

registersize指定方法寄存器个数。insSize字段指定方法的参数个数。

insSize字段指定了方法的参数个数,对应smali的.paramter指令。outsSize字段指定了方法在调用外部方法是使用的寄存器个数。

insnsSize字段为代码部分。

在这里插入图片描述



DEX文件验证优化过程

因为同一台虚拟机验证失败会导致部分资源(如加载的Native动态库)难以从内存中释放,Android提供一个专门验证与优化DEX文件的工具dexopt,源码位于dalvik/dexopt目录下,Dalvik虚拟机加载DEX文件时,指定验证优化选项来调用dexopt进行验证与优化。

dexpot的主程序代码为OptMain.cpp,其中处理apk/jar/zip中的classes.dex文件函数是extratAndProcessZip()。通过dexZipFindEntry()检查是否有classes.dex。dexZipGetEntryInfo()读时间戳和CRC校验值。->dexZipExtractEntryToFile()函数释放class.dex缓存文件。解析过来的优化选项。调用dvmPrepForDexOpt()函数,启动虚拟机。初始化完成,调用dvmContinueOptimization(),开始真正验证优化。



AndroidManifest.xml



AXML文件格式

AXML文件由文件头ResFileHeader,字符串池ResStringPool,资源ID块ResIDs,XML数据内容块ReXMLTree组成。

ResFileheader表示文件头部,用ResChunk_header表示,除了在文件开头表示文件头,还用于表示其他chunk头部信息。

structuralResChunk_header
{
    uint16_t type;
    uint16_t headerSize;
    uint32_t size;
}

在这里插入图片描述

type为chunk所属结构体类型。

文件头8字节,filesize包含当前头结构体8字节。

文件头之后字符串池ResStringPool,包含AXML使用的字符串。由字符串头,字符串列表,样式列表。

接着是资源ID块ResIDs,存放AndroidManifest.xml使用系统属性值所对应的资源ID。

最好是ResXMLTree,表示Xml文件具体内容。



resources.arsc

这是一个包含不同语言环境中res目录下所有资源的类型,名称,ID所对应的信息,格式为ARSC。

结构从整体看,由ResTableHeader,资源项值字符串池ResStringPool,Package数据内容块ResTablePackage三部分组成。

在这里插入图片描述



META-INF目录

储存一些与APK签名有关的信息。

CERT.RSA存放apk开发者证书与签名信息。

MANIFEST.MF是签名的清单文件。



ODEX

为了提高DEX文件执行效率,将DEX文件生成ODEX。

在这里插入图片描述

比起DEX增加了:

DexOptHeader:ODEX文件头,描述ODEX基本信息。

Dependences:依赖库列表。

classLookups:优化数据块的类索引列表信息。

RegisterMaps:优化数据块寄存器图信息。用于帮助Dalvik虚拟机进行精确垃圾回收。



OAT

ART使用AOT(ahead-of-Time)编译技术,将apk在第一次安装时候,使用dex2oat将dex的dalvik编译成native机器码。为了提高安装速率又引入了JIT编译。



OAT文件格式

androi在ELF基础上,增加了属于Android应用程序二进制接口。

一个oat文件必须包含oatdata,oatexec,oatlastword三个符号。oatdata符号指向的地址是OAT所在elf的.rodata段,存访OAT文件头OATHeader,OAT的DEX文件头OATDexFile,原始的EDX文件DexFile,OAT的DEX类OatClass等信息。

oatexec符号指向text段,存放编译产生的Native指令代码。

oatlastword符号指向OAT文件结束出在elf文件中的偏移。确定OAT文件在哪结束。

在这里插入图片描述

OATHeader被定义成了C++类。

在这里插入图片描述

在这里插入图片描述



版权声明:本文为youyou519原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。