resources.arsc解析

文章中所使用软件和代码资源

示例apk
示例代码
binary view二进制文件查看工具:
android 6.0系统源码(网上搜索下载,这里暂不提供资源)

chunk

整个文件是由一系列的chunk构成的,算是整个文件划分的基本单位吧,实际上就是把整个文件无差别的划分成多个模块,每个模块就是一个chunk,结构更加清晰。每个chunk是最前面是一个ResChunk_header的结构体,描述这个chunk的信息。

所有的chunk的header定义在android-6.0.0_r1\frameworks\base\include\androidfw\ResourceTypes.h

/** ********************************************************************
 *  Base Types
 *
 *  These are standard types that are shared between multiple specific
 *  resource types.
 *
 *********************************************************************** */

/**
 * Header that appears at the front of every data chunk in a resource.
 */
struct ResChunk_header
{
    // Type identifier for this chunk.  The meaning of this value depends
    // on the containing chunk.
    uint16_t type;

    // Size of the chunk header (in bytes).  Adding this value to
    // the address of the chunk allows you to find its associated data
    // (if any).
    uint16_t headerSize;

    // Total size of this chunk (in bytes).  This is the chunkSize plus
    // the size of any data associated with the chunk.  Adding this value
    // to the chunk allows you to completely skip its contents (including
    // any child chunks).  If this value is the same as chunkSize, there is
    // no data associated with the chunk.
    uint32_t size;
};

enum {
    RES_NULL_TYPE               = 0x0000,
    RES_STRING_POOL_TYPE        = 0x0001,
    RES_TABLE_TYPE              = 0x0002,
    RES_XML_TYPE                = 0x0003,

    // Chunk types in RES_XML_TYPE
    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,
    // This contains a uint32_t array mapping strings in the string
    // pool back to resource identifiers.  It is optional.
    RES_XML_RESOURCE_MAP_TYPE   = 0x0180,

    // Chunk types in RES_TABLE_TYPE
    RES_TABLE_PACKAGE_TYPE      = 0x0200,
    RES_TABLE_TYPE_TYPE         = 0x0201,
    RES_TABLE_TYPE_SPEC_TYPE    = 0x0202,
    RES_TABLE_LIBRARY_TYPE      = 0x0203
};

如注释解释,每个chunk的头都会包含ResChunk_header,它是一个基础类型。上面的枚举定义了chunk的类型。ResChunk_header的成员变量解释:

  • type 定义chunk的类型,与上面定义的枚举中的值对应。
  • headerSize 定义每个chunk头的大小。
  • size 定义每个chunk数据块的大小。

文件header(ResTable_header)

resources.arsc整个文件内容也是一个chunk,是ResTable。头部为ResTable_header。我们看到定义

/** ********************************************************************
 *  RESOURCE TABLE
 *
 *********************************************************************** */

/**
 * Header for a resource table.  Its data contains a series of
 * additional chunks:
 *   * A ResStringPool_header containing all table values.  This string pool
 *     contains all of the string values in the entire resource table (not
 *     the names of entries or type identifiers however).
 *   * One or more ResTable_package chunks.
 *
 * Specific entries within a resource table can be uniquely identified
 * with a single integer as defined by the ResTable_ref structure.
 */
struct ResTable_header
{
    struct ResChunk_header header;

    // The number of ResTable_package structures.
    uint32_t packageCount;
};

这是整个资源表的头,资源中的chunk也是按照一定的顺序进行存储。

  • 字符串资源表,包含了所有的表的值。这个字符串池包含整个资源中表中的字符串。如:名称,类型等等。
  • 一个或多个资源包chunk
    我们先查看resources.arsc文件的开头:
ResTable_header

从ResTable_header的结构体定义我们进行解析:

  • 02 00 前两个字节是 type。chunk的类型,根据上面的枚举定义RES_TABLE_TYPE = 0x0002知道这是一个资源表的chunk.
  • 0C 00 这两个字节存储的是headerSize的值,也就是chunk head的大小。我们看到ResTable_header内是一个ResChunk_header和packageCount,计算得到是12个字节,正好想对应。
  • 78 74 03 00 这四个字节存储的是uint32_t size;真个chunk的大小。转化为十进制等于 226424。这是第一个chunk也是最外层的chunk。所以它的大小是整个文件。我们查看一下resources.arsc的大小。
    resources.arsc的大小

    结果是相符的。
  • 01 00 这个存储的是packageCount。包的数量为1。

这里的二进制的存储是小端对齐。如果不懂的可以google补习一下。对uint32_t、uint16_t不太了解的也可以搜一下。这里我们就不跑题了。

到这里第一个chunk,也就是最外层的chunk的头我们已经解析完成了。

全局字符串池

紧接着是Global String Pool,全局字符串池,这也是Resources.arsc存在最重要的一个原因之一,就是把所有字符串放到这个池子里,大家复用这些字符串,可以很大的减小APK包的尺寸。

/**
 * Definition for a pool of strings.  The data of this chunk is an
 * array of uint32_t providing indices into the pool, relative to
 * stringsStart.  At stringsStart are all of the UTF-16 strings
 * concatenated together; each starts with a uint16_t of the string's
 * length and each ends with a 0x0000 terminator.  If a string is >
 * 32767 characters, the high bit of the length is set meaning to take
 * those 15 bits as a high word and it will be followed by another
 * uint16_t containing the low word.
 *
 * If styleCount is not zero, then immediately following the array of
 * uint32_t indices into the string table is another array of indices
 * into a style table starting at stylesStart.  Each entry in the
 * style table is an array of ResStringPool_span structures.
 */
struct ResStringPool_header
{
    struct ResChunk_header header;

    // Number of strings in this pool (number of uint32_t indices that follow
    // in the data).
    uint32_t stringCount;

    // Number of style span arrays in the pool (number of uint32_t indices
    // follow the string indices).
    uint32_t styleCount;

    // Flags.
    enum {
        // If set, the string index is sorted by the string values (based
        // on strcmp16()).
        SORTED_FLAG = 1<<0,

        // String pool is encoded in UTF-8
        UTF8_FLAG = 1<<8
    };
    uint32_t flags;

    // Index from header of the string data.
    uint32_t stringsStart;

    // Index from header of the style data.
    uint32_t stylesStart;
};

同样全局池的head里有一个ResChunk_header。我们继续解析resources.arsc文件


ResStringPool_header
  • 01 00 type=1则为RES_STRING_POOL_TYPE = 0x0001
  • 1C 00headerSize=28
  • 3C E8 00 00 size=59452
  • A8 06 00 00 stringCount=1704 等于字符串的数量。
  • 00 00 00 00 styleCount=0 字符串的样式的数量。
  • 00 01 00 00 flags =0x00000100==1<<8 。UTF-8编码,枚举中有定义
        // String pool is encoded in UTF-8
        UTF8_FLAG = 1<<8
    
    等于0、SORTED_FLAG、UTF8_FLAG或者它们的组合值,用来描述字符串资源串的属性,例如,SORTED_FLAG位等于1表示字符串是经过排序的,而UTF8_FLAG位等于1表示字符串是使用UTF8编码的,否则就是UTF16编码的。
  • BC 1A 00 00 stringsStart=6844 等于字符串内容块相对于其头部的距离
  • 00 00 00 00 stylesStart=0 等于字符串样式块相对于其头部的距离。

除了ResStringPool_header头部、字符串内容块和字符串样式内容块之外,还有两个偏移数组,分别是字符串偏移数组和字符串样式偏移数组,这两个偏移数组的大小就分别等于字符串的数量stringCount和styleCount的值,而每一个元素都是一个无符号整数。整个字符中资源池的组成就如图13所示:


9.jpg

上面我们解析到stringCount=1704 ,1704*4=6816 而6816+28=6844正好等于stringsStart。

11.png

我们解析第一个字符串。在String Offset Array的前四个字节为0。所以它是从String Content的起始位置开始存储。由前面计算知道String content相对head为6844。则相对绝对地址为6844+12=6856=0x1AC8。上面截图显示。

无论是UTF8,还是UTF16的字符串编码,每一个字符串的前面都有2个字节表示其长度,而且后面以一个NULL字符结束。对于UTF8编码的字符串来说,NULL字符使用一个字节的0x00来表示,而对于UTF16编码的字符串来说,NULL字符使用两个字节的0x0000来表示。

的到的结果就是res/anim/abc_fade_out.xml。这个我们用到的appcompat兼容库中包含这个文件。


12.png

同样的方法再往后解析就是res/drawable/abc_list_selector_background_transition_holo_dark.xml,这个同样在兼容库中。

abc_list_selector_background_transition_holo_dark.xml

后面的解析以此类推,我就不做过多的介绍了

Package解析

/**
 * A collection of resource data types within a package.  Followed by
 * one or more ResTable_type and ResTable_typeSpec structures containing the
 * entry values for each resource type.
 */
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.
    uint16_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;

    uint32_t typeIdOffset;
};

前面我们计算知道ResTable_header的大小为12,紧接着后面就是全局字符串chunk。大小为 59452。所以package前有59452+12=59464=0xE848。我们跳转到resources.arsc的这个地址处。

ResTable_package
  • 00 02 type=0x0200,RES_TABLE_PACKAGE_TYPE = 0x0200
  • 120 01 head_size=288 12+4+128*2+4+4+4+4+4=288。
  • 30 8C 02 00 size=166960。我们看到166960+59452=226412正好是等于文件的大小
  • 7F 00 00 00是id
  • 后面128*2个字节就是包名,每个字符占用两个字节,从图中我们可以看到包名 com.wangheart.resdemo
6.png
  • 00 00 00 00 typeStrings 类型字符串资源池相对头部的偏移位置。
  • 00 00 00 00 lastPublicType 等于最后一个导出的Public类型字符串在类型字符串资源池中的索引,目前这个值设置为类型字符串资源池的大小。
  • 20 01 00 00 keyStrings。资源项名称字符串池keyStrings相对于package header起始位置的偏移是0x0120
  • 0C 00 00 00 lastPublicKey
  • D0 01 00 00 typeIdOffset
    8.jpg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,594评论 18 139
  • Apk中的resources.arsc是aapt工具编译资源时生成的一个重要文件。App资源能根据配置的变化,索引...
    小爨阅读 20,725评论 4 43
  • 插件化-资源处理 写的比较长,可以选择跳过前面2节,直接从0x03实例分析开始。如有错误,请不吝指正。 0x00 ...
    唐一川阅读 5,312评论 2 22
  • 一、MemCache简介 session MemCache是一个自由、源码开放、高性能、分布式的分布式内存对象缓存...
    李伟铭MIng阅读 3,781评论 2 13
  • 休眠是植物适应冬季低温的一种自我保护性生理现象。在露地栽培的自然条件下,草莓经过旺盛生长,进入温度变低、日照变短的...
    七星代阅读 2,108评论 0 0