其实Android代码混淆原理及作用解析的问题并不复杂,但是又很多的朋友都不太了解,因此呢,今天小编就来为大家分享Android代码混淆原理及作用解析的一些知识,希望可以帮助到大家,下面我们一起来看看这个问题的分析吧!
目录
1. 混淆编译器
如果按照混淆编译器来划分,Android代码混淆可以分为以下两个时期:
ProGuard:通用Java字节码优化工具,由比利时团队开发GuardSquare R8:ProGuard的继承者,专为Android设计,具有更好的编译性能和更好的编译产品。下图整理了随着Android Gradle Plugin版本迭代它们对应的变化。所做的更改:
Android Gradle Plugin版本迭代,令人困惑的编译器变化:
古代:ProGuard3.2.0:ProGuard(默认)、R8(引入) 3.4.0:R8(默认) 其中:DEX编译器变化:
远古时代:DX3.0.0:DX(默认)、D8(引入) 3.1.0:D8(默认) 如果需要修改Android Gradle Plugin的默认行为,可以在gradle.properties中添加配置:
启用和禁用R8# 显式启用R8
android.enableR8=true# 1. 仅针对Android 库模块禁用R8 编译器
android.enableR8.libraries=false
# 2. 禁用所有模块的R8 编译器
android.enableR8=false 启用和禁用D8# 显式启用D8
android.enableD8=true# 显式禁用D8
android.enableD8=false 此外,如果在应用程序模块的build.gradle 文件中设置useProguard=false,则将使用R8 编译器而不是ProGuard。
2. 四大功能
ProGuard和R8都提供四大功能:收缩器、优化器、混淆器和预验证器:
压缩(也称为tree shake):从应用及依赖项中删除未使用的类、方法和字段,以帮助避免64个方法的瓶颈
优化:删除更多未使用的代码,甚至通过代码分析重写代码
混淆:对重命名类/方法/字段使用无意义的短名称,增加逆向工程的难度
预校验:对于面向Java 6或Java 7 JVM的类文件,可以在编译时将预校验信息添加到类文件(StackMap和StackMapTable属性)中,以加快类加载效率。 Java 7 JVM 需要预验证,但对Android 平台没有影响
使用ProGuard时,部分编译流程如下图所示:
ProGuard 对.class 文件执行代码压缩、优化和混淆。 D8编译器执行脱糖并将.class文件转换为.dex文件。使用R8时,部分编译过程如下图所示:
R8 将脱糖(Desugar)、压缩、优化、混淆和 dex(D8 编译器)整合到一个步骤R8 对.class 文件执行代码压缩、优化和混淆。 D8编译器执行脱糖并将.class文件转换为.dex文件。 ProGuard与R8对比如下:
共同点:1.开源
2. R8支持所有现有的ProGuard规则文件
3. 两者都提供四大功能:压缩,优化,混淆,预校验不同点:1. ProGuard可以用于Java项目,而R8是专门为Android项目设计的
2. R8将脱糖、压缩、优化、混淆和dex(D8编译器)集成到一个步骤中,显着提高编译性能
关于 D8 编译器
将 .class 字节码转化为 .dex 字节码的过程被称为 DEX 编译,最初由DX编译器完成。与DX编译器相比,新的D8编译器的编译速度为更快,输出.dex文件为更小,但可以保持相同甚至更出色的应用程序运行时性能
3. 使用示例
无论使用 R8 还是 ProGuard,默认不会启用压缩、优化和混淆功能。这样的设计主要基于两个考虑:一方面是因为这些编译时任务会增加编译时间,另一方面是因为如果混淆保留规则没有完全定义,运行时会出现错误也可介绍。因此,最好使用只在应用的测试版本和发布版本中启用这些编译时任务,参考使用示例:
//构建.gradle
.
安卓{
构建类型{
//测试版本
预览{
//启用代码压缩、优化和混淆(由R8 或ProGuard 执行)
minifyEnabled true
//启用资源压缩(由Android Gradle 插件执行)
收缩资源true
//指定混淆保留规则文件
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
}
//发布版本
发布{
minifyEnabled true
收缩资源true
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
}
//开发版本
调试{
minifyEnabled false
}
}
.
}minifyEnabled:(默认)启用代码压缩、优化、混淆和预验证shrinkResources:启用资源压缩proguardFiles、proguardFile:指定ProGuard规则文件,前者可以指定多个参数。下面两段配置效果相同。 //方法一:
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
//方法2:
proguardFile getDefaultProguardFile("proguard-android-optimize.txt")
前面提到的proguardFile "proguard-rules.pro":无论使用R8还是ProGuard,压缩、优化和混淆函数都是默认关闭。可以通过以下配置进行灵活控制:
整体关闭minifyEnabled 假
//这行没有任何作用
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"关闭压缩-dontshrink关闭优化(R8无效) -dontoptimize 注意:R8 不能关闭优化,也不允许修改优化的行为,实际上R8会忽略修改默认优化行为规则。例如,设置-optimizations 和-optimizationpasses 后,您将收到编译时警告:
AGPBI: {"kind":"警告","text":"忽略选项: -优化","sources":[{"file":"省略."}],"tool":"D8"}
AGPBI: {"kind":"warning","text":"忽略option: -optimizationpasses","sources":"省略."}],"tool":"D8"}关闭混淆(开发版推荐关闭混淆) -dontobfuscate关闭预校验(Android平台无效,建议关闭) -dontpreverify
4. ProGuard 规则文件
R8 延续了 ProGuard 使用规则文件修改默认行为的做法。很多情况下,规则文件也叫混淆保留规则文件,因为绝大多数规则该文件中定义的规则都是与代码混淆相关的。实际上,文件中还可以定义代码压缩、优化和预验证规则,因此更严格地称为ProGuard 规则文件。
在上一节中,我们提到使用proguardFiles 和proguardFile 来指定ProGuard 规则文件。对于任何项目,其ProGuard 规则文件都来自以下三个来源:
1、Android Gradle 插件编译时,Android Gradle插件会生成proguard-android-optimize.txt、proguard-android.txt,位于/build/intermediates/proguard-files/。除了注释之外,这两个文件唯一的区别是前者启用了代码压缩,而后者关闭了代码压缩,如下所示:
# proguard-android-optimize.txt
-优化!代码/简化/算术,代码/简化/转换,字段/*,类/合并/*
-优化5次
-允许访问修改
相同部分省略.# proguard-android.txt
-不优化
相同部分省略……混淆规则的相同部分中,以下部分比较特殊:
-keep 类android.support.annotation.Keep
-keep 类androidx.annotation.Keep
//保留@Keep注解的类并保留.TODO
-keep @android.support.annotation.Keep 类* {*;}
-keep @androidx.annotation.Keep 类* {*;}
//保留@Keep修饰的方法
-keepclasseswithmembers 类* {
@android.support.annotation.Keep;
}
-keepclasseswithmembers 类* {
@androidx.annotation.Keep;
}
//保留@Keep修改的字段
-keepclasseswithmembers 类* {
@android.support.annotation.Keep;
}
-keepclasseswithmembers 类* {
@androidx.annotation.Keep;
}
//保留@Keep修改的构造函数
-keepclasseswithmembers 类* {
@android.support.annotation.Keep(.);
}
-keepclasseswithmembers 类* {
@androidx.annotation.Keep(.);
它指定与@Keep注释相关的所有保留规则。这就解释了为什么用@Keep修改的成员不会混淆了吧?
2、Android Asset Package Tool 2 (AAPT2)在编译时,AAPT2 根据清单中对类、布局和其他应用程序资源的引用生成aapt_rules.txt,位于/build/intermediates/proguard-rules/debug/aapt_rules.txt。
例如,AAPT2 为清单中注册的每个组件添加保留规则:
引用于[项目路径]/app/build/intermediates/merged_manifests/release/AndroidManifest.xml:19
-保持类com.have.a.good.time.MainActivity {(); }
略.这里,AAPT2生成了MainActivity的保留规则,同时也指出了参考来源:AndroidManifest.xml:19。这是因为在启动一个Activity的过程中,需要使用反射来实例化每个具体的Activity。有兴趣的话可以看看ActivityThread#performLaunchActivity()-Instrumentation#newActivity()
3、Module创建新模块时,IDE 在模块的根目录中创建一个proguard-rules.pro 文件。当然,除了这个自动生成的文件之外,还可以根据需要创建额外的规则文件。例如,以下配置添加一个额外的规则文件用于发布:
.
安卓{
.
构建类型{
发布{
minifyEnabled true
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
}
}
产品风味{
开发{
.
}
发布{
proguardFile "release-rules.pro"
}
}
}
.总结一下:
规则文件来源说明Android Gradle 插件编译时,AAPT2 由Android Gradle 插件生成。编译时,AAPT2 根据对应用程序清单中的类、布局和其他应用程序资源的引用生成保留规则。模块是由IDE 在创建新模块时创建的。或者如果minifyEnabled 属性设置为true(ProGuard 或 R8 会将来自上面列出的所有可用来源的规则组合在一起),则根据需要另外创建。为了查看完整的规则文件,您可以在proguard-rules.pro 中添加以下配置,以输出编译项目时应用的所有规则的完整报告:
-printconfiguration build/intermediates/proguard-files/full-config.txt
5. 组件化混淆
在组件化项目中,需要关注application Module和Library Module的行为差异以及组件化的资源聚合规则,总结如下以下要点:
编译时,各层Library Module会依次编译。最底层的Base Module会先被编译成aar文件。那么上层编译时,依赖于Module输出的aar文件/jar文件就会相应解压到模块的build中。当文件夹中的App Module层汇总完所有aar文件后,编译操作才真正开始。编译后的Module会覆盖之前编译的Module中的同名资源。组件化资源汇总Lib Module汇总为App Module,并使用更高版本。 Android Gradle 插件不会将聚合资源放置在exploded-aar 文件夹中。即便如此,将Lib Module 资源聚合到App Module 的规则是相同的。
我们用一个简单的例子来测试不同配置下的混淆结果:
配置一配置二配置三配置四App Module 开启混淆XXBase Module 开启混淆XX 示例程序:App Module 依赖于Base Module。将构建好的apk包拖到Android Studio面板中,分析Base类混淆结果。例如配置一的结果:
使用配置1时,Base类不进行混淆,所有测试结果如下:
配置一配置二配置三配置四App Module 开启混淆XXBase Module 开启混淆XX (结果) Base 类是否被混淆XX 可见,混淆开启由 App Module 决定, 与Lib Module 无关。
现在我们分别在Lib Module和App Module的proguard-rules.pro中添加Base类的混淆保留规则,并在build.gradle中添加配置文件来测试是否可以保留Base类:
-keep class com.rui.base.Base测试结果如下:
配置位置Lib ModuleApp Module(结果)基类保留X可见:(默认情况)混淆规则以 App Module 中的混淆规则文件为准。
这里介绍两种主流的组件化混淆方案:
在 App Module 中设置混淆规则该解决方案将所有混淆规则放置在应用程序模块的proguard-rules.pro 中。这是最简单、最直观的。缺点是移除Lib Module时,需要从App Module中移除相应的混淆规则。冗余的混淆规则虽然不会导致编译错误或运行错误,但仍然会影响编译效率。
很多第三方SDK都采用了这种组件化的混淆方案。在App Module中添加依赖时,还需要在proguard-rules.pro中添加独占混淆规则,以保证release版本正常运行。
在 App Module 中设置公共混淆规则,在 Lib Module 中设置专属混淆规则该方案为Lib Module的proguard-rules.pro设置了专属的混淆规则。但根据之前的测试,Lib Module中设置的混淆规则并没有生效。为了使规则生效,需要在Lib Module的build.gradle中添加以下配置:
.
安卓{
默认配置{
ConsumerProguardFiles "consumer-rules.pro"
}
}consumer-rules.pro 文件:
-keep class com.rui.base.Base 测试结果显示Base类已经被保留。这种使用consumerProguardFiles的方式有以下特点:
ConsumerProguardFiles 仅对Lib Module 有效,对App Module 无效。 ConsumerProguardFiles会将混淆规则输出为proguard.txt文件,并打包成aar文件。 App Module会使用aar文件中的proguard.txt来总结最终的混淆规则。这可以通过前面提到的-printconfiguration证明
6. 总结
ProGuard是一个Java字节码优化工具,而R8是专门为Android设计的,其编译性能和编译产品更好;
ProGuard和R8都提供了四大功能:压缩、优化、混淆和预验证。 ProGuard主要对.class文件进行代码压缩、优化和混淆,然后D8编译器进行脱糖,转换为.dex文件。 R8将压缩、优化、混淆、脱糖、dex合二为一;
ProGuard规则文件来自三个来源:Android Gradle插件、AAPT2和Module;
默认情况下,混淆规则基于应用模块中的混淆规则文件。使用Consumer-rules.pro 文件设置特定于Lib 模块的混淆规则。
【Android代码混淆原理及作用解析】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
我听说代码混淆可以保护程序不被破解,这是真的吗?
有11位网友表示赞同!
我想知道混淆后的代码能不能阅读,对调试有什么影响吗?
有7位网友表示赞同!
平时写程序的时候会用到代码混淆吗?
有19位网友表示赞同!
Android 开发者都喜欢用代码混淆吗?
有13位网友表示赞同!
看标题感觉代码混淆应该是个很重要的技术啊!
有15位网友表示赞同!
学习代码混淆需要掌握哪些知识?推荐点学习资源吧!
有18位网友表示赞同!
我想知道不同类型的 Android 应用程序,它们的代码混淆方法有什么区别吗?
有18位网友表示赞同!
除了保护程序,代码混淆还有其他用途吗?
有19位网友表示赞同!
请问代码混淆会影响程序的性能吗?
有11位网友表示赞同!
我现在是在学 Android 开发,听讲要学习代码混淆,我应该从哪里开始学习呢?
有13位网友表示赞同!
感觉现在有很多开发工具都支持代码混淆,是吗?
有20位网友表示赞同!
有没有什么好的代码混淆平台推荐吗?
有20位网友表示赞同!
代码混淆对程序安全性有哪些方面的提升?
有11位网友表示赞同!
我想知道代码混淆的历史,和它发展到现在经历过哪些变化呢?
有18位网友表示赞同!
学习代码混淆会不会让我在 Android 开发的道路上更上一层楼?
有15位网友表示赞同!
代码混淆对于个人开发项目有什么意义吗?
有7位网友表示赞同!
我想知道在使用代码混淆的时候,需要注意哪些问题?
有11位网友表示赞同!
代码混淆是不是一个很复杂的技术?
有13位网友表示赞同!
学习代码混淆需要多久的时间才能掌握呢?
有19位网友表示赞同!
如果想学习更深入的代码混淆知识,去哪找资料合适?
有12位网友表示赞同!