resources.arsc解析

文章中所使用軟件和代碼資源

示例apk
示例代碼
binary view二進(jìn)制文件查看工具:
android 6.0系統(tǒng)源碼(網(wǎng)上搜索下載,這里暫不提供資源)

chunk

整個文件是由一系列的chunk構(gòu)成的,算是整個文件劃分的基本單位吧,實際上就是把整個文件無差別的劃分成多個模塊,每個模塊就是一個chunk,結(jié)構(gòu)更加清晰。每個chunk是最前面是一個ResChunk_header的結(jié)構(gòu)體,描述這個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,它是一個基礎(chǔ)類型。上面的枚舉定義了chunk的類型。ResChunk_header的成員變量解釋:

  • type 定義chunk的類型,與上面定義的枚舉中的值對應(yīng)。
  • headerSize 定義每個chunk頭的大小。
  • size 定義每個chunk數(shù)據(jù)塊的大小。

文件header(ResTable_header)

resources.arsc整個文件內(nèi)容也是一個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也是按照一定的順序進(jìn)行存儲。

  • 字符串資源表,包含了所有的表的值。這個字符串池包含整個資源中表中的字符串。如:名稱,類型等等。
  • 一個或多個資源包chunk
    我們先查看resources.arsc文件的開頭:
ResTable_header

從ResTable_header的結(jié)構(gòu)體定義我們進(jìn)行解析:

  • 02 00 前兩個字節(jié)是 type。chunk的類型,根據(jù)上面的枚舉定義RES_TABLE_TYPE = 0x0002知道這是一個資源表的chunk.
  • 0C 00 這兩個字節(jié)存儲的是headerSize的值,也就是chunk head的大小。我們看到ResTable_header內(nèi)是一個ResChunk_header和packageCount,計算得到是12個字節(jié),正好想對應(yīng)。
  • 78 74 03 00 這四個字節(jié)存儲的是uint32_t size;真?zhèn)€chunk的大小。轉(zhuǎn)化為十進(jìn)制等于 226424。這是第一個chunk也是最外層的chunk。所以它的大小是整個文件。我們查看一下resources.arsc的大小。
    resources.arsc的大小

    結(jié)果是相符的。
  • 01 00 這個存儲的是packageCount。包的數(shù)量為1。

這里的二進(jìn)制的存儲是小端對齊。如果不懂的可以google補(bǔ)習(xí)一下。對uint32_t、uint16_t不太了解的也可以搜一下。這里我們就不跑題了。

到這里第一個chunk,也就是最外層的chunk的頭我們已經(jīng)解析完成了。

全局字符串池

緊接著是Global String Pool,全局字符串池,這也是Resources.arsc存在最重要的一個原因之一,就是把所有字符串放到這個池子里,大家復(fù)用這些字符串,可以很大的減小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。我們繼續(xù)解析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 等于字符串的數(shù)量。
  • 00 00 00 00 styleCount=0 字符串的樣式的數(shù)量。
  • 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表示字符串是經(jīng)過排序的,而UTF8_FLAG位等于1表示字符串是使用UTF8編碼的,否則就是UTF16編碼的。
  • BC 1A 00 00 stringsStart=6844 等于字符串內(nèi)容塊相對于其頭部的距離
  • 00 00 00 00 stylesStart=0 等于字符串樣式塊相對于其頭部的距離。

除了ResStringPool_header頭部、字符串內(nèi)容塊和字符串樣式內(nèi)容塊之外,還有兩個偏移數(shù)組,分別是字符串偏移數(shù)組和字符串樣式偏移數(shù)組,這兩個偏移數(shù)組的大小就分別等于字符串的數(shù)量stringCount和styleCount的值,而每一個元素都是一個無符號整數(shù)。整個字符中資源池的組成就如圖13所示:


9.jpg

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

11.png

我們解析第一個字符串。在String Offset Array的前四個字節(jié)為0。所以它是從String Content的起始位置開始存儲。由前面計算知道String content相對head為6844。則相對絕對地址為6844+12=6856=0x1AC8。上面截圖顯示。

無論是UTF8,還是UTF16的字符串編碼,每一個字符串的前面都有2個字節(jié)表示其長度,而且后面以一個NULL字符結(jié)束。對于UTF8編碼的字符串來說,NULL字符使用一個字節(jié)的0x00來表示,而對于UTF16編碼的字符串來說,NULL字符使用兩個字節(jié)的0x0000來表示。

的到的結(jié)果就是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。我們跳轉(zhuǎn)到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個字節(jié)就是包名,每個字符占用兩個字節(jié),從圖中我們可以看到包名 com.wangheart.resdemo
6.png
  • 00 00 00 00 typeStrings 類型字符串資源池相對頭部的偏移位置。
  • 00 00 00 00 lastPublicType 等于最后一個導(dǎo)出的Public類型字符串在類型字符串資源池中的索引,目前這個值設(shè)置為類型字符串資源池的大小。
  • 20 01 00 00 keyStrings。資源項名稱字符串池keyStrings相對于package header起始位置的偏移是0x0120
  • 0C 00 00 00 lastPublicKey
  • D0 01 00 00 typeIdOffset
    8.jpg
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,694評論 19 139
  • Apk中的resources.arsc是aapt工具編譯資源時生成的一個重要文件。App資源能根據(jù)配置的變化,索引...
    小爨閱讀 21,444評論 4 44
  • 插件化-資源處理 寫的比較長,可以選擇跳過前面2節(jié),直接從0x03實例分析開始。如有錯誤,請不吝指正。 0x00 ...
    唐一川閱讀 5,809評論 2 22
  • 一、MemCache簡介 session MemCache是一個自由、源碼開放、高性能、分布式的分布式內(nèi)存對象緩存...
    李偉銘MIng閱讀 4,014評論 2 13
  • 休眠是植物適應(yīng)冬季低溫的一種自我保護(hù)性生理現(xiàn)象。在露地栽培的自然條件下,草莓經(jīng)過旺盛生長,進(jìn)入溫度變低、日照變短的...
    七星代閱讀 2,562評論 0 0

友情鏈接更多精彩內(nèi)容