http://blog.csdn.net/beyond702/article/details/51744082
一、前言
對於APK里面的Resources.arsc文件大家應該都知道是干什么的(不知道的請看我的另一篇文章Android應用程序資源文件的編譯和打包原理),它實際上就是App的資源索引表。下面我會結合實例對它的格式做一下剖析,讀完這篇文章應該能夠知道Resources.arsc的格式,並可以從二進制的文件中查找到資源的相關信息,或者根據資源的id可以定位到二進制文件中的位置。不過本人對Android資源文件的有一些相關概念並不是特別熟悉,所以文章中有很多地方也並不明白,如有錯誤歡迎指正!
二、R.java文件及資源ID
首先先介紹一下我們在Android應用開發過程中程序中用的資源的id,相信大家都知道R.java文件,這個是通過aapt對資源文件進行編譯生成的資源id文件,這樣我們程序中使用資源文件更加方便。舉例我們先看一下原始的資源文件res/values/strings.xml內容如下:
- <?xml version="1.0" encoding="utf-8"?>
- <resources>
- <string name="app_name">Cert</string>
- <string name="hello_world">Hello world!</string>
- <string name="action_settings">Settings</string>
- </resources>
這里先介紹幾個概念,上面的app_name和hello_world這些叫做資源項名稱(其它的還有windowActionBar、ActionBarTabStyle類似這種),而它們對應的資源項類型就是string(其它的還有attr、drawable類似這些),資源項的值就是Cert和Hello world!這些。
下面是對應R.java文件的內容:
- public final class R {
- ...
- public static final class string {
- ...
- /** Description of the choose target button in a ShareActionProvider (share UI). [CHAR LIMIT=NONE]
- */
- public static final int abc_shareactionprovider_share_with=0x7f0a000c;
- /** Description of a share target (both in the list of such or the default share button) in a ShareActionProvider (share UI). [CHAR LIMIT=NONE]
- */
- public static final int abc_shareactionprovider_share_with_application=0x7f0a000b;
- public static final int action_settings=0x7f0a000f;
- public static final int app_name=0x7f0a000d;
- public static final int hello_world=0x7f0a000e;
- }
- ...
- }
代碼段2
可以看到每個資源文件在R中都是一個class,每個資源項名稱都分配了一個id,id值是一個四字節無符號整數,格式是這樣的:0xpptteeee,(p代表的是package,t代表的是type,e代表的是entry),最高字節代表Package ID,次高字節代表Type ID,后面兩個字節代表Entry ID。
Package ID相當於是一個命名空間,限定資源的來源。Android系統當前定義了兩個資源命令空間,其中一個系統資源命令空間,它的Package ID等於0x01,另外一個是應用程序資源命令空間,它的Package ID等於0x7f。所有位於[0x01, 0x7f]之間的Package ID都是合法的,而在這個范圍之外的都是非法的Package ID。前面提到的系統資源包package-export.apk的Package ID就等於0x01,而我們在應用程序中定義的資源的Package ID的值都等於0x7f,這一點可以通過生成的R.java文件來驗證。
Type ID是指資源的類型ID。資源的類型有animator、anim、color、drawable、layout、menu、raw、string和xml等等若干種,每一種都會被賦予一個ID。
Entry ID是指每一個資源在其所屬的資源類型中所出現的次序。注意,不同類型的資源的Entry ID有可能是相同的,但是由於它們的類型不同,我們仍然可以通過其資源ID來區別開來。
三、解析Resources.arsc
1. Resources.arsc文件格式
下面我們開始看Resources.arsc(后面截圖給出的resources.arsc文件的二進制內容都是與上面代碼段1和代碼段2相對應的),首先看一下文件的格式,如下面兩個圖:
圖2
以上兩個圖都是Resources.arsc文件的格式,圖1是從網上找的,其中很多項都展開了,不了解對應的數據結構肯定看不懂,所以我自己畫了圖2(畫圖好蛋疼的說~),相對來說更容易接受一點,這里都放出來做個對照吧。Resources.arsc對應的數據結構的定義在Android源碼/frameworks/base/include/androidfw/ResourceType.h中,大家可以自己去看一下。
2. chunk
下面我來從上到下介紹一下文件的格式,首先是chunk概念,整個文件是由一系列的chunk構成的,算是整個文件划分的基本單位吧,實際上就是把整個文件無差別的划分成多個模塊,每個模塊就是一個chunk,結構更加清晰。每個chunk是最前面是一個ResChunk_header的結構體,描述這個chunk的信息,ResChunk_header如下:
- 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;
- };
3. 文件header
Resources.arsc文件的最開始是整個文件的header,結構是ResTable_header:
- struct ResTable_header
- {
- struct ResChunk_header header;
- // The number of ResTable_package structures.
- uint32_t packageCount;
- ;
可以看到header就是一個chunk,以ResChunk_header結構開頭來描述這個chunk。resources.arsc文件的header內容如下圖中選中部分:
圖3
圖中選中的部分就是header,可以看到類型是0x0002,對應類型是RES_TABLE_TYPE,headerSize是0x0c,整個chunk的大小也就是文件的大小是0x019584,package的數量是1個。
4. 全局字符串池
緊接着是Global String Pool,全局字符串池,這也是Resources.arsc存在最重要的一個原因之一,就是把所有字符串放到這個池子里,大家復用這些字符串,可以很大的減小APK包的尺寸。從圖1和圖2可以看到后面還有兩個字符串池,那么什么字符串會放到這個全局字符串池中呢?所有的資源文件的路徑名,以及資源文件中所定義的資源的值,比如代碼段1中的Cert和Hello world!都存在這里。
字符串池的結構體如下:
- 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;<span style="white-space:pre"> </span>//If flags is 0x0, string pool is encoded in UTF-16
- // Index from header of the string data.
- uint32_t stringsStart;
- // Index from header of the style data.
- uint32_t stylesStart;
對應的二進制內容如下圖選中部分:
圖4
從圖中可以看到類型是0x0001,對應代碼段3中RES_STRING_POOL_TYPE,整個chunk的大小是0x919C,stringCount是0x03E1,styleCount是0,flags是0x0100即UTF8格式,stringsStart即字符串相對頭部起始位置的偏移是0x0FA0。
從圖2中可以看到緊接着header的是stringCount個字符串偏移數組,數組每一個元素記錄着每個字符串的起始位置相對於stringsStart的偏移。字符串池中每個UTF8格式字符串都是以字符串結束符0x00結束的,UTF16是0x0000。
style偏移數組與string是一樣的就不多說了,但這個style是干什么的現在我還不清楚,以后知道了再更新。
5. Package解析
下面要介紹重頭戲Package了。首先是一個package的header,結構體如下:
- 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;
- };
圖4中全局字符串池的起始位置是0xC,而整個chunk的大小是0x919C,那么package的起始位置就是兩者相加得到0x91A8,對應二進制內容如下圖選中部分:
圖5
從上圖可以看到chunk類型是0x0200,對應代碼段3中的RES_TABLE_PACKAGE_TYPE,id是0x7F(這與R.java中的每個資源id的最高字節是一樣的),這個package的名字是com.example.cert,類型字符串池typeStrings相對於package header起始位置的偏移是0x011C,類型字符串的個數是0x0C,資源項名稱字符串池keyStrings相對於package header起始位置的偏移是0x01C8,個數是0x01E1。
5.1 類型字符串池和資源項名稱字符串池
對於類型字符串池(圖2中的Type String Pool)和資源項名稱字符串池(圖2中的Key String Pool)的結構和內容我這里就不貼出來了,結構和全局字符串池是一樣的。類型字符串池中存儲的是所有類型相關的字符串,比如attr,drawable,layout這些;而資源項名稱字符串池中存儲的是應用所有資源文件中的資源項名稱相關的字符串,比如代碼段1中的app_name,hello_world,action_settings。
5.2 類型規范數據塊(Type Spec)
類型規范數據塊用來描述資源項的配置差異性。通過這個差異性描述,我們就可以知道每一個資源項的配置狀況。知道了一個資源項的配置狀況之后,Android資源管理框架在檢測到設備的配置信息發生變化之后,就可以知道是否需要重新加載該資源項。類型規范數據塊是按照類型來組織的,也就是說,每一種類型都對應有一個類型規范數據塊。
上面是從參考文章里copy過來的,可能有些人不太了解這個Type Spec是什么東西,我個人的理解它實際上就是類型。說到這里需要提幾句Android資源文件的配置問題,大家都知道Android設備眾多,為了使得一個應用程序能夠在運行時同時支持不同的大小和密度的屏幕,以及支持國際化,即支持不同的國家地區和語言,Android應用程序資源的組織方式有18個維度,每一個維度都代表一個配置信息,從而可以使得應用程序能夠根據設備的當前配置信息來找到最匹配的資源來展現在UI上,從而提高用戶體驗。也就是說,每一個資源類,都會有一個配置列表,配置着這個資源類的不同維度的信息,那么Type Spec就是這個資源類的代表。比如前面看到的attr,drawable,string這種都是資源類,Type Spec就是描述這些的結構,前面說到過R.java中每個資源id的格式是0xpptteeee,里面那個次高字節的tt就是Type Spec的id,同時這個id值也是這個Type Spec的類型名稱在Type String Pool類型字符串池中索引數組的索引值,根據id值就可以找到其名稱。
下面是Type Spec的結構:
- struct ResTable_typeSpec
- {
- struct ResChunk_header header;
- //標識資源的Type ID,Type ID是指資源的類型ID,從1開始。資源的類型有animator、anim、color、drawable、layout、menu、raw、string和xml等等若干種,每一種都會被賦予一個ID。
- uint8_t id;
- //保留,始終為0
- uint8_t res0;
- //保留,始終為0
- uint16_t res1;
- //等於本類型的資源項個數,指名稱相同的資源項的個數。
- uint32_t entryCount;
- };
下圖是其對應的二進制數據:
圖6
上圖可以看出該chunk的類型是0x0202,這個Type Spec的id是1,entryCount是6E,在這個ResTable_typeSpec結構后面緊跟着entryCount個資源spec數組,entryCount指的是這個類型有多少資源項,在后面我們會講到aapt解碼resources.arsc,輸出中每個Type Spec的資源項后面會有一個flags,它的值就是這個數組中對應的值,但是這個flag代表什么我還不清楚。
5.3 Config List
上面講到每個Type Spec是對一個類型的描述,每個類型會有多個維度,那就是接下來的Config List了,這個Config List是由多個ResTable_type結構來描述的,每個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;
- };
其中的id與ResTable_typeSpec中的id值是一樣的。其中的ResTable_config就是這個維度的具體描述了,如下:
- struct ResTable_config
- {
- // Number of bytes in this structure.
- uint32_t size;
- union {
- struct {
- // Mobile country code (from SIM). 0 means "any".
- uint16_t mcc;
- // Mobile network code (from SIM). 0 means "any".
- uint16_t mnc;
- };
- uint32_t imsi;
- };
- union {
- struct {
- // \0\0 means "any". Otherwise, en, fr, etc.
- char language[2];
- // \0\0 means "any". Otherwise, US, CA, etc.
- char country[2];
- };
- uint32_t locale;
- };
- union {
- struct {
- uint8_t orientation;
- uint8_t touchscreen;
- uint16_t density;
- };
- uint32_t screenType;
- };
- union {
- struct {
- uint8_t keyboard;
- uint8_t navigation;
- uint8_t inputFlags;
- uint8_t inputPad0;
- };
- uint32_t input;
- };
- union {
- struct {
- uint16_t screenWidth;
- uint16_t screenHeight;
- };
- uint32_t screenSize;
- };
- union {
- struct {
- uint16_t sdkVersion;
- // For now minorVersion must always be 0!!! Its meaning
- // is currently undefined.
- uint16_t minorVersion;
- };
- uint32_t version;
- };
- union {
- struct {
- uint8_t screenLayout;
- uint8_t uiMode;
- uint16_t smallestScreenWidthDp;
- };
- uint32_t screenConfig;
- };
- union {
- struct {
- uint16_t screenWidthDp;
- uint16_t screenHeightDp;
- };
- uint32_t screenSizeDp;
- };
- }
下圖是示例中ResTable_type的二進制內容:
圖7
上圖可以看到這個chunk的類型是0x0201,id是1與上面的Type Spec是對應的,entryCount是0x6E與上面的Type Spec也是一樣的,entriesStart是0x01F0表示entry列表相對於此頭部起始位置的偏移,后面0x24表示ResTable_config的大小,后面其它的字節全是0說明配置信息全是any,也就是default默認的配置。
5.4 Entries
緊接着ResTable_type后面是entryCount個entry的索引數組,每個索引數組的值表示該entry相對於entriesStart的偏移。那么這個entry代表什么呢?就是一個資源項!R.java中每個id的結構是0xpptteeee,低位兩個字節的eeee就是這個資源項在索引數組中的索引值。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;
- };
上面結構中的key字段的值是這個資源項的名字在資源項字符串資源池的索引數組中的索引值,通過這個值就可以查到這個資源項的名稱。另一個很重要的字段是flags,如果是0x0001位為0的話,那么這個結構后面跟一個ResTable_value;若該位為1的話,就表示這個結構是個ResTable_map_entry(繼承自ResTable_entry),並且后面會跟一個或多個ResTable_map結構。這些結構的定義如下:
- struct ResTable_map_entry : public ResTable_entry
- {
- //指向父ResTable_map_entry的資源ID,如果沒有父ResTable_map_entry,則等於0。
- ResTable_ref parent;
- //等於后面ResTable_map的數量
- uint32_t count;
- };
- 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結構后面跟多少個ResTable_map由其結構中的count字段來決定。
下面看示例中對應ResTable_entry的二進制代碼:
圖8
可以看到size是0x10,flags是0x0001,也就是說它是一個ResTable_map_entry結構而不是ResTable_entry,key是0表示其名字在Key String Pool的索引數組中的0號元素,然后看到count是1,那么后面跟一個ResTable_map,其中name的值是0x01000000,具體含義查看系統源碼文件中該結構的定義,這里就不多說了,后面ResTable_value的size是0x08,dataType是0x10即TYPE_FIRST_INT即后面data數據是int類型。
到這里整個文件的結構解析大概就介紹完了,下面我們會從另一個角度來介紹,我們根據資源的id值來找resources.arsc中的數據。
四、根據資源的id值定位resources.arsc中的數據
我們在用Eclipse或者Android Studio來寫Android應用的時候,IDE直接幫我們生成了R.java文件,我們可以在這里面看到某個資源的id值,其實IDE也是使用aapt來編譯資源文件生成的R.java。如果我們拿到一個APK怎么看資源的id呢?當然也是用aapt來反編譯就好了,命令如下:
- aapt d resources XXX.apk
對應我們之前的示例輸出如下:
- Package Groups (1)
- Package Group 0 id=127 packageCount=1 name=com.example.cert
- Package 0 id=127 name=com.example.cert typeCount=12
- ......
- type 9 configCount=56 entryCount=16
- spec resource 0x7f0a000b com.example.cert:string/abc_shareactionprovider_share_with_application: flags=0x00000004
- spec resource 0x7f0a000c com.example.cert:string/abc_shareactionprovider_share_with: flags=0x00000004
- spec resource 0x7f0a000d com.example.cert:string/app_name: flags=0x00000000
- spec resource 0x7f0a000e com.example.cert:string/hello_world: flags=0x00000000
- spec resource 0x7f0a000f com.example.cert:string/action_settings: flags=0x00000000
- config (default):
- resource 0x7f0a000b com.example.cert:string/abc_shareactionprovider_share_with_application: t=0x03 d=0x00000162 (s=0x0008 r=0x00)
- resource 0x7f0a000c com.example.cert:string/abc_shareactionprovider_share_with: t=0x03 d=0x0000015b (s=0x0008 r=0x00)
- resource 0x7f0a000d com.example.cert:string/app_name: t=0x03 d=0x00000154 (s=0x0008 r=0x00)
- resource 0x7f0a000e com.example.cert:string/hello_world: t=0x03 d=0x00000152 (s=0x0008 r=0x00)
- resource 0x7f0a000f com.example.cert:string/action_settings: t=0x03 d=0x00000155 (s=0x0008 r=0x00)
- config ca:
- resource 0x7f0a000b com.example.cert:string/abc_shareactionprovider_share_with_application: t=0x03 d=0x00000197 (s=0x0008 r=0x00)
- resource 0x7f0a000c com.example.cert:string/abc_shareactionprovider_share_with: t=0x03 d=0x0000019f (s=0x0008 r=0x00)
- config da:
- resource 0x7f0a000b com.example.cert:string/abc_shareactionprovider_share_with_application: t=0x03 d=0x000001bb (s=0x0008 r=0x00)
- resource 0x7f0a000c com.example.cert:string/abc_shareactionprovider_share_with: t=0x03 d=0x000001bc (s=0x0008 r=0x00)
- config fa:
- resource 0x7f0a000b com.example.cert:string/abc_shareactionprovider_share_with_application: t=0x03 d=0x000001fe (s=0x0008 r=0x00)
- resource 0x7f0a000c com.example.cert:string/abc_shareactionprovider_share_with: t=0x03 d=0x000001fb (s=0x0008 r=0x00)
- config ja:
- resource 0x7f0a000b com.example.cert:string/abc_shareactionprovider_share_with_application: t=0x03 d=0x00000287 (s=0x0008 r=0x00)
- resource 0x7f0a000c com.example.cert:string/abc_shareactionprovider_share_with: t=0x03 d=0x00000280 (s=0x0008 r=0x00)
- ......
從上面的輸出可以看到,只有一個package,id是127即0x7F,name是com.example.cert,與之前看到的一樣,typeCount有12個即一共有12個ResTable_typeSpec結構體。字符串string類型的資源類型id是0x0A(上面那個type 9是因為aapt工具從0開始數的,而ResTable_typeSpec里的id是從1開始數的)。
后面我們看到spec resource的字樣,這個spec就和我們前面介紹的Type Spec是一樣的了,這幾行就代表它是類型規范數據塊了,然后后面是Config List,有很多config,每個config是一個維度,里面對每個資源項都有自己的配置信息,aapt輸出config的格式如下:
- resource <Resource ID> <Package Name>:<Type>/<Name>: t=<DataType> d=<Data> (s=<Size> r=<Res0>)
- Resource ID R.java中的資源ID
- Package Name 資源所在的的包
- Type 資源的類型
- Name 資源名稱
- DataType 數據類型,按照以下枚舉類型取值
- Data 資源的值,根據dataType進行解釋
- Size 一直為0x0008
- Res0 固定為0x00
因為hellow_world和app_name這些字符串是我自己demo用的,沒有配置多維度,所以只有在config default里面才有,其它維度沒有。下面我們以hello_world為例,來定位它在resources.arsc文件中的位置。hello_world的id值是0x7F0A000E,分解以后package id是0x7F,Type Spec的id是0x0A,entry id是0x0E。
這里面因為只有一個Package,所以就不需要去定位Package了。
從圖2中我們沒找到對Type Spec有索引數組,所以我們需要去一個一個的找Type Spec,從第一個ResTable_typeSpec開始,在ResChunk_header中有整個chunk的大小,一個一個的將收地址加上這個大小就可以了,直到我們找到chunk的類型是0x0202,id是0x0A的ResTable_typeSpec結構體就可以了,下圖是示例對應的二進制數據:

可以看到ResTable_typeSpec后面是0x00000004,這個就是代碼段11中每行后面的flags,具體含義還不清楚。entryCount是0x10,有16個string類型的資源項。然后下面我們去找第0x0E個ResTable_entry結構體,當然不一定Config List中每個維度都會有,但是default默認的config中肯定是有的。首先找到第一個ResTable_type結構體,方法就是ResTable_typeSpec的起始地址0x11C7C加上這個chunk的大小0x50,位置在0x11CCC,數據如下圖:
可以看到entryCount是0x10,entriesStart是0x78,將起始地址與這個偏移相加就可以得到ResTable_entry的開始地址是0x11D44。
hello_world的entry id是0x0E,那么我們從緊接着ResTable_type的entry數組中找第0x0E個元素,它的值是0xE0,加上ResTable_entry的起始地址,得到我們要找的hello_world對應的ResTable_entry的地址是0x11E24,對應內容如下圖:
圖11
可以看到size是0x08,flags是0,也就是說它是ResTable_entry結構,后面跟一個ResTable_value結構,再看key的值是0x0152,即資源項名稱在Key String Pool的字符串偏移數組的索引是第0x0152個,找到以后如下圖,具體方法我就不啰嗦了,就是偏移數組收地址加上那個0x0152的時候別忘了這個0x0152要先乘以4,因為數組元素是4字節大小嘛。
圖12
然后再看圖11中ResTable_value的內容,size是0x08,type是0x03對應TYPE_STRING類型,data值是0x0152,這個就是全局字符串池的字符串偏移數組的索引了,找到以后如下圖:
圖13
可以看到對應的字符串值就找到了!!
五、總結
啰嗦了這么多,好累。。。不過大概搞明白了,算是自己的筆記吧,同時也分享給大家,我相信這個夠詳細了!只不過里面很多16進制的地址,還有亂七八糟的數據結構,看着肯定頭大,所以不能光看,還是要動手去做,這樣才能明白的透徹,否則肯定看不下來。resources.arsc文件的格式還是很簡單的,說白了就是一個索引文件。稍后我會寫一個解析這個文件的C++程序練手,加深印象,寫完以后我會放到github上並把鏈接放出來。
參考文章:
1. http://www.freebuf.com/articles/terminal/75944.html
2. http://blog.csdn.net/jiangwei0910410003/article/details/50628894
3. http://blog.csdn.net/luoshengyang/article/details/8744683
4. http://blog.csdn.net/mldxs/article/details/44956911