应用资源文件格式解析及阿里破解示例解析.docx
《应用资源文件格式解析及阿里破解示例解析.docx》由会员分享,可在线阅读,更多相关《应用资源文件格式解析及阿里破解示例解析.docx(21页珍藏版)》请在冰点文库上搜索。
应用资源文件格式解析及阿里破解示例解析
应用资源文件格式解析
及阿里破解示例
0x01前言:
目前大部份公司如阿里,腾讯,360等对apk资源保护的方法是通过自动化测试工具寻找一些异常的数据格式,安卓虚拟机及aapt对于这些异常的格式可以正常处理,而apktool工具处理分析时会出现异常。
找到这些异常的数据格式后,利用改装后的aapt工具,对apk打包时设置这些异常的格式,从而达到保护的作用。
本文主要介绍应用资源文件的格式,分析阿里的APK资源保护机制,给出了破解方法。
0x02arsc文件格式简介
我们知道一个APK文件结构为:
mete-INF存放签名文件的目录。
res存放了二进制编译XML及图片资源文件的目录
AndroidManifest.xml程序全局配置文件
classes.dexDalvik字节码
resources.arsc编译后的二进制资源文件
Resources.arsc文件包含了二进制编译的String,style等资源。
Resources.arsc文件采用小端编码方式(即低位在前,高位在后),Resources.arsc文件整体是由一系列的chunk构成。
Resources.arsc文件的整体可以用以下的这张图来概述。
在一个apk中,对应chunck的数量一般有:
ResTable(1个)
ResStringPool(1个)
ResTable_Package(1~n个)
RES_TABLE_TYPE_SPEC(1~n个)
RES_TABLE_TYPE(1~n个)。
我们也可以通过模版来分析Resources.arsc文件,这样更直接一点。
我们用010edit(010edit的下载地址见参考资料[4])打开Resources.arsc文件
在template菜单->open打开ARSCTemplate.bt模版(下载地址见参考资料[5]),点击template菜单->Run即可看到Resources.arsc文件的相关结构。
具体示意如下图
下面我们以helloworld.apk做为实例,帮助大家理解一下Resources.arsc文件的结构。
helloworld.apk大家可以通过参考资料[6]所标注的地址下载。
Resources.arsc整体是由一系列的chunk构成。
每一个chunk均包含如下结构的ResChunk_header,用来描述这个chunk的基本信息。
struct ResChunk_header {
enum {
RES_NULL_TYPE = 0x0000,
RES_STRING_POOL_TYPE = 0x0001,
RES_TABLE_TYPE = 0x0002,
RES_XML_TYPE = 0x0003,
RES_XML_FIRST_CHUNK_TYPE = 0x0100,
RES_XML_START_NAMESPACE_TYPE= 0x0100,
RES_XML_END_NAMESPACE_TYPE = 0x0101,
RES_XML_START_ELEMENT_TYPE = 0x0102,
RES_XML_END_ELEMENT_TYPE = 0x0103,
RES_XML_CDATA_TYPE = 0x0104,
RES_XML_LAST_CHUNK_TYPE = 0x017f,
RES_XML_RESOURCE_MAP_TYPE = 0x0180,
RES_TABLE_PACKAGE_TYPE = 0x0200,
RES_TABLE_TYPE_TYPE = 0x0201,
RES_TABLE_TYPE_SPEC_TYPE = 0x0202 };
//当前这个chunk的类型
uint16_t type;
//当前这个chunk的头部大小
uint16_t headerSize;
//当前这个chunk的大小
uint32_t size;
};
1.ResTable_header(资源索引表头)
Resources.arsc文件的第一个结构是资源索引表头。
其结构如下,描述了Resources.arsc文件的大小和资源包数量。
struct ResTable_header {
struct ResChunk_header header;
uint32_t packageCount; //被编译的资源包的个数
};
下图蓝色加亮部分标出亮helloworld.apk的Resources.arsc文件的资源索引表头的内容。
ResTable_header的chunk(02000C006895010001000000)
0200位置0~1,共2字节,表示该chunk的类型,值为0x0002表示类型为RES_TABLE_TYPE.
0C00位置2~3,共2字节,表示该chunk类型的头长度,
值为0x000C表示该类型的头长度为12字节长度。
68950100位置4~7,共4字节,表示该chunk的总长度
,值为0x00019568表示该chunk的总长度(这里为整个文件的大小)为103784字节长度。
01000000被编译的资源包的个数,这里只有一个。
2.ResStringPool_header(全局字符串资源)
紧跟着资源索引表头部的是资源项的全局字符串资源,这个字符串资源池包含了所有的在资源包里面所定义的资源项的全局字符串,包括android工程中部分资源文件名(如res/drawable-hdpi/ic_launcher.png,res/layout/activity_main.xml等)及res/values/strings.xml中的字符串值(如helloworld,helloworld,Settings)
全局字符串资源头部的结构如下。
struct ResStringPool_header {
struct ResChunk_header header;
uint32_t stringCount; //字符串的数量
uint32_t styleCount; //字符串样式的数量
uint32_t flags; //字符串的属性,可取值包括0x000(UTF-16),0x001(字符串经过排序)、0X100(UTF-8)和他们的组合值
uint32_t stringsStart;//字符串内容块相对于其头部的距离
uint32_t stylesStart;//字符串样式块相对于其头部的距离
};
0100位置0~1,共2字节,表示该chunk的类型,值为0x0001表示类型为RES_STRING_POOL_TYPE.
1C00位置2~3,共2字节,表示该chunk类型的头长度,值为0x001C表示该类型的头长度为28字节长度。
A4910000位置4~7,共4字节,表示该chunk的总长度,值
为0x000091A4表示该chunk的总长度为37284字节长度。
E1030000位置0x08~0x0B,共4字节,表示字符串数量,
值为0x000003E1表示字符串数量为993个。
00000000位置0x0C~0x0F,共4字节,表示字符串样式数量,值
为0x00000000表示字符串样式数量为0个。
00010000位置0x10~0x13字符串的属性,共4字节,值
为0x00000100表示字符串采用utf-8编码。
A00F00000x14~0x17共4字节,值为0x00000FA0表示字符串内容相对于RES_STRING_POOL头部的偏移为4000。
实际的文件偏移地址为0x00000FA0+0x0C=0x0FAC。
000000000x18~0x1B共4字节,值为0表示字符串样式内容相对于RES_STRING_POOL头部的偏移为0。
3.ResTable_package结构说明
struct ResTable_package
{
struct ResChunk_header header;
//包的ID,等于Package Id,一般用户包的值Package Id为0X7F,
系统资源包的Package Id为0X01。
uint32_t id;
//包名称
char16_t name[128];
//类型字符串资源池相对头部的偏移
uint32_t typeStrings;
//最后一个导出的Public类型字符串在类型字符串资源池中的索引,目前这个值设置为类型字符串资源池的元素个数。
uint32_t lastPublicType;
//资源项名称字符串相对头部的偏移
uint32_t keyStrings;
//最后一个导出的Public资源项名称字符串在资源项名称字符串资源池中的索引,目前这个值设置为资源项名称字符串资源池的元素个数。
uint32_t lastPublicKey;
};
typeStrings资源类型字符串存储类型资源的类型字符串
一般有animator、anim、color、drawable、layout、menu、raw、string和xml。
keyStrings(资源项名称字符串)一般储存资源文件XML内容的键名的名称字符串,以helloworld的strings.xml为例,
键名“app_name”,”hello_world”,“action_settings”做为资源项名称字符串存储在该区域。
ResTable_package包头解析
00021C01B8030100为ResTable_package的头ResChunk_header。
7F000000值0x0000007f为包的ID
6300…位置0x91bc~0x92bb为包名
com.example.helloworld
1C010000位置0x9bc~0x92bf包的typeStrings
(类型字符串资源池相对头部的偏移)
0C000000typeStrings(类型字符串资源池)的字符串个数,
0x00000c个
C8010000keyStrings(资源项名称字符串)相对头部的偏移,在文件中的起始位置为0x01C8+0x91b0=0x9378
E0010000keyStrings(资源项名称字符串资源池)的字符串元素个数。
ResTable_package包后是typeString和keyStrings的内容。
位置0x92CC~0x9377为typeString的内容
typeString的格式与RES_STRING_POOL_TYPE一致。
0100位置0~1,共2字节,表示该chunk的类型,值为0x0001表示类型为RES_STRING_POOL_TYPE.
1C00位置2~3,共2字节,表示该chunk类型的头长度,值为0x001C表示该类型的头长度为28字节长度。
Ac000000位置4~7,共4字节,表示该chunk的总长度,值
为0x000000ac表示该chunk的总长度为172字节长度。
0c000000位置0x08~0x0B,共4字节,表示字符串数量,
值为0x0000000c表示字符串数量为12个。
00000000位置0x0C~0x0F,共4字节,表示字符串样式数量,值
为0x00000000表示字符串样式数量为0个。
00010000位置0x10~0x13字符串的属性,共4字节,值
为0x00000100表示字符串采用utf-8编码。
4c0000000x14~0x17共4字节,值为0x0000004c表示字符串内容相对于RES_STRING_POOL头部的偏移为72。
实际的文件偏移地址为0x0000004c+0x92cc=0x9318。
000000000x18~0x1B共4字节,值为0表示字符串样式内容相对于RES_STRING_POOL头部的偏移为0。
typeString后是12个偏移数组,每个数组元素都记录了typeString字符串数组的相对偏移位置(即相对于0x9318的位置)。
Offset[0]=0x0000;
Offset[1]=0x0007;
Offset[2]=0x0012;
Offset[3]=0x001B;
Offset[4]=0x0022;
Offset[5]=0x0027;
Offset[6]=0x002e;
Offset[7]=0x0036;
Offset[8]=0x003e;
Offset[9]=0x0048;
Offset[10]=0x0051;
Offset[11]=0x0059;
偏移数组后面是typeString的字符串的数组内容。
typeString[0]=”attr”
typeString[1]=”drawable”
typeString[2]=”layout”
typeString[3]=”anim”
typeString[4]=”id”
typeString[5]=”bool”
typeString[6]=”color”
typeString[7]=”dimen”
typeString[8]=”integer”
typeString[9]=”string”
typeString[10]=”style”
typeString[11]=”menu”
位置0x9378~0xd617为keyString的内容,keyString的格式与RES_STRING_POOL_TYPE一致。
这里不再介绍。
4.ResTable_typeSpec结构说明
struct ResTable_typeSpec
{
struct ResChunk_header header;
//标识资源的Type ID,Type ID是指资源的类型ID。
资源的类型有animator、anim、color、drawable、layout、menu、raw、string和xml等等若干种,每一种都会被赋予一个ID。
uint8_t id;
//保留,始终为0
uint8_t res0;
//保留,始终为0
uint16_t res1;
//等于本类型的资源项个数,指名称相同的资源项的个数。
uint32_t entryCount;
};
下图蓝色加亮部分为helloworld.apk的第一个ResTable_typeSpec内容
02021000C8010000为ResTable_typeSpec的头ResChunk_header。
01id,是指资源的类型ID,对照的typeString的字符串值
,我们可以知道该ID的指是attr
00res0
0000res1
0000006EentryCount,共有0X6E个相同的名称类型ID。
ResTable_typeSpec后面紧跟着的是一个大小为entryCount的uint32_t数组
文件位置0xd628~0xd7df为entry数组。
5.ResTable_type结构说明
struct ResTable_type
{
struct ResChunk_header header;
enum {
NO_ENTRY = 0xFFFFFFFF
};
//标识资源的Type ID
uint8_t id;
//保留,始终为0
uint8_t res0;
//保留,始终为0
uint16_t res1;
//等于本类型的资源项个数,指名称相同的资源项的个数。
uint32_t entryCount;
//等于资源项数据块相对头部的偏移值。
uint32_t entriesStart;
//指向一个ResTable_config,用来描述配置信息,地区,语言,分辨率等
ResTable_config config;
};
下图蓝色加亮部分为helloworld.apk的第一个ResTable_type内容
01023800E80E0000ResTable_type的ResChunk_header
01标识资源的Type ID0x7f010000
00res0
0000res1
6E000000 等于本类型的资源项个数,指名称相同的资源项的
个数。
F0010000ResTable_entry数据的起始位置0xD7E0+0x01F0=0xD9D0
6.ResTable_entry结构说明
struct ResTable_entry
{
//表示资源项头部大小。
uint16_t size;
enum {
//如果flags此位为1,则ResTable_entry后跟随ResTable_map数组,为0则跟随一个Res_value。
FLAG_COMPLEX = 0x0001,
//如果此位为1,这个一个被引用的资源项
FLAG_PUBLIC = 0x0002
};
//资源项标志位
uint16_t flags;
//资源项名称在资源项名称字符串资源池的索引
struct ResStringPool_ref key;
};
structResStringPool_ref
{
uint32_tident;
};
下图蓝色加亮部分为的ResTable_entry
1000长度
0100如果flags此位为1,则ResTable_entry后跟随ResTable_map数组
00000000key资源项名称在资源项名称字符串资源池的索引
“windowActionBar”
7.ResTable_map_entry结构说明
struct ResTable_map_entry :
public ResTable_entry
{
//指向父ResTable_map_entry的资源ID,如果没有父ResTable_map_entry,则等于0。
ResTable_ref parent;
//等于后面ResTable_map的数量
uint32_t count;
};
ResTable_map的结构如下:
struct ResTable_map
{
//bag资源项ID
ResTable_ref name;
//bag资源项值
Res_value value;
};
struct Res_value
{
//Res_value头部大小
uint16_t size;
//保留,始终为0
uint8_t res0;
enum {
TYPE_NULL = 0x00,
TYPE_REFERENCE = 0x01,
TYPE_ATTRIBUTE = 0x02,
TYPE_STRING = 0x03,
TYPE_FLOAT = 0x04,
TYPE_DIMENSION = 0x05,
TYPE_FRACTION = 0x06,
TYPE_FIRST_INT = 0x10,
TYPE_INT_DEC = 0x10,
TYPE_INT_HEX = 0x11,
TYPE_INT_BOOLEAN = 0x12,
TYPE_FIRST_COLOR_INT = 0x1c,
TYPE_INT_COLOR_ARGB8 = 0x1c,
TYPE_INT_COLOR_ARGB8 = 0x1c,
TYPE_INT_COLOR_RGB8 = 0x1d,
TYPE_INT_COLOR_ARGB4 = 0x1e,
TYPE_INT_COLOR_RGB4 = 0x1f,
TYPE_LAST_COLOR_INT = 0x1f,
TYPE_LAST_INT = 0x1f
};
//数据的类型,可以从上面的枚举类型中获取
uint8_t dataType;
//数据对应的索引
uint32_t data;
};
下图为ResTable_map_entry的举例,本例中0xd9d8~0xd9ec为ResTable_map_entry的数据。
00000000如果没有父ResTable_map_entry,则等于0
01000000后面ResTable_map的数量,这里为1个。
当count不为0时,ResTable_map_entry其后跟随则count个ResTable_map类型的数组。
00000001ResTable_ref 的name;
0800长度
00res0;
10TYPE_INT_DEC
08000000值为8
我们打开helloworld源码工程生成的R.java可以发现windowActionBar确实编码为0x7f010000,值为8,表示隐藏。
为了完整地介绍ResTable_entry,我们最后列出,flags最后一位为0时的ResTable_entry的例子。
8000