一个HelloWord的resources.arsc分析
参考资料:http://blog.csdn.net/luoshengyang/article/details/8744683
android4.4,aapt部分源代码
apktool源代码
首先对于逆向来说几个重要的常量定义,定义在frameworks\base\include\androidfw\ResourceTypes.h
1 enum { 2 RES_NULL_TYPE = 0x0000, 3 RES_STRING_POOL_TYPE = 0x0001, 4 RES_TABLE_TYPE = 0x0002, 5 RES_XML_TYPE = 0x0003, 6 7 // Chunk types in RES_XML_TYPE 8 RES_XML_FIRST_CHUNK_TYPE = 0x0100, 9 RES_XML_START_NAMESPACE_TYPE= 0x0100, 10 RES_XML_END_NAMESPACE_TYPE = 0x0101, 11 RES_XML_START_ELEMENT_TYPE = 0x0102, 12 RES_XML_END_ELEMENT_TYPE = 0x0103, 13 RES_XML_CDATA_TYPE = 0x0104, 14 RES_XML_LAST_CHUNK_TYPE = 0x017f, 15 // This contains a uint32_t array mapping strings in the string 16 // pool back to resource identifiers. It is optional. 17 RES_XML_RESOURCE_MAP_TYPE = 0x0180, 18 19 // Chunk types in RES_TABLE_TYPE 20 RES_TABLE_PACKAGE_TYPE = 0x0200, 21 RES_TABLE_TYPE_TYPE = 0x0201, 22 RES_TABLE_TYPE_SPEC_TYPE = 0x0202 23 };
resources.arsc结构:
资源索引表头部+字符串资源池+N个Package数据块,这种结构其实和swf结构类似
每一结构中都有一个头部,头部的结构的第一个字段都是ResChunk_header,定义在frameworks\base\include\androidfw\ResourceTypes.h
1 struct ResChunk_header 2 { 3 // Type identifier for this chunk. The meaning of this value depends 4 // on the containing chunk. 5 uint16_t type; 6 7 // Size of the chunk header (in bytes). Adding this value to 8 // the address of the chunk allows you to find its associated data 9 // (if any). 10 uint16_t headerSize; 11 12 // Total size of this chunk (in bytes). This is the chunkSize plus 13 // the size of any data associated with the chunk. Adding this value 14 // to the chunk allows you to completely skip its contents (including 15 // any child chunks). If this value is the same as chunkSize, there is 16 // no data associated with the chunk. 17 uint32_t size; 18 };
资源索引表头部:
02 00 0C 00 98 08 00 00 01 00 00 00
对应的结构是 ResTable_header
1 struct ResTable_header 2 { 3 struct ResChunk_header header; 4 5 // The number of ResTable_package structures. 6 uint32_t packageCount; 7 };
type值为 RES_TABLE_TYPE,头部大小为0xc,整个TABLE大小是0x0898,也就是整个文件的大小,packageCount=1,表示只有一个packet,也就是我的helloword资源包,包名为com.example.test
字符串资源池:
整个资源池的16进制如下,这些字符串资源是所有packet共用的
0000h: 01 00 1C 00 28 01 00 00 09 00 00 00 00 00 00 00 ....(...........
0010h: 00 01 00 00 40 00 00 00 00 00 00 00 00 00 00 00 ....@...........
0020h: 1F 00 00 00 33 00 00 00 57 00 00 00 7B 00 00 00 ....3...W...{...
0030h: A0 00 00 00 C6 00 00 00 D1 00 00 00 E0 00 00 00 ...Æ...Ñ...à...
0040h: 1C 1C 72 65 73 2F 6C 61 79 6F 75 74 2F 61 63 74 ..res/layout/act
0050h: 69 76 69 74 79 5F 6D 61 69 6E 2E 78 6D 6C 00 11 ivity_main.xml..
0060h: 11 72 65 73 2F 6D 65 6E 75 2F 6D 61 69 6E 2E 78 .res/menu/main.x
0070h: 6D 6C 00 21 21 72 65 73 2F 64 72 61 77 61 62 6C ml.!!res/drawabl
0080h: 65 2D 6D 64 70 69 2F 69 63 5F 6C 61 75 6E 63 68 e-mdpi/ic_launch
0090h: 65 72 2E 70 6E 67 00 21 21 72 65 73 2F 64 72 61 er.png.!!res/dra
00A0h: 77 61 62 6C 65 2D 68 64 70 69 2F 69 63 5F 6C 61 wable-hdpi/ic_la
00B0h: 75 6E 63 68 65 72 2E 70 6E 67 00 22 22 72 65 73 uncher.png.""res
00C0h: 2F 64 72 61 77 61 62 6C 65 2D 78 68 64 70 69 2F /drawable-xhdpi/
00D0h: 69 63 5F 6C 61 75 6E 63 68 65 72 2E 70 6E 67 00 ic_launcher.png.
00E0h: 23 23 72 65 73 2F 64 72 61 77 61 62 6C 65 2D 78 ##res/drawable-x
00F0h: 78 68 64 70 69 2F 69 63 5F 6C 61 75 6E 63 68 65 xhdpi/ic_launche
0100h: 72 2E 70 6E 67 00 08 08 53 65 74 74 69 6E 67 73 r.png...Settings
0110h: 00 0C 0C 48 65 6C 6C 6F 20 77 6F 72 6C 64 21 00 ...Hello world!.
0120h: 04 04 54 65 73 74 00 00 ..Test..
头部 ResStringPool_header
1 struct ResStringPool_header 2 { 3 struct ResChunk_header header; 4 5 // Number of strings in this pool (number of uint32_t indices that follow 6 // in the data). 7 uint32_t stringCount; 8 9 // Number of style span arrays in the pool (number of uint32_t indices 10 // follow the string indices). 11 uint32_t styleCount; 12 13 // Flags. 14 enum { 15 // If set, the string index is sorted by the string values (based 16 // on strcmp16()). 17 SORTED_FLAG = 1<<0, 18 19 // String pool is encoded in UTF-8 20 UTF8_FLAG = 1<<8 21 }; 22 uint32_t flags; 23 24 // Index from header of the string data. 25 uint32_t stringsStart; 26 27 // Index from header of the style data. 28 uint32_t stylesStart; 29 };
16进制数据:
01 00 1C 00 28 01 00 00 09 00 00 00 00 00 00 00
00 01 00 00 40 00 00 00 00 00 00 00
可以看出 stringCount=9 styleCount=0 flags=UTF8_FLAG
header结束后是string offset array、style offset arry,就是2个int型数组,string offset array数每个元素记录着每个字符串相对于StringContent开始的索引
整个字符串资源区用下面的图来看一目了然
Package数据块:
结构如下,图片来自老罗的文章
16进制如下:
0130h: 00 02 1C 01 64 07 00 00 7F 00 00 00 63 00 6F 00 ....d......c.o.
0140h: 6D 00 2E 00 65 00 78 00 61 00 6D 00 70 00 6C 00 m...e.x.a.m.p.l.
0150h: 65 00 2E 00 74 00 65 00 73 00 74 00 00 00 00 00 e...t.e.s.t.....
0160h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0170h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0180h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0190h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01A0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01B0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01C0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01D0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01E0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
01F0h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0200h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0210h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0220h: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0230h: 00 00 00 00 00 00 00 00 00 00 00 00 1C 01 00 00 ................
0240h: 08 00 00 00 98 01 00 00 0A 00 00 00 01 00 1C 00 ....˜...........
0250h: 7C 00 00 00 08 00 00 00 00 00 00 00 00 01 00 00 |...............
0260h: 3C 00 00 00 00 00 00 00 00 00 00 00 07 00 00 00 <...............
0270h: 12 00 00 00 1B 00 00 00 23 00 00 00 2C 00 00 00 ........#...,...
0280h: 34 00 00 00 3B 00 00 00 04 04 61 74 74 72 00 08 4...;.....attr..
0290h: 08 64 72 61 77 61 62 6C 65 00 06 06 6C 61 79 6F .drawable...layo
02A0h: 75 74 00 05 05 64 69 6D 65 6E 00 06 06 73 74 72 ut...dimen...str
02B0h: 69 6E 67 00 05 05 73 74 79 6C 65 00 04 04 6D 65 ing...style...me
02C0h: 6E 75 00 02 02 69 64 00 01 00 1C 00 E8 00 00 00 nu...id.....è...
02D0h: 0A 00 00 00 00 00 00 00 00 01 00 00 44 00 00 00 ............D...
02E0h: 00 00 00 00 00 00 00 00 0E 00 00 00 1E 00 00 00 ................
02F0h: 3B 00 00 00 56 00 00 00 61 00 00 00 6F 00 00 00 ;...V...a...o...
0300h: 81 00 00 00 90 00 00 00 9B 00 00 00 0B 0B 69 63
开头是ResTable_package 结构,结构太简单了,对着数据一看就清楚,就不详述了
struct ResTable_package { struct ResChunk_header header; // If this is a base package, its ID. Package IDs start // at 1 (corresponding to the value of the package bits in a // resource identifier). 0 means this is not a base package. uint32_t id; // Actual name of this package, \0-terminated. char16_t name[128]; // Offset to a ResStringPool_header defining the resource // type symbol table. If zero, this package is inheriting from // another base package (overriding specific values in it). uint32_t typeStrings; // Last index into typeStrings that is for public use by others. uint32_t lastPublicType; // Offset to a ResStringPool_header defining the resource // key symbol table. If zero, this package is inheriting from // another base package (overriding specific values in it). uint32_t keyStrings; // Last index into keyStrings that is for public use by others. uint32_t lastPublicKey; };
Type String Pool 是一个package中用到的类型字符串,理解成这个package用到了哪些类型
Key String Pool是资源项名称字符串,比如 string.xml中的内容如下:
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">Test</string> <string name="hello_world">Hello world!</string> <string name="action_settings">Settings</string> </resources>
那么这个pool就收集了 app_name hello_world action_settings 这3个字符串
下面的Type Specification Trunk 和Type Info Trunk是逆向分析的关键,防止反编译的措施会经常在这里面搞文章。起码楼主遇到了好多个,这次只是分析,熟悉下结构!
Type Specification Trunk 类型规范数据块(名词来自老罗的文章),结构如下
struct ResTable_typeSpec { struct ResChunk_header header; // The type identifier this chunk is holding. Type IDs start // at 1 (corresponding to the value of the type bits in a // resource identifier). 0 is invalid. uint8_t id; // Must be 0. uint8_t res0; // Must be 0. uint16_t res1; // Number of uint32_t entry configuration masks that follow. uint32_t entryCount; enum { // Additional flag indicating an entry is public. SPEC_PUBLIC = 0x40000000 }; };
Type Info Trunk 类型资源数据块
struct ResTable_type { struct ResChunk_header header; enum { NO_ENTRY = 0xFFFFFFFF }; // The type identifier this chunk is holding. Type IDs start // at 1 (corresponding to the value of the type bits in a // resource identifier). 0 is invalid. uint8_t id; // Must be 0. uint8_t res0; // Must be 0. uint16_t res1; // Number of uint32_t entry indices that follow. uint32_t entryCount; // Offset from header where ResTable_entry data starts. uint32_t entriesStart; // Configuration this collection of entries is designed for. ResTable_config config; };
注意:ResTable_typeSpec和ResTable_type的生成规则见下图
对于每个Type Strings里面的每一个类型都对应的生成ResTable_typeSpec和ResTable_type
ResTable_typeSpec是用来描述资源项的配置差异性,描述某一种类型资源下面的资源是否有配置差异性,每一项是这种类型下面的所有同名资源项
ResTable_type是描述某个类型下的某个配置下的所有资源项,每个资源项是这个配置下所有同名的资源,一个具有N个配置的类型就有N个ResTable_type
现在把type string pool的16进制贴出来,对照着里面每一个类型先来研究 ResTable_typeSpec
0250h: 01 00 1C 00 7C 00 00 00 08 00 00 00 00 00 00 00 ....|...........
0260h: 00 01 00 00 3C 00 00 00 00 00 00 00 00 00 00 00 ....<...........
0270h: 07 00 00 00 12 00 00 00 1B 00 00 00 23 00 00 00 ............#...
0280h: 2C 00 00 00 34 00 00 00 3B 00 00 00 04 04 61 74 ,...4...;.....at
0290h: 74 72 00 08 08 64 72 61 77 61 62 6C 65 00 06 06 tr...drawable...
02A0h: 6C 61 79 6F 75 74 00 05 05 64 69 6D 65 6E 00 06 layout...dimen..
02B0h: 06 73 74 72 69 6E 67 00 05 05 73 74 79 6C 65 00 .string...style.
02C0h: 04 04 6D 65 6E 75 00 02 02 69 64 00 ..menu...id.
ResTable_typeSpec的16进制如下:
03B0h: 02 02 10 00 10 00 00 00 01 00 00 00 00 00 00 00 ................
03C0h: 02 02 10 00 14 00 00 00 02 00 00 00 01 00 00 00 ................
03D0h: 00 01 00 00 ....
可以看到有2个 ResTable_typeSpec,第一个entry count 为0,也就是attr资源没有适配
第二个ResTable_typeSpec,描述的是drawable类型的资源,entry count为1,有1个适配项,值为0x100,CONFIG_DENSITY位位为1
这个APK资源中drawable类型的资源,,这个资源有4个配置,名字都叫ic_launcher.png,所以entry count为1
drawable的 ResTable_typeSpec完后是 ResTable_type数组了,因为drawable类型有4个配置,所以后面接4个ResTable_type
struct ResTable_entry { // Number of bytes in this structure. uint16_t size; enum { // If set, this is a complex entry, holding a set of name/value // mappings. It is followed by an array of ResTable_map structures. FLAG_COMPLEX = 0x0001, // If set, this resource has been declared public, so libraries // are allowed to reference it. FLAG_PUBLIC = 0x0002 }; uint16_t flags; // Reference into ResTable_package::keyStrings identifying this entry. struct ResStringPool_ref key; };
再来看ResTable_type 数组,ResTable_type数组是按照每种类型的不同配置来分配的,比如drawable类型,有4种配置,那么就有4个ResTable_type
来看第一个ResTable_type
03D0h: 01 02 38 00 4C 00 00 00 02 00 00 00 01 00 00 00 ..8.L...........
03E0h: 3C 00 00 00
ResTable_type.config 类型 ResTable_config 可以从这个字段来确定配置的一些信息
24 00 00 00 00 00 00 00 00 00 00 00 <...$...........
03F0h: 00 00 A0 00 00 00 00 00 00 00 00 00 04 00 00 00 .. .............
0400h: 00 00 00 00 00 00 00 00 //ResTable_type结束
00 00 00 00 //entry count =1所以 这个4字节数组只有1个 每个4字节是ResTable_entry的偏移
//第一个ResTable_entry 开始 偏移是0
08 00 00 00 ................
//这个4字节是资源项名称索引 在aapt源码中对应的是ResTable_entry.key 结构是ResStringPool_ref ,在apktool源码中对应的名称是specNamesId,从这4个字节中从packet的 key string pool 中得到资源名称
0410h: 00 00 00 00
//因为ResTable_entry.flag=0所以下面是Res_value 结构
08 00 00 03 02 00 00 00 ............
资源ID是2表示在type string pool中第二个字符串,是drawable,
entryCount=1,这个配置的信息从config字段可以获取,这个字段是ResTable_config类型,从这个结构中可以找到配置信息
每个ResTable_type结束后4字节数组,然后是ResTable_entry数组,对照着16进制分析第一个ResTable_entry
ResTable_entry flag=0 key是ResStringPool_ref 结构 其中index=0 ,表示的是这个资源项的名称,查看表是ic_launcher 字符串
紧跟后面的结构是 资源项的数据 由于flag是0表示是是普通的资源项,后面的结构是 Res_value
其中dataType=3 表示的这个资源的值是字符串类型 02是字符串索引,字符串池的第三个位置,查表是
res/drawable-mdpi/ic_launcher.png 字符串
注意:字符串值的索引是从0开始 比如上面的02,这个是查的字符串资源池,因为这些字符串是各个packet共用的
资源type索引是从1开始的,资源项名和资源项值的索引都是从0开始
未解决的问题:
对于ResTable_typeSpec, 中各个4字节的entry对应的资源名怎么获得?我想的是各个entry的名称和package的资源项名字符串池中的字符串对应,经验证应该是对的
apktool代码中解析resources.arsc部分分析笔记:
1、ARSCDecoder中mResId的计算
从packet结构中读取packet的id,mResId = id << 24;,从ResTable_typeSpec中读取type id,mResId = (0xff000000 & mResId) | id << 16;
从ResTable_type 的每个entry对应的索引 mResId = (mResId & 0xffff0000) | i;,从而得到一个具体的资源id,来写入到R.java文件中
2、ResTable_typeSpec对应apktool中的结构是ResType ResTable_type 对应的是ResConfig