Android 混淆

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

Android 应用在发布时一般都会做代码混淆,具体就是在 build.gradle 中配置

// 缩减代码
minifyEnabled true

作用一是增加反编译的难度,二是缩减应用大小,下面来看一下混淆的具体原理


以下是本篇文章正文内容

一、ProGuard、DX、D8、R8

Android 打包流程
在上面 Android 打包流程图中会把项目和依赖库中的 Class 文件经过 dex 编译器编译为 dex 文件,在编译之前会先经过混淆和脱糖

ProGuard&D8
在 Android gradle plugin 3.4.0 之前默认编译过程是这样的,D8 是新一代的 dex 编译器在 3.1.0 之前是 DX 编译器

R8&D8
在 Android gradle plugin 3.4.0 及更高版本是这样的,R8 集成了混淆和 dex 编译器 D8

  • ProGuard 压缩、优化和混淆 Java 字节码文件的免费工具
  • R8 专为 Android 项目设计支持所有现有 ProGuard 规则文件
  • DX 老版本的 dex 编译器
  • D8 执行脱糖,并将 class 文件转换为 dex 文件,与老牌编译器 DX 相比,D8 运行得更快,生成的 dex 文件更小,运行时性能相当或更好

混淆在 AGP3.2.0 及之前是 ProGuard ,3.2.0 支持 R8,3.4.0 及之后默认使用 R8
dex 编译器在 AGP3.0.0 及之前使用 DX,3.0.0 支持 D8,3.1.0 及之后默认使用 D8

二、缩减、混淆处理和优化应用

混淆这个叫法其实并不准确,因为开启混淆之后 R8 做了以下几件事:

  • 代码缩减(即摇树优化):从应用及其库依赖项中检测并安全地移除不使用的类、字段、方法和属性(这使其成为了一个对于规避 64k 引用限制非常有用的工具)。例如,如果您仅使用某个库依赖项的少数几个 API,那么缩减功能可以识别应用不使用的库代码并仅从应用中移除这部分代码
  • 从封装应用中移除不使用的资源,包括应用库依赖项中不使用的资源。此功能可与代码缩减功能结合使用,这样一来,移除不使用的代码后,也可以安全地移除不再引用的所有资源
  • 缩短类和成员的名称,从而减小 DEX 文件的大小
  • 检查并重写代码,以进一步减小应用的 DEX 文件的大小。例如,如果 R8 检测到从未采用过给定 if/else 语句的 else {} 分支,则会移除 else {} 分支的代码

三、ProGuard 规则文件来源

R8 延续了 ProGuard 使用规则文件修改默认行为的做法。在很多时候,规则文件也被称为混淆保留规则文件,这是因为该文件内定义的绝大多数规则都是和代码混淆相关的。事实上,文件内还可以定义代码压缩、优化和预校验规则,因此称为 ProGuard 规则文件比较严谨


上面一段文字说明来自参考&感谢

ProGrard 规则文件有多个来源:

来源位置说明
Android Studio<module-dir>/proguard-rules.pro当使用 Android Studio 创建新模块时,Android Studio 会在该模块的根目录中创建 proguard-rules.pro 文件。默认情况下此文件不会应用任何规则,应在此添加自己的proguard 规则
Android Gradle 插件由 Android Gradle 插件在编译时生成Android Gradle 插件会生成 proguard-android-optimize.txt(其中包含了对大多数 Android 项目都有用的规则)它指定了与 @Keep 注解相关的所有保留规则,这里就解释了为什么使用 @Keep 修饰的成员不会被混淆了
依赖库AAR: <library-dir>/proguard.txt
JAR: <library-dir>/META-INF/proguard/
如果某个 AAR 库是使用它自己的 ProGuard 规则文件发布的,并且将该 AAR 库作为编译时依赖项纳入到项目中,那么 R8 在编译项目时会自动应用其规则
AAPT2<module-dir>/build/intermediates/aapt-proguard-rules/${变体-buildTypes}/aapt_rules.txtAAPT2 会根据对应用清单中的类、布局及其他应用资源的引用,生成保留规则。例如,AAPT2 会为您在应用清单中注册为入口点的每个 Activity 添加一个保留规则(因为 Activity 的实例都是通过反射创建的)

另外也可以通过 proguardFile 给不同的变体配置不同的规则文件

    productFlavors {
        flavor1 {
            ...
        }
        flavor2 {
            proguardFile 'flavor2-rules.pro'
        }
    }

R8 会将来自上述所有可用来源的规则组合在一起,将以下代码添加到模块的 proguard-rules.pro 文件中可输出 R8 在构建项目时应用的所有规则的完整报告

// 输出路径和文件名可以更改
-printconfiguration build/intermediates/proguard-files/full-r8-config.txt

我测试在开启混淆后 Android Gradle 插件会生成 proguard-android.txt、proguard-defaults.txt、proguard-android-optimize.txt 三个文件,其中 defaults 与 android-optimize 一样都开启了代码优化 android 没有开启代码优化,选择其中一个使用就可以

四、模块混淆

不管 Module 的 minifyEnabled 设置 true 或 false 在 proguard-rules.pro 中配置的混淆规则都不生效,只有通过 consumerProguardFiles 配置的混淆规则才生效并且最后会合并到整个 App 的混淆规则中对整个项目生效

    defaultConfig {
        consumerProguardFiles "consumer-rules.pro"
    }

上面是我的测试结果跟 ‘参考&感谢’ 中的结论不一致

五、异常堆栈信息去混淆

项目开启混淆后发生 crash 时堆栈信息也是混淆过的不方便排查定位,这个时候就需要对混淆信息去混淆,首先在混淆规则文件里定义:

# 输出 mapping.txt 文件
-printmapping ./build/outputs/mapping/mapping.txt

然后在 SDK 路径下 android-sdk/tools/proguard/bin/ 下有 proguard.sh 和 proguardgui.sh 前者是命令行方式后者是用户界面的方式,第一种:

SDK 路径/tools/proguard/bin/retrace.sh <mapping.txt 文件路径> <异常堆栈文件.txt路径>

之后就可以得到去混淆后的堆栈信息,第二种双击 proguardgui.sh 选择 mapping.txt 文件路径输入异常堆栈信息点击 retrace 即可

六、混淆字典

混淆后默认使用英文字母替换源代码但也可以通过定位混淆字典把代码混淆我其他特殊字符或者是汉字,配置混淆字典:

# 混淆字典
-obfuscationdictionary ../proguard-dictionary.txt
-classobfuscationdictionary ../proguard-dictionary.txt
-packageobfuscationdictionary ../proguard-dictionary.txt

通过反编译混淆后的 apk 即可以看到效果,以下是几个混淆字典开源库:

ProguardDictionary
ProguardDictionaryGenerator
FrenziedProguard


上面两部分内容使用公司项目测试所以没有添加截图

七、资源压缩

通过以下配置即可开启资源压缩,开启资源压缩时需要先开启代码压缩,因为在移除不需要的代码后才可以确定哪些资源也是不需要的

// 资源压缩
shrinkResources true

如果有要保留和舍弃的特定资源文件,可以在项目中创建一个包含 <resources> 标记的 XML 文件,并在 tools:keep 属性中指定每个要保留的资源,在 tools:discard 属性中指定每个要舍弃的资源。这两个属性都接受以逗号分隔的资源名称列表,可以将星号字符用作通配符

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"
    tools:discard="@layout/unused2" />

默认情况下资源压缩可以准确的判断是否使用了某个资源,如果代码中使用了 Resources.getIdentifier() 动态生成字符串查找资源,资源收缩器会把名称匹配的资源标记为已使用不删除。例如,以下代码会将所有带 img_ 前缀的资源标记为已使用

val name = String.format("img_%1d", angle + 1)
val res = resources.getIdentifier(name, "drawable", packageName)

可以通过在上面定义保留和舍弃资源的 xml 文件中将 shrinkMode 设为 strict 指定资源缩减器只保留确定要使用的资源

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:shrinkMode="strict" />

如果启用了严格缩减模式,并且代码通过动态生成的字符串引用资源,必须使用 tools:keep 属性手动保留这些资源

八、保留指定资源

默认情况下 Gradle 会将所有尺寸所有语言等各种维度的资源全部打入 APK 增大了 APK 体积,同一类资源通常会选择性的打入 APK 以减少包体积,例如:

android {    
	defaultConfig {        
		// ...        
		resConfigs "zh", "xxhdpi", "ldltr", "desk"    
	}
}

有些依赖库包含了很多其它非中文资源,所以可以添加了 zh 配置用于只保留中文资源
添加 xxhdpi 配置用于只保留一套图片资源,例如如果有 xhdpi 和 xxhdpi 的两套图片,会只保留 xxhdpi 的图片,如果有 hdpi 和 xhdpi 两套图片,会只保留 xhdpi 的图片
ldltr 指定方向性资源上只包含左到右的资源文件
如果应用没有适配 watch 手表,可以通过添加 desk 配置将 watch 相关资源移除


上面这一部分内容没有验证

九、AndResGuard

Android 本身只支持资源压缩(移除无用资源)不支持资源混淆,这部分可以使用 AndResGuard 来完成,它的原理就是创建了一个资源混淆打包任务,该任务会先调用默认的打包任务,在默认打包工作结束后,会解压打好的 apk 包,识别解析包里的 resources.arsc 资源表,然后再混淆 res 文件夹下面的所有资源文件,同时相对应的修改资源表,最后将修改后的资源重新打包签名,生成新的 apk 包。

参考与感谢

缩减、混淆处理和优化应用
Android Gradle 插件版本说明
补齐Android技能树 - 从害怕到玩转Android代码混淆
Android | 代码混淆到底做了什么?| 牛气冲天新年征文
58同城厂商内置包大小减少实战
采用AndResGuard进行资源混淆

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android混淆是一种通过对代码进行重命名和优化来增加应用程序安全性和性能的技术。在Android开发中,可以使用自带的混淆工具来进行代码混淆。默认情况下,Android SDK提供了一些默认的混淆文件,如proguard-android.txt或proguard-android-optimize.txt。这些文件包含了一些常用的混淆命令,可以对代码进行混淆处理。如果需要对自定义的混淆进行配置,可以在proguard-rules.pro文件中进行设置。\[1\] 在进行混淆时,需要注意不要混淆Activity中参数是View的方法。因为在Android开发中,有一种常见的用法是在XML中配置android:onClick属性,当用户点击按钮时,会调用Activity中的对应方法,例如buttonClick(View view)。如果这个方法被混淆,就无法找到对应的方法了。为了避免这种情况,可以使用如下混淆命令来保留Activity中参数是View的方法:-keepclassmembers class * extends android.app.Activity { public void *(android.view.View); }\[2\] 此外,还可以使用一些通用的混淆命令来保留Android中的一些常用类,例如Activity、Application、Service、BroadcastReceiver和ContentProvider:-keep public class * extends android.app.Activity -keep public class * extends android.app.Application -keep public class * extends android.app.Service -keep public class * extends android.content.BroadcastReceiver -keep public class * extends android.content.ContentProvider\[3\] 通过使用混淆技术,可以有效地保护Android应用程序的代码安全性,并提高应用程序的性能。 #### 引用[.reference_title] - *1* [Android:代码混淆概念整理](https://blog.csdn.net/qjyws/article/details/126488356)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Android--混淆配置(比较详细的混淆规则)](https://blog.csdn.net/weixin_42602900/article/details/127671586)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值