1. 程式人生 > >Android resources.arsc檔案與資源防護

Android resources.arsc檔案與資源防護

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內容如下:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.     <string name="app_name">Cert</string>  
  4.     <string name="hello_world">Hello world!</string>  
  5.     <string name="action_settings">Settings</string>  
  6. </resources>  
程式碼段1

這裡先介紹幾個概念,上面的app_name和hello_world這些叫做資源項名稱(其它的還有windowActionBar、ActionBarTabStyle類似這種),而它們對應的資源項型別就是string(其它的還有attr、drawable類似這些),資源項的值就是Cert和Hello world!這些。

下面是對應R.java檔案的內容:

  1. public final class R {  
  2.               ...  
  3.     public static final class string {  
  4.                      ...  
  5.         /**  Description of the choose target button in a ShareActionProvider (share UI). [CHAR LIMIT=NONE]  
  6.          */  
  7.         public static final int abc_shareactionprovider_share_with=0x7f0a000c;  
  8.         /**  Description of a share target (both in the list of such or the default share button) in a ShareActionProvider (share UI). [CHAR LIMIT=NONE]  
  9.          */  
  10.         public static final int abc_shareactionprovider_share_with_application=0x7f0a000b;  
  11.         public static final int action_settings=0x7f0a000f;  
  12.         public static final int app_name=0x7f0a000d;  
  13.         public static final int hello_world=0x7f0a000e;  
  14.     }  
  15.                      ...  
  16. }  

程式碼段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相對應的),首先看一下檔案的格式,如下面兩個圖:

圖1

圖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如下:

  1. struct ResChunk_header  
  2.  {  
  3.      enum   
  4.      {  
  5.          RES_NULL_TYPE               = 0x0000,  
  6.          RES_STRING_POOL_TYPE        = 0x0001,  
  7.          RES_TABLE_TYPE              = 0x0002,  
  8.          RES_XML_TYPE                = 0x0003,  
  9.          RES_XML_FIRST_CHUNK_TYPE    = 0x0100,  
  10.          RES_XML_START_NAMESPACE_TYPE= 0x0100,  
  11.          RES_XML_END_NAMESPACE_TYPE  = 0x0101,  
  12.          RES_XML_START_ELEMENT_TYPE  = 0x0102,  
  13.          RES_XML_END_ELEMENT_TYPE    = 0x0103,  
  14.          RES_XML_CDATA_TYPE          = 0x0104,  
  15.          RES_XML_LAST_CHUNK_TYPE     = 0x017f,  
  16.          RES_XML_RESOURCE_MAP_TYPE   = 0x0180,  
  17.          RES_TABLE_PACKAGE_TYPE      = 0x0200,  
  18.          RES_TABLE_TYPE_TYPE         = 0x0201,  
  19.          RES_TABLE_TYPE_SPEC_TYPE    = 0x0202  
  20.      };  
  21.      //當前這個chunk的型別  
  22.      uint16_t type;  
  23.      //當前這個chunk的頭部大小  
  24.      uint16_t headerSize;  
  25.      //當前這個chunk的大小  
  26.      uint32_t size;  
  27.  };  
程式碼段3

3. 檔案header

Resources.arsc檔案的最開始是整個檔案的header,結構是ResTable_header:

  1. struct ResTable_header  
  2. {  
  3.     struct ResChunk_header header;  
  4.     // The number of ResTable_package structures.  
  5.     uint32_t packageCount;  
  6. ;  
程式碼段4

可以看到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!都存在這裡。

字串池的結構體如下:

  1. struct ResStringPool_header  
  2. {  
  3.     struct ResChunk_header header;  
  4.     // Number of strings in this pool (number of uint32_t indices that follow in the data).  
  5.     uint32_t stringCount;  
  6.     // Number of style span arrays in the pool (number of uint32_t indices follow the string indices).  
  7.     uint32_t styleCount;  
  8.     // Flags.  
  9.     enum {  
  10.         // If set, the string index is sorted by the string values (based on strcmp16()).  
  11.         SORTED_FLAG = 1<<0,  
  12.         // String pool is encoded in UTF-8  
  13.         UTF8_FLAG = 1<<8  
  14.     };  
  15.     uint32_t flags;<span style="white-space:pre"> </span>//If flags is 0x0, string pool is encoded in UTF-16  
  16.     // Index from header of the string data.  
  17.     uint32_t stringsStart;  
  18.    // Index from header of the style data.  
  19.    uint32_t stylesStart;  
程式碼段5

對應的二進位制內容如下圖選中部分:

圖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,結構體如下:

  1. struct ResTable_package  
  2.  {  
  3.      struct ResChunk_header header;  
  4.      //包的ID,等於Package Id,一般使用者包的值Package Id為0X7F,系統資源包的Package Id為0X01。  
  5.      uint32_t id;  
  6.      //包名稱  
  7.      char16_t name[128];  
  8.      //型別字串資源池相對頭部的偏移  
  9.      uint32_t typeStrings;  
  10.      //最後一個匯出的Public型別字串在型別字串資源池中的索引,目前這個值設定為型別字串資源池的元素個數。  
  11.      uint32_t lastPublicType;  
  12.      //資源項名稱字串相對頭部的偏移  
  13.      uint32_t keyStrings;  
  14.      //最後一個匯出的Public資源項名稱字串在資源項名稱字串資源池中的索引,目前這個值設定為資源項名稱字串資源池的元素個數。  
  15.      uint32_t lastPublicKey;  
  16.  };  
程式碼段6

圖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的結構:

    相關推薦

    Android resources.arsc檔案資源防護

    http://blog.csdn.net/beyond702/article/details/51744082 一、前言 對於APK裡面的Resources.arsc檔案大家應該都知道是幹什麼的(不知道的請看我的另一篇文章Android應用程式資原始檔的編

    NIO.2中的檔案資源支援

    隨著JDK 7 的釋出,Java對NIO進行了極大的擴充套件,增強了對檔案處理和檔案系統特性的支援,以至於我們稱他們為NIO.2。因為NIO 提供的一些功能,NIO已經成為檔案處理中越來越重要的部分。 【1】Path 與Paths java.nio.file.P

    Android 自定義型別檔案程式關聯

    0x01 功能  實現在其他應用中開啟某個字尾名的檔案 可以直接跳轉到本應用中的某個activity進行處理   0x01 實現    首先建立一個activity ,然後在manifest裡對該activity項編輯,加入    <intent-

    大資料背景下檔案資訊資源挖掘策略方法研究

    檔案資訊資源具有來源多元、內容豐富、資訊散佈、資料繁雜等特性,對檔案部門的檔案資訊管理控制能力和開發利用能力提出了很高的要求。大資料技術通過新的資訊處理模式和技術,解決了一些無法用常規軟體工具處理的資料問題,為提升檔案資訊管理和開發能力提供了新的解決思路和技術手段。 青島市檔案館完成的“大資料背

    Android逆向之旅---解析編譯之後的Resource arsc檔案格式

                    一、前言快過年了,先提前祝賀大家新年快樂,這篇文章也是今年最後一篇了。今天我們繼續來看逆向的相關知識,前篇文章中我們介紹瞭如何解析Android中編譯之後的AndroidManifest.xml檔案格式:http://blog.csdn.net/jiangwei09104100

    關於android studio3.2後引入資源問題error: failed linking file resources.

    當我們在專案中新增一些第三方庫時,我們只需要將資源通過app中build檔案引入,正常情況下是直接Sync重新編譯就可以。但是在使用studio3.2時卻出現引入資源錯誤。檢視網上其他大神的遇到同樣的錯誤後解決方法。 其中一種是: 解決辦法:  Android的Gradl

    iOS檔案路徑操作資源匯入方法

    //獲取整個程式所在目錄 NSString* homePath = NSHomeDirectory(); NSLog(@"%@",homePath); //獲取.app檔案目錄 NSString* appPath = [[NSBund

    Android Studio快速匯入SO檔案jAR包

    首先將你需要匯入的jar包和so檔案 直接拷貝至Projuect目錄下的lib包下 在Module層級下的build.gradle檔案裡寫入此段程式碼 sourceSets{ main

    Android】多語言適配:語言、名稱、資源對應關係

    語言碼_國家碼  ->  語言選擇裡的顯示語言  ->  英文下的語言(國家) -> 簡體中文下的語言(國家)  af ->     Afrikaans ->     Afrikaans ->     南非荷蘭文  af_N

    Android中的ClassLoaderdex檔案加密實現分析

    Android中的ClassLoader BaseDexClassLoader Dex類載入器的基類,包含Dex類載入器之間通用功能的實現。 DexClassLoader A class loader that loads classes from .jar

    Android重疊包資源合併一見

    前言 在 Android逆向分析(2) APK的打包與安裝 一文中對資源編譯過程的介紹中,筆者提到了overlay(重疊包)這個概念,一位每天都被自己帥醒的好友看了那篇東西后,來問我這個重疊包究竟是個什麼東西,筆者想了想,確實這個概念有很多同學們都不甚瞭解,搜尋了一下網上了介紹,也幾乎沒有看到任何對這個

    Android中dex檔案的載入優化流程

    目錄 1、dex檔案分析 邏輯上,可以把dex檔案分成3個區,標頭檔案、索引區和資料區。索引區的ids字尾為identifiers的縮寫。 header dex檔案裡的header。除了描述.dex檔案的檔案資訊外,還有檔案裡其他各個區域的索引。 (

    Android學習記錄(1)—Android中XML檔案的序列化生成解析

    xml檔案是非常常用的,在android中json和xml是非常常用的兩種封裝資料的形式,從伺服器中獲取資料也經常是這兩種形式的,所以學會生成和解析xml和json是非常有用的,json相對來說是比較簡單的,咱不做介紹了,這裡給大家講一下xml的序列化生成和解析。不廢話了,

    android中image檔案的壓縮解壓縮

    我們將android原始碼進行編譯之後,會在out/target/product/<product-name>/目錄下生成幾個image檔案:system.img,recovery.img,userdata.img等,將這些image檔案連同一些bin檔案燒寫到

    Android之引用其它已安裝或未安裝apk檔案資源

    Android應用有時候會涉及到面板的更換問題,在這裡,我用一種引用其它已安裝或未安裝apk檔案的資源來說明。 其核心思想就是利用反射來獲取。 a、引用其它未安裝apk檔案的資源來說明        1、首先建立一個application(StyleClient),將其打

    UnityAndroid——Androidmanifest.xml檔案的介紹

    說明: 在Unity開發移動平臺相關應用程式時,難免會涉及到一些必要的外掛(如:社會化分享外掛ShareSDK、Umeng;增強現實開發Vufoia;掃描二維碼外掛等一些列),每一種外掛分開使用時特別好用,無需特殊配置,使用Example案例就能快速上手使用。然後,當有時同

    Android記憶體洩漏(執行緒造成的記憶體洩漏資源未關閉造成的記憶體洩漏)

    一.執行緒造成的記憶體洩漏 對於執行緒造成的記憶體洩漏,也是平時比較常見的,leakCanary官方Demo就是執行緒成造成的記憶體洩漏,使用了AsyncTask去執行非同步執行緒,現在我們換個

    android 原始碼的m、mm、mmm編譯命令的使用重新打包android系統映象檔案

    一、android 原始碼的m、mm、mmm編譯命令的使用 m:編譯整個安卓系統      makes from the top of the tree mm:編譯當前目錄下的模組,當前目錄下需要有Android.mk這個makefile檔案,否則就往上找最近的A

    ndk開發中的Android.mk檔案Application.mk詳解及例項

    Android.mk檔案的作用: An Android.mk file is written to describe your sources to the build system. 中文意思是:寫一個Android.mk檔案是為了向生成系統描述你的原始碼。

    java在gradle工程訪問src/test/resources目錄下的資源配置檔案

    package com.jiepu; import java.io.File; import java.net.URISyntaxException; import java.util.Map; im