是时候来一波逆向技术分析了之Android Resources.arsc

简介: 近日,我国在中国文昌航天发射场,用长征五号遥四运载火箭成功发射首次火星探测任务天问一号探测器,火箭飞行约2167秒后,成功将探测器送入预定轨道,开启火星探测之旅,迈出了我国行星探测第一步

近日,我国在中国文昌航天发射场,用长征五号遥四运载火箭成功发射首次火星探测任务天问一号探测器,火箭飞行约2167秒后,成功将探测器送入预定轨道,开启火星探测之旅,迈出了我国行星探测第一步

/ 前言 /

gradle 中,配置如下代码可以将无用的资源移除:

android {
    ...
    buildTypes {
        release {
            shrinkResources true
            minifyEnabled true
        }
    }
}

“shrinkResources”资源压缩功能,它需要配合ProGurad的“minifyEnabled”功能同时使用。

如果ProGuard把部分无用代码移除,这些代码所引用的资源也会被标记为无用资源,然后通过资源压缩功能将它们移除。

这个看起来很不错,但是实际上却有些待改进的地方。

对于一些无用的 String、ID、Attr、Dimen 等资源,实际上还存在于 .arsc 文件中。

对于Drawable、Layout这些无用资源,shrinkResources也没有真正把它们删掉,而是仅仅替换为一个空文件。

还是用例子说话吧,我们写一个初始demo(为了减少资源,这里连一个依赖库都不引入,appcompat 也不要),里面有些无用的资源:

strings.xml

<resources>
    ...
    <string name="unused_string">这是个无用的字符串</string>
</resources>

activity_old_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="old main activity" />
</android.support.constraint.ConstraintLayout>

build.gradle

buildTypes {
    debug {
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
    release {
        shrinkResources true
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
}

我们这里,有一个无用的字符串:unused_string,有一个无用布局文件:activity_old_main,可以打 debug 与 release 包,release 包配置了混淆,先使用 010Editor 分析一下 debug 包的 .arsc 文件,看看这两个文件的信息是否存在。

/ 字符串池 /

可以看到,这个无用文件的路径是存在的。路径存在字符串池中,这里储存了资源路径与strings.xml中定义的字符串等等一些资源。

/ 资源名称字符串池 /

在package中,也是有这两个资源的数据,它们也是在字符串池中,注意与上面的字符串池的区别。

/ 资源类型字符串池 /

这里的字符串池储存的是资源的 类型 与 名称。我们展开 typeStrings 可以看到类型:

/ ResTable_type /

注意到,string 是 strdata 数组第 6+1 个元素,所以,我们展开 ResTable_typeSpec typeSpec[6] 后面的 typeType,可以看到:

这里可以看到,我们定义的 app_name,unused_string,但是他们的值却看不到,我刚开始以为是模板出错了,后来想了一下,可能是因为如果是图片等资源的话,根本就解析不出来。

所以这里的 value 应该是指向的第一个字符串池,里面储存的是资源路径,拿到资源路径后再去加载资源。理论上值就是上面字符串池中的索引引用。

layout 也是一样的,这里就不贴图了。有的 typeType 会有多个,是因为不同配置的原因:

可以看到,这里 mipmap 有6个,是因为我们的 res 目录里面就有 6 个 mipmap 文件夹,其作用就是用作适配的。不同的手机会从不同的结构体中读取资源。

/ ResTable_entry与Res_value /

还是要重点说一下, ResTable_entry 与 Res_value,最简单的理解就是:

<bool name="abc_action_bar_embed_tabs">false</bool>

ResTable_entry 里面存了 abc_action_bar_embed_tabs,但是不是直接存的,存的是上面字符串池的字符索引。

Res_value 里面存了当前资源的类型,我们需要先要搞清楚它的类型,然后,再去 data 里面去拿资源路径。

我们拿,app_name 来说明一下,arsc 是如何根据 resourceId 来获取资源的。


我们可以看到,该资源的类型 id 为 7。其 entry 的偏移为 0(它是 entry 数组的第 1 项)。

所以它的 resourceId 为 0x7F070000。那么这个是怎么得出来的呢?

首先,7F 是 packageId,包的命名空间,取值范围为[0x01, 0x7F],一般第三方应用均为7F,系统的为 01。
07 是资源类型id,这里代表的是 string。
0000 就是在 entry 中的偏移。

它的data为0,指向的是第一个字符串池中的第1项,其内容是 Sample。

OK,这里我们对 arsc 应该有一定的了解了,再回到主题,看看 release 包有哪些变化呢?

这里,我就不贴图了,结果就是 .arsc 文件是一样的,连大小都一样。但是我们去 res 目录里面发现,activity_old_main.xml 文件变了。

debug 包里面的文件,有 540 个字节,但是 release 包里面的文件只有 104 个字节,使用编辑器打开,发现 release 包里面的文件数据都是 null,与上面的提到的类似,变成了一个空文件。

所以,shrinkResources 并没有我们想的那么美好。

/ ResTable_map_entry /

还有,对于 style 与 attr 来说,他们的储存方式又不一样,为啥呢?从写法就可以看出来:

<attr name="buttonTintMode">
    <enum name="src_over" value="3"/>
    <enum name="src_in" value="5"/>
    <enum name="src_atop" value="9"/>
    <enum name="multiply" value="14"/>
    <enum name="screen" value="15"/>
    <enum name="add" value="16"/>
</attr>
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
</style>

这种写法,显然不能像 string drawable 一样,搞个键值对,只能使用 map:

这个map 里面没啥东西,是因为我把 AppTheme 里面的东西都删了。

其实,我这里讲的很粗,但是我研究了几天这个文件结构,发现比较重要的东西就是这些了,除非你想对 arsc 做自定义处理,否则的话了解上面的东西也就够了。再深入的话,需要去查看对应的 C 结构,然后搞懂每个字段的意义。

老罗的文章讲这个讲的很细,但是对于现在的我来说,看完就忘,因为用不到,所以我暂时只深入到这里吧…

有个额外的东西需要说一下,我特么还在 stackoverflow 上提了问题。

关于字符串常量头的结构:

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

string 与 stringStart 都好理解,style 是个啥,字符串还有样式???

老罗的文章中说,mango b 与 i 就是字符串样式。

注意到第四个字符串“mango”,它实际表示的是一个字符串“mango”,不过它的前三个字符“man”通过b标签来描述为粗体的,而后两个字符通过i标签来描述为斜体的。字符串“mango”来有两个sytle,第一个style表示第1到第3个字符是粗体的,第二个style表示第4到第5个字符是斜体的。

我使用 010Editor 打开后,发现 styleCount 为 1。

相关文章
|
2月前
|
安全 Android开发 iOS开发
安卓与iOS的较量:技术深度对比
【10月更文挑战第18天】 在智能手机操作系统领域,安卓和iOS无疑是两大巨头。本文将深入探讨这两种系统的技术特点、优势以及它们之间的主要差异,帮助读者更好地理解这两个平台的独特之处。
58 0
|
1月前
|
安全 搜索推荐 Android开发
揭秘安卓与iOS系统的差异:技术深度对比
【10月更文挑战第27天】 本文深入探讨了安卓(Android)与iOS两大移动操作系统的技术特点和用户体验差异。通过对比两者的系统架构、应用生态、用户界面、安全性等方面,揭示了为何这两种系统能够在市场中各占一席之地,并为用户提供不同的选择。文章旨在为读者提供一个全面的视角,理解两种系统的优势与局限,从而更好地根据自己的需求做出选择。
96 2
|
1月前
|
安全 搜索推荐 Android开发
揭秘iOS与安卓系统的差异:一场技术与哲学的较量
在智能手机的世界里,iOS和Android无疑是两大巨头,它们不仅定义了操作系统的标准,也深刻影响了全球数亿用户的日常生活。本文旨在探讨这两个平台在设计理念、用户体验、生态系统及安全性等方面的本质区别,揭示它们背后的技术哲学和市场策略。通过对比分析,我们将发现,选择iOS或Android,不仅仅是选择一个操作系统,更是选择了一种生活方式和技术信仰。
|
2月前
|
安全 Android开发 iOS开发
iOS与安卓:技术生态的双雄争霸
在当今数字化时代,智能手机操作系统的竞争愈发激烈。iOS和安卓作为两大主流平台,各自拥有独特的技术优势和市场地位。本文将从技术架构、用户体验、安全性以及开发者支持四个方面,深入探讨iOS与安卓之间的差异,并分析它们如何塑造了今天的移动技术生态。无论是追求极致体验的苹果用户,还是享受开放自由的安卓粉丝,了解这两大系统的内在逻辑对于把握未来趋势至关重要。
|
2月前
|
安全 搜索推荐 Android开发
揭秘iOS与Android系统的差异:一场技术与哲学的较量
在当今数字化时代,智能手机操作系统的选择成为了用户个性化表达和技术偏好的重要标志。iOS和Android,作为市场上两大主流操作系统,它们之间的竞争不仅仅是技术的比拼,更是设计理念、用户体验和生态系统构建的全面较量。本文将深入探讨iOS与Android在系统架构、应用生态、用户界面及安全性等方面的本质区别,揭示这两种系统背后的哲学思想和市场策略,帮助读者更全面地理解两者的优劣,从而做出更适合自己的选择。
|
2月前
|
安全 Android开发 iOS开发
安卓vs iOS:探索两种操作系统的独特魅力与技术深度###
【10月更文挑战第16天】 本文旨在深入浅出地探讨安卓(Android)与iOS这两种主流移动操作系统的特色、优势及背后的技术理念。通过对比分析,揭示它们各自如何塑造了移动互联网的生态,并为用户提供丰富多彩的智能体验。无论您是科技爱好者还是普通用户,都能从这篇文章中感受到技术创新带来的无限可能。 ###
58 2
|
2月前
|
机器学习/深度学习 人工智能 Android开发
安卓与iOS:技术演进的双城记
【10月更文挑战第16天】 在移动操作系统的世界里,安卓和iOS无疑是两个最重要的玩家。它们各自代表了不同的技术理念和市场策略,塑造了全球数亿用户的移动体验。本文将深入探讨这两个平台的发展历程、技术特点以及它们如何影响了我们的数字生活,旨在为读者提供一个全面而深入的视角,理解这两个操作系统背后的哲学和未来趋势。
36 2
|
1月前
|
搜索推荐 安全 Android开发
安卓与iOS的哲学对话:技术生态中的选择与命运
【10月更文挑战第24天】 在智能设备的世界里,安卓和iOS不仅是操作系统的简单对立,它们代表了不同的技术哲学和生态策略。本文将探讨这两种系统背后的设计理念、用户体验差异以及它们如何塑造我们的数字生活,从而引发对于“我们如何选择技术”这一命题的深入思考。
|
1月前
|
安全 5G Android开发
安卓与iOS的较量:技术深度解析
【10月更文挑战第24天】 在移动操作系统领域,安卓和iOS无疑是两大巨头。本文将深入探讨这两个系统的技术特点、优势和不足,以及它们在未来可能的发展方向。我们将通过对比分析,帮助读者更好地理解这两个系统的本质和内涵,从而引发对移动操作系统未来发展的深思。
51 0
|
2月前
|
Android开发 Swift 数据安全/隐私保护
安卓与iOS的较量:一场永无止境的技术竞赛
【10月更文挑战第14天】 在智能手机操作系统的战场上,安卓和iOS一直是两大主角。它们各自拥有独特的优势和特性,吸引了全球数以亿计的用户。本文将深入探讨这两个系统的发展历程、技术特点以及它们之间的竞争关系,带您领略这场永无止境的技术竞赛的魅力。
49 0