随笔-1  评论-0  文章-3 

一个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

       

posted on 2014-12-28 14:42 搁浅的鱼 阅读(...) 评论(...)  编辑 收藏