Android代码混淆

前言

最近在用Kotlin撸App,准备发版了,做下代码混淆,想用原来的混淆逻辑,但是发现各种报错,头大的很,觉得是自己关于混淆的知识积累不够多,是应该系统的学习一下了!顺便在此记录下遇到的坑。那下面我们开始吧。

代码混淆

开启代码混淆

只要在app.gradle文件下配置proguardFiles

    buildTypes {
        release {
            minifyEnabled true //是否开启混淆
            zipAlignEnabled true //对齐zip
            debuggable false // 是否debug
            versionNameSuffix "_release" // 版本命名后缀
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' // 混淆文件
            signingConfig signingConfigs.release
        }
        ...
    }

proguard-android.txt 是android自带的混淆规则,我们只需要在proguard-rules.pro这个文件中配置我么的混淆规则就可以了。

Proguard混淆流程

proguard混淆流程

  • 压缩(shrink):检测并移除代码中无用的类、字段、方法和特性
  • 优化(optimize):对字节码进行优化,移除无用指令
  • 混淆(obfuscate):使用a,b,c,d这样简短而无意义的名称,对类、字段和方法进行重命名
  • 预检(preveirfy):在java平台上对处理后的代码进行预检,确保加载的class文件时可执行的

混淆规则

Proguard关键字 描述
keep 保留类和类中的成员,防止被混淆或移除
keepnames 保留类和类中的成员,防止被混淆,成员没有被引用会被移除
keepclassmembers 只保留类中的成员,防止被混淆或移除
keepclassmembernames 只保留类中的成员,防止被混淆,成员没有引用会被移除
keepclasseswithmembers 保留类和类中的成员,防止被混淆或移除,保留指明的成员,前提是指名的类中的成员必须存在,如果不存在则还是会混淆。
keepclasseswithmembernames 保留类和类中的成员,防止被混淆,保留指明的成员,成员没有引用会被移除,前提是指名的类中的成员必须存在,如果不存在则还是会混淆。
通配符 描述
field 匹配类中的所有字段
method 匹配类中的所有方法
init 匹配类中的所有构造函数
* 匹配任意长度字符,但不含包名分隔符(.)。
** 匹配任意长度字符,并且包含包名分隔符(.)。
*** 匹配任意参数类型
匹配任意长度任意类型参数

举例:
我们完整的包名是com.xxx.ui.MainAct,使用com.*或者com.xxx.\*都是无法匹配的,正确的写法是com.xxx.\*.\*,或者com.xxx.ui.*

避免混淆的因素

  • native method:因为native是根据方法名去调用的,若混淆后会导致找不到此方法名。
  • 反射相关的方法和类:反射原理就是通过方法名和类名去实例化相应的对象,调用相关的方法。
  • setXX和getXX方法:这里指的是通过配置文件直接生成相应的set和get方法的相关库,所以javaBean类很多情况下不能做混淆。
  • 第三方jar包:这个需要具体情况具体分析,很多库都会提供默认的混淆配置,大多数情况可以不用做混淆。

处理混淆失败问题

通常混淆失败导致gradle构建项目失败,原因在输出的错误日志上并不明显,我们可以在Build Output中找到构建出错的task,例如我构建失败的任务是transformClassesAndResourcesWithProguardForBaiduRelease,因此我可以执行

gradlew transformClassesAndResourcesWithProguardForBaiduRelease -- stacktrace

这样我们就能在shell中看清楚到底是什么地方出错啦。

参考

ProGuard manual
Android混淆
Android 代码混淆零基础入门
ProGuard 最全混淆规则说明
Android 混淆:proguard实践

评论