Android & ProGuard

参考链接首选官方文档>>

  • 开启方式
  • 配置方式
  • 调整方法

开始之前说一下自己对 ProGuard 的理解,更多时候开启 ProGuard 的性价比是比较小的:
在安全方面,简单的混淆并不能阻止反编译,但同时又带来了调试和迭代上的问题(针对项目配置混淆规则、升级/引入第三方库可能存在问题)。
另一方面,很多 APP 的价值都不在本身的逻辑上而依赖于对应的后台/运营,而对于本身的关键代码更应该放到 os 文件里并加固 java 代码来加大反编译的难度。
当然 ProGuard 同时也有压缩资源文件来减小 APK 大小的作用,这里也不是完全否认 ProGuard 的作用。

开启

android {
    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
  • proguardFiles:
    <SDK>/tools/proguard/proguard-android.txt 是默认的配置;
    app/proguard-rules.pro 是我们自定义规则的文件;

  • app/build.gradle:为不同的 build type 设置不同的配置;

  • minifyEnabled: 移除无效的类、类成员、方法、属性等;把类名、属性名、方法名替换为简短且无意义的名称

  • shrinkResources: 删除 / 合并资源,将 drawable/layout 中没有被引用的文件的内容清空而保留文件;而名称相同的资源被视为重复资源会被合并;
    注意:删除资源很容易出现问题,这时候要找到被误删的资源并在 res/raw/keep.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" />

构建成功后可以在 <module-name>/build/outputs/mapping/release/ 中可以找到相关文件:
dump.txt: 说明 APK 中所有类文件的内部结构。
mapping.txt: 提供原始与混淆过的类、方法和字段名称之间的转换。
seeds.txt: 列出未进行混淆的类和成员。
usage.txt: 列出从 APK 移除的代码。

配置

自定义的混淆规则的格式如下:

[保持命令] <类> {
    [成员] 
}
  • [保存命令]
命令 说明
-dontwarn 不提示指定包名的混淆打包 warning
-keep 防止类和成员被移除或者被重命名
-keepnames 防止类和成员被重命名
-keepclassmembers 防止成员被移除或者被重命名
-keepnames 防止成员被重命名
-keepclasseswithmembers 保留指定的类和其成员,前提是它们在压缩阶段没有被删除
-keepclasseswithmembernames 防止拥有该成员的类和成员被重命名

<类> 指定了作用文件,可以有如下匹配写法:

  • 具体的类(完整包名)
    -dontnote retrofit2.Platform
  • 访问修饰符(public、protected、private)
    -keep public class * extends android.app.Fragment
  • 通配符 *,匹配任意长度字符,但不含包名分隔符(.)
  • 通配符 **,匹配任意长度字符,并且包含包名分隔符(.)
    -dontwarn com.tencent.bugly.**
  • extends,即可以指定类的基类
    -keep public class * extends android.app.Fragment
  • implement,匹配实现了某接口的类
    -keep public class * implements com.bumptech.glide.module.GlideModule
  • $,内部类

[成员] 指定了类成员相关的限定条件,可以使用:

  • 匹配所有构造器
    public <init>();
  • 匹配所有字段
    <fields>
  • 匹配所有方法
# 保留所有类中的有 @org.greenrobot.eventbus.Subscribe 注解的方法
-keepclassmembers class ** {
    @org.greenrobot.eventbus.Subscribe <methods>;
}
  • 通配符 *,匹配任意长度字符,但不含包名分隔符(.)
  • 通配符 **,匹配任意长度字符,并且包含包名分隔符(.)
  • 通配符 ***,匹配任意参数类型
  • …,匹配任意长度的任意类型参数。
    void test(…)
  • 访问修饰符(public、protected、private)

而一些常用的写法:

# 不混淆某个类
-keep public class name.huihui.example.Test { *; }

# 不混淆某个包所有的类
-keep class name.huihui.test.** { *; }

#不混淆某个类的子类
-keep public class * extends name.huihui.example.Test { *; }

#不混淆所有类名中包含了“model”的类及其成员
-keep public class **.*model*.** {*;}

#不混淆某个接口的实现
-keep class * implements name.huihui.example.TestInterface { *; }

#不混淆某个类的构造方法
-keepclassmembers class name.huihui.example.Test { 
  public <init>(); 
}

#不混淆某个类的特定的方法
-keepclassmembers class name.huihui.example.Test { 
  public void test(java.lang.String); 
}

调整

混淆的规则大部分是固定的,然后重点在于我们要根据项目引用的框架、使用的技术去调整规则:

  • 第三方库所需的混淆规则。正规的第三方库一般都会在接入文档中写好所需混淆规则,使用时注意添加。

  • 在运行时动态改变的代码,例如反射。
    比较典型的例子就是会与 json 相互转换的实体类。假如项目命名规范要求实体类都要放在 Model 包下的话,可以添加类似这样的代码把所有实体类都保持住:
    -keep public class **.*Model*.** {*;}

  • JNI 中调用的类。

  • WebViewJavaScript 调用的方法

#保留 annotation, 例如 @JavascriptInterface 
-keepattributes *Annotation*

#保留跟 javascript 相关的属性 
-keepattributes JavascriptInterface

# #package# 为实际的包名
#-keepclassmembers #package#.JSInterface {
#    <methods>;
#}
  • Layout 布局使用的 View 构造函数、android:onClick 等。
# 保持自定义 View 的 get 和 set 相关方法
-keepclassmembers public class * extends android.view.View {
   void set*(***);
   *** get*();
}

  • 编译时问题
Note: there were 8 references to unknown classes.
You should check your configuration for typos.
([http://proguard.sourceforge.net/manual/troubleshooting.html#unknownclass](http://proguard.sourceforge.net/manual/troubleshooting.html#unknownclass))
Note: there were 272 unkept descriptor classes in kept class members.
You should consider explicitly keeping the mentioned classes
(using '-keep').
([http://proguard.sourceforge.net/manual/troubleshooting.html#descriptorclass](http://proguard.sourceforge.net/manual/troubleshooting.html#descriptorclass))
Note: there were 75 unresolved dynamic references to classes or interfaces.
You should check if you need to specify additional program jars.
([http://proguard.sourceforge.net/manual/troubleshooting.html#dynamicalclass](http://proguard.sourceforge.net/manual/troubleshooting.html#dynamicalclass))
Warning: there were 11 unresolved references to classes or interfaces.
You may need to add missing library jars or update their versions.
If your code works fine without the missing classes, you can suppress
the warnings with '-dontwarn' options.
([http://proguard.sourceforge.net/manual/troubleshooting.html#unresolvedclass](http://proguard.sourceforge.net/manual/troubleshooting.html#unresolvedclass))
Warning: Exception while processing task java.io.IOException: Please correct the above warnings first.

Warning 中提到有 11 个未解析的类或引用,并且给出两个解决方案;
方案一:你可能需要添加丢失的库或更新它们的版本。
方案二:如果你现在代码运行得好好的,也就是没有它们也没关系,那你可以使用 -dontwarn 来禁止这样的警告。

具体的类或引用会出现在日志中:

Warning: okhttp3.internal.platform.ConscryptPlatform: can't find referenced class org.conscrypt.OpenSSLProvider
Warning: okhttp3.internal.platform.ConscryptPlatform: can't find referenced class org.conscrypt.OpenSSLProvider
Warning: okhttp3.internal.platform.ConscryptPlatform: can't find referenced class org.conscrypt.Conscrypt
Warning: okhttp3.internal.platform.ConscryptPlatform: can't find referenced class org.conscrypt.Conscrypt
Warning: okhttp3.internal.platform.ConscryptPlatform: can't find referenced class org.conscrypt.Conscrypt
Warning: okhttp3.internal.platform.ConscryptPlatform: can't find referenced class org.conscrypt.Conscrypt
Warning: okhttp3.internal.platform.ConscryptPlatform: can't find referenced class org.conscrypt.Conscrypt
Warning: okhttp3.internal.platform.ConscryptPlatform: can't find referenced class org.conscrypt.Conscrypt
Warning: okhttp3.internal.platform.ConscryptPlatform: can't find referenced class org.conscrypt.Conscrypt
Warning: okhttp3.internal.platform.ConscryptPlatform: can't find referenced class org.conscrypt.Conscrypt
Warning: okhttp3.internal.platform.ConscryptPlatform: can't find referenced class org.conscrypt.Conscrypt

上述例子中类 okhttp3.internal.platform.ConscryptPlatform 引用了 org.conscrypt.OpenSSLProvider 但混淆压缩后找不到。如果项目中实际并没有用上类 org.conscrypt.OpenSSLProvider 就可以通过以下规则抑制警告:

-dontwarn org.conscrypt.*

-dontwarn okhttp3.internal.platform.ConscryptPlatform
  • 运行时问题
    这一块最麻烦的是,这种问题往往可能藏的比较深,所以除了彻底的回归测试外只有理解这其中的原理才能有效的减少问题。
    配置混淆规则时提到过,动态改变的代码、反射、js 调用等方法 / 类不能被混淆,否则在运行时会报错。
    具体比如 Retrofit 等网络框架或者用 Gson 解析 json 至实体类时,如果没有使用 -keep 标注实体类将会导致实体类的字段名被混淆而无法解析到预想的字段中。

部分参考自 写给 Android 开发者的混淆使用手册

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容