手把手教你解析Resources.arsc

一、前言

对于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>

代码段1

这里先介绍几个概念,上面的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相对应的),首先看一下文件的格式,如下面两个图:


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

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

3. 文件header

Resources.arsc文件的最开始是整个文件的header,结构是ResTable_header:

 struct ResTable_header
 {
     struct ResChunk_header header;
 
     // The number of ResTable_package structures.
     uint32_t packageCount;
};

代码段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!都存在这里。

字符串池的结构体如下:

  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;
};

代码段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,结构体如下:

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;
 };

代码段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。

对于类型字符串池和资源项名称字符串池的结构和内容我这里就不贴出来了,结构和全局字符串池是一样的。类型字符串池中存储的是所有类型相关的字符串,比如attr,drawable,layout这些;而资源项名称字符串池中存储的是应用所有资源文件中的资源项名称相关的字符串,比如代码段1中的app_name,hello_world,action_settings。


今天先写到这里,明天再更新。。。



参考文章:

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

本页内容版权归属为原作者,如有侵犯您的权益,请通知我们删除。
一、IMS开机初始化 (如果图片看不清的话,可以右键选择在查看图片,或者把图片另存到自己电脑再查看。) 本 文 来 自 http://blog.csdn.net/linyongan , 转 载 请 务 必 注 明 出 处 。 1.1 监控IMS Service PhoneApp进程是在系统开机时启动的,Phone进程初始化的时候(步骤1~6),在创建GSMPhone或者CDMAPhone之后,会执行监控IMS Service的流程,也就是流程图上的 步骤7 ,在PhoneFactory. Java 的ma
怎样防止App在后台运行,点击App桌面的图标重新启动?            在项目中,遇到一个问题百思不得其解,那就是:我在app使用过程中,点击了home键,然后去看看微信之类的其他应用,这个时候再点击app桌面的图标,这个时候app是重新启动的,而不是从上次停止的界面开始的。            对于上面的情况,我觉得既然我的app已经在后台还运行着,为什么就不能继续重上一个界面继续运行,非得从新运行呢。然后我就去查资料解决了这个问题。首先讲讲这个现象的本质。            原因:当点击
1、ZIP文件目录遍历简介 因为ZIP压缩包文件中允许存在“../”的字符串,攻击者可以利用多个“../”在解压时改变ZIP包中某个文件的存放位置,覆盖掉应用原有的文件。如果被覆盖掉的文件是动态链接so、dex或者odex文件,轻则产生本地拒绝服务漏洞,影响应用的可用性,重则可能造成任意代码执行漏洞,危害用户的设备安全和信息安全。比如近段时间发现的“寄生兽”漏洞、海豚浏览器远程命令执行漏洞、三星默认输入法远程代码执行漏洞等都与ZIP文件目录遍历有关。 阿里聚安全的应用漏洞扫描服务,可以检测出应用的ZIP文

浅谈Android中的MVP - 2016-06-24 17:06:24

转载请标明出处: http://blog.csdn.net/hai_qing_xu_kong/article/details/51745798 本文出自: 【顾林海的博客】 前言 为什么使用MVP,网上有很多说法,最主要就是减轻了Activity的责任,相比于MVC中的Activity承担的责任太多,因此有必要讲讲MVP。 MVP入门 在MVC框架中,View是可以直接读取Model模型中的数据的,Model模型数据发生改变是会通知View数据显示发生相应的改变。而在MVP中Model和View之间的没有
阅读此文前请先阅读 Retrofit+okhttp网络框架介绍 从上文中我们已经了解通过如下代码即可得到返回给我们call 以及 response对象,今天我们通过源码来分析这个过程是如何实现的。 /** * 获取天气数据 * @param cityname * @param key * @return */ @GET ( "/weather/index" ) CallWeatherData getWeatherData( @Query ( "format" ) String format, @Query
一、Block 的类型 根据 Block 在内存中的位置分 为三种类型 NSGlobalBlock , NSStackBlock, NSMallocBlock 。 NSGlobalBlock :类似函数,位于 text 段; NSStackBlock :位于 栈内存,函数返回后 Block 将无效; NSMallocBlock :位于堆内存。 二、Block 的 copy 、 retain 、 release 操作   不同于 NSObjec 的 copy 、 retain 、 release 操作: B
前言 module 怎能少得了动画呢~ 代码解读 weex code API 接口 transition (node, options, callback) Arguments 参数node(Node):将要动画的元素。options( object ):操作选项styles( object ):指定要应用的过渡效果的样式的名称和值。color( string ):色彩的元素时,animaiton完成。transform( object ):变换函数被应用到元素。支持下列值。translate/ tran
目录 概述 这是一个关于 RecycleView 滑动事件的辅助类,该辅助类可以检测 RecycleView 滑动到顶部或者底部的状态. 可用于实现 RecycleView 加载更多或者刷新(虽然刷新可以直接用 SwipeRefreshLayout ).也可用于某些滑动相关的需求,如 FloatingActionButton 的隐藏与显示之类的. 关于 RecycleView 的滑动监听 RecycleView 本身已经提供了滑动的监听接口, OnScrollListener ,这个接口包含了以下的方法.
这个小案例建议在手机上运行。 package com.example.camera;import java.io.File;import android.net.Uri;import android.os.Bundle;import android.os.Environment;import android.provider.MediaStore;import android.app.Activity;import android.content.Intent;import android.view.Me
简介 NSScanner是一个类,用于在字符串中扫描指定的字符,尤其是把它们翻译/转换为数字和别的字符串。可以在创建NSScaner时指定它的string属性,然后scanner会按照你的要求从头到尾地扫描这个字符串的每个字符。 NSScanner官方文档 NSScanner类是一个类簇的抽象父类,该类簇为一个从NSString对象扫描值的对象提供了程序接口。 NSScanner对象把NSString 对象的的字符解释和转化成 number和string 类型的值。在创建NSScanner对象的时候为它分