程序猿阵线联盟-汇总各类技术干货Android开发Android开发

Android | 代码混淆到底做了什么?

2019-10-12  本文已影响0人  彭旭锐

前言

代码混淆对于每个入门的 Android 工程师来说都不会太陌生,因为在编译正式版本时,这是一个必不可少的过程。而且使用代码混淆也相当简单,简单到只需要配置一句minifyEnabled true。但是你是否理解混淆的原理,如果问你代码混淆到底做了什么,你会怎么说?


目录


1. 混淆编译器

如果以混淆编译器来划分的话,Android 代码混淆可以分为以下两个时期:

下图梳理了它们随着 Android Gradle Plugin 版本迭代相应做出的变更:

Android Gradle Plugin 版本迭代

其中,混淆编译器的变更:

其中:DEX编译器的变更:

如果需要修正 Android Gradle Plugin 的默认行为,可以在gradle.properties中添加配置:

另外,如果在应用模块的 build.gradle 文件中设置useProguard = false,也会使用 R8 编译器代替 ProGuard。


2. 四大功能

ProGuard 与 R8 都提供了压缩(shrinker)、优化(optimizer)、混淆(obfuscator)、预校验(preverifier)四大功能:

使用 ProGuard 时,部分编译流程如下图所示:

使用 R8 时,部分编译流程如下图所示:

对比以下 ProGuard 与 R8 :

关于 D8 编译器

将 .class 字节码转化为 .dex 字节码的过程被称为 DEX 编译,最初是由DX 编译器完成。与 DX 编译器相比,新的 D8 编译器的编译速度 更快,输出的 .dex 文件 更小 ,却能保持相同乃至 更出色 的应用运行时性能


3. 使用示例

无论使用 R8 还是 ProGuard,默认不会启用压缩、优化和混淆功能。这个设计主要是出于两方面考虑:一方面是因为这些编译时任务会增加编译时间,另一方面是因为如果没有充分定义混淆保留规则,还可能会引入运行时错误。因此,最好 只在应用的测试版本和发布版本中启用这些编译时任务,参考使用示例:

// build.gradle
...
android {

  buildTypes {

    // 测试版本
    preview {
      // 启用代码压缩、优化和混淆(由R8或者ProGuard执行)
      minifyEnabled true
      // 启用资源压缩(由Android Gradle plugin执行)
      shrinkResources true
      // 指定混淆保留规则文件
      proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    } 

    // 发布版本
    release {
      minifyEnabled true
      shrinkResources true
      proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }

    // 开发版本
    debug{
      minifyEnabled false
    }
  }
...
}

前面提到了:无论使用R8还是ProGuard,压缩、优化和混淆功能都是默认关闭的。通过以下配置可以灵活控制:

minifyEnabled false
// 这行就没有效果了
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
-dontshrink
-dontoptimize

注意:R8 不能关闭优化,也不允许修改优化的行为,事实上,R8 会忽略修改默认优化行为的规则。例如设置 -optimizations-optimizationpasses后会得到编译时警告:

AGPBI: {"kind":"warning","text":"Ignoring option: -optimizations","sources":[{"file":"省略..."}],"tool":"D8"}
AGPBI: {"kind":"warning","text":"Ignoring option: -optimizationpasses","sources":"省略..."}],"tool":"D8"}
-dontobfuscate
-dontpreverify

4. ProGuard 规则文件

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

在上一节里,我们提到了使用proguardFilesproguardFile指定 ProGuard 规则文件。对于任何一个项目,它的 ProGuard 规则文件有以下三种来源:

小结一下:

规则文件来源 描述
Android Gradle 插件 在编译时,由 Android Gradle 插件生成
AAPT2 在编译时,AAPT2 根据对应用清单中的类、布局及其他应用资源的引用来生成保留规则
Module 创建新 Module 时,由 IDE 创建,或者另外按需创建

如果将 minifyEnabled 属性设为 trueProGuard 或 R8 会将来自上面列出的所有可用来源的规则组合在一起。为了看到完整的规则文件,可以在proguard-rules.pro 中添加以下配置,输出编译项目时应用的所有规则的完整报告:

-printconfiguration build/intermediates/proguard-files/full-config.txt

5. 组件化混淆

在组件化的项目中,需要注意应用 Module 和 Library Module 的行为差异和组件化的资源汇合规则,总结为以下几个重点:

组件化资源汇总 Lib Module 汇总到 App Module

使用较高版本的 Android Gradle Plugin,不会将汇总的资源放置在 exploded-aar文件夹。即便如此,Lib Module 的资源汇总到 App Module 的规则是一样的。

我们通过一个简单示例测试不同配置下的混淆结果:

配置一 配置二 配置三 配置四
App Module 开启混淆 X X
Base Module 开启混淆 X X
示例程序:App Module 依赖了 Base Module

将构建的 apk 包拖到 Android Studio 面板上即可分析 Base 类混淆结果,例如配置一的结果:


使用配置一时,Base 类没有被混淆

全部测试结果如下:

配置一 配置二 配置三 配置四
App Module 开启混淆 X X
Base Module 开启混淆 X X
(结果)Base 类是否被混淆 X X

可以看到,混淆开启由 App Module 决定, 与Lib Module 无关

现在我们分别在 Lib Module 和 App Module 的 proguard-rules.pro中添加 Base 类的混淆保留规则,并在 build.gradle中添加配置文件,测试 Base 类是否能保留:

-keep class com.rui.base.Base

测试结果如下:

配置位置 Lib Module App Module
(结果)Base 类是否保留 X

可以看到:(默认情况)混淆规则以 App Module 中的混淆规则文件为准

这里就引入两种主流的组件化混淆方案:

这种方案将混淆规则都放置到 App Module 的proguard-rules.pro中,最简单也最直观,缺点是移除 Lib Module 时,需要从 App Module 中移除相应的混淆规则。尽管多余的混淆规则并不会造成编译错误或者运行错误,但还是会影响编译效率。

很多的第三方 SDK,就是采用了这种组件化混淆方案。在 App Module 中添加依赖的同时,也需要在proguard-rules.pro中添加专属的混淆规则,这样才能保证release版本正常运行。

这种方案将专属的混淆规则设置到 Lib Module 的proguard-rules.pro,但是根据前面的测试,在 Lib Module 中设置的混淆规则是不生效的。为了让规则生效,还需要在 Lib Module 的build.gradle中添加以下配置:

...
android{
  defaultConfig{
    consumerProguardFiles 'consumer-rules.pro'
  }
}

其中consumer-rules.pro文件:

-keep class com.rui.base.Base

测试结果表明,Base 类已经被保留了。这种使用consumerProguardFiles的方式有以下几个特点:


6. 总结


参考资料


创作不易,你的「三连」是丑丑最大的动力,我们下次见!

上一篇 下一篇

猜你喜欢

热点阅读