Android面试题16–代码混淆(Proguard)

  • 内容
  • 评论
  • 相关

1. Proguard认识

Proguard是一个非常高效和方便的混淆工具,使用了这个工具混淆打包后,apk体积显著的减少了,而且反编译难度也加大了。官网的介绍是:ProGuard是一个免费的Java类文件缩小,优化,混淆和预验证的工具。它检测和删除未使用的类,字段,方法和属性;优化字节码并删除未使用的指令;它使用短的无意义的名称重命名剩余的类,字段和方法。所得到的应用程序和库更小,更快,并且更好地针对逆向工程进行优化。

而且Proguard已经集成在Android studio构建系统里了,可以通过简单的代码来实现构建apk的时候进行混淆打包。

官网地址:https://developer.android.google.cn/studio/build/shrink-code.html

2. Proguard使用

我们需要在项目里的build.gradle文件里配置Proguard。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
buildTypes {
        release {
            shrinkResources true
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
 
        debug {
            shrinkResources false
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
 
}

getDefaultProguardFile(‘proguard-android.txt’) 方法可从 Android SDK tools/proguard/ 文件夹获取默认 ProGuard 设置。
提示:要想做进一步的代码压缩,可尝试使用位于同一位置的 proguard-android-optimize.txt 文件。它包括相同的 ProGuard 规则,但还包括其他在字节码一级(方法内和方法间)执行分析的优化,以进一步减小 APK 大小和帮助提高其运行速度。

shrinkResources 是去除无效的资源文件,压缩资源。

minifyEnabled 是开启混淆。

这是默认的Proguard配置,proguard-rules.pro是需要在你的项目里创建的文件,层级跟build.gradle文件一样的,当然你可以随意改他的文件名,只不过需要在配置代码里面跟着修改,其实这个文件就是Proguard的自定义配置文件,没有这个文件你构建是会报错的。

proguardFiles getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.pro’

如果你希望在不通的渠道版本里使用不同的ProGuard 规则,那就在相应的 productFlavor 代码块中再添加一个 proguardFiles 属性。例如,以下 Gradle 文件会向 flavor2 产品风味添加 flavor2-rules.pro。现在 flavor2 使用所有三个 ProGuard 规则,因为还应用了来自 release 代码块的规则。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
android {
    ...
    buildTypes {
        release {
	    shrinkResources true
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'
        }
    }
    productFlavors {
        flavor1 {
        }
        flavor2 {
            proguardFile 'flavor2-rules.pro'
        }
    }
}

每次构建时 ProGuard 都会输出下列文件:

dump.txt //说明 APK 中所有类文件的内部结构。

mapping.txt //提供原始与混淆过的类、方法和字段名称之间的转换。

seeds.txt //列出未进行混淆的类和成员。

usage.txt //列出从 APK 移除的代码。

这些文件保存在 <module-name>/build/outputs/mapping/release/。

Proguard基本语法

-keep 不混淆类和成员或者被重命名

-keepnames 防止类和成员被重命名

-keepclassmembers 不混淆成员被移除或者被重命名

–keepmembernames 防止成员被重命名

-keepclasseswithmembers 不混淆拥有该成员的类和成员或者被重命名

-keepclasseswithmembernames 防止拥有该成员的类和成员被重命名

而且还支持通配符*

例如

不混淆某个类

-keep public class com.aoaoyi.example.abc { *; }

不混淆某个包

-keep public class com.aoaoyi.example.** { *; }

不混淆某个类的子类

-keep public class * extends com.aoaoyi.exampl.xy { *; }

 

3. 以下是在项目里常使用的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
## --- 基础混淆配置 ---
 
# 指定代码的压缩级别
-optimizationpasses 5  
 
# 优化时允许访问并修改有修饰符的类和类的成员
-allowaccessmodification  
 
# 不使用大小写混合
-dontusemixedcaseclassnames 
 
# 指定不去忽略非公共库的类
-dontskipnonpubliclibraryclasses  
 
# 混淆时是否记录日志
-verbose    
 
# 忽略警告,避免打包时某些警告出现,没有这个的话,构建报错
-ignorewarnings  
 
# 不做预校验,preverify是proguard的四个步骤之一,Android不需要preverify,去掉这一步能够加快混淆速度。
-dontpreverify
 
# 过滤泛型  出现类型转换错误时,启用这个
-keepattributes Signature  
 
# 抛出异常时保留代码行号 方便测试
-keepattributes SourceFile,LineNumberTable
 
# 混淆时所采用的算法
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# --- Android开发中一些需要保留的公共部分 ---
 
# 不混淆注解相关
-keepattributes *Annotation* 
 
# 保持 native 方法不被混淆
-keepclasseswithmembernames class * {  
    native ;
}
 
# 保持枚举 enum 类不被混淆
-keepclassmembers enum * {  
    public static **[] values();
    public static ** valueOf(java.lang.String);
}
 
# 不混淆继承Parcelable的子类
-keep class * implements android.os.Parcelable {   
public static final android.os.Parcelable$Creator *;
}
 
# 不混淆继承Serializable的子类
-keep class * implements java.io.Serializable {*;}
-keepnames class * implements java.io.Serializable
-keepclassmembers class * implements java.io.Serializable {*;}
 
 
# --- 不能被混淆的基类 ---
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Fragment
-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
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends android.view.View
-keep class org.xmlpull.v1.** { *; }
 
# 不混淆我们自定义控件(继承自View-keep public class * extends android.view.View{
     *** get*();
     void set*(***);
     public (android.content.Context);
     public (android.content.Context, android.util.AttributeSet);
     public (android.content.Context, android.util.AttributeSet, int);
 }
 
# 不混淆R文件
-keepclassmembers class **.R$* { 
    public static ;
}
 
# --- 不混淆android-support-v4包 ---
-dontwarn android.support.v4.**
-keep class android.support.v4.** { *; }
-keep interface android.support.v4.app.** { *; }
-keep class * extends android.support.v4.** { *; }
-keep public class * extends android.support.v4.**
-keep public class * extends android.support.v4.widget
-keep class * extends android.support.v4.app.** {*;}
-keep class * extends android.support.v4.view.** {*;}
-keep public class * extends android.support.v4.app.Fragment
 
 
# 不混淆继承的support类
-keep public class * extends android.support.v4.**
-keep public class * extends android.support.v7.**
-keep public class * extends android.support.annotation.**
 
 
# 不混淆log 
-assumenosideeffects class android.util.Log {
    public static boolean isLoggable(java.lang.String, int);
    public static int v(...);
    public static int i(...);
    public static int w(...);
    public static int d(...);
    public static int e(...);
}
 
 
# 保持Activity中参数类型为View的所有方法
-keepclassmembers class * extends android.app.Activity {
          public void *(android.view.View);
}
 
# 有用到WebView的JS调用接口不被混淆
#webkit
-keep public class javax.**
-keep public class android.webkit.**
-dontwarn android.webkit.WebVie
 
#JavascriptInterface不混淆webview中对javascript接口
-keep public class * implements java.lang.reflect.Type
-keepattributes JavascriptInterface
 
-keepclasseswithmembernames class * {
    native ;
}
 
# 对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆
-keepclassmembers class * {
       void *(**On*Event);
       void *(**On*Listener);
}
 
# --- 不混淆第三方库 这个可以去相关的第三方库官网找寻混淆代码 如果被混淆了会无法使用 ---
 
#Google
-dontwarn com.google.android.maps.**
-keepclassmembers class * {
	public (org.json.JSONObject);
}
-keep public class com.smile.android.R$*{
	public static final int *;
}
 
##Begin: proguard configuration for Gson
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.** {*;}
#-keep class com.google.gson.stream.** { *; }
# Prevent proguard from stripping interface information from TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
##End: proguard configuration for Gson
-keep class com.你的bean.** { *; }
 
 
# OkHttp3
-dontwarn okhttp3.logging.**
-keep class okhttp3.internal.**{*;}
-dontwarn okio.**
 
 
# Retrofit
-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-keepattributes Signature
-keepattributes Exceptions
 
# Glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
  **[] $VALUES;
  public *;
}
 
# RxJava RxAndroid
-dontwarn sun.misc.**
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
    long producerIndex;
    long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
    rx.internal.util.atomic.LinkedQueueNode producerNode;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
    rx.internal.util.atomic.LinkedQueueNode consumerNode;
}
 
#facebook
-keep class com.facebook.** { *; }
 
#twitter
-keep class twitter4j.**  { *; }
-keep class oauth.signpost.**  { *; }
 
#cocos2dx
-keep class org.cocos2dx.lib.**  { *; }
 
#apache
-keep class org.apache.** { *; }
-keep class a.a.a.**      { *; }
-keep class com.mrocker.** { *; }
 
#umeng Analytics begin
-keep class com.umeng.commonsdk.** {*;}
-keep class com.umeng.analytics.** {*;}
-keep class com.umeng.** {*;}
#umeng Analytics end
 
# 微信
 -keep class com.tencent.mm.** {*;}
 
#Glide图片库 begin
-keep class com.bumptech.glide.**{*;}
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
  **[] $VALUES;
  public *;
}
# for DexGuard only
#-keepresourcexmlelements manifest/application/meta-data@value=GlideModule
#Glide end
 
# Testin
-dontwarn com.testin.agent.**
-keep class com.testin.agent.** {*;}
 
 
# 支付宝钱包
-dontwarn com.alipay.**
-dontwarn HttpUtils.HttpFetcher
-dontwarn com.ta.utdid2.**
-dontwarn com.ut.device.**
-keep class com.alipay.android.app.IAlixPay{*;}
-keep class com.alipay.android.app.IAlixPay$Stub{*;}
-keep class com.alipay.android.app.IRemoteServiceCallback{*;}
-keep class com.alipay.android.app.IRemoteServiceCallback$Stub{*;}
-keep class com.alipay.sdk.app.PayTask{ public *;}
-keep class com.alipay.sdk.app.AuthTask{ public *;}
-keep class com.alipay.mobilesecuritysdk.*
-keep class com.ut.*
 
#nineoldandroids
-keep class com.nineoldandroids.** {*;}
 
#避免Log打印输出
-assumenosideeffects class android.util.Log {
   public static *** v(...);
   public static *** d(...);
   public static *** i(...);
   public static *** w(...);
}

你用了以上的配置文件,我相信大部分项目都是没有问题的

4. 容易出现的问题总结

4.1 json解析Release版APK报NullPointException错误

在混淆的时候,如果没有在混淆文件中keep掉我们的JavaBean类,那么这个类中的变量可能会被混淆成其他内容,导致在json解析的时候难以准确解析。
解决的办法是找到工程目录下的 proguard-project.txt 混淆配置文件,在文件中添加不需要混淆的类或者包名即可,取消混淆的命令如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 取消com.aoaoyi.model这个包名路径下所有类和子包的混淆
-keep class com.aoaoyi.model.** {*;} 
# 取消混淆Test类
-keep public class com.aoaoyi.model.Test { *; }
# 取消混淆Test类的子类
-keep public class * extends com.aoaoyi.model.Test { *; }
#取消所有类名中包含了“model”的类及其成员的混淆
-keep public class **.*model*.** {*;}
# 取消lgInterface接口实现类的混淆
-keep class * implements com.aoaoyi.lgInterface { *; }
# 取消某个类的内部类的混淆
-keep class com.aoaoyi.model.Test$* {
      *;
}

4.2 使用第三方开源库或者引用其他第三方的SDK包记得要添加混淆规则

如Umeng,hotfix,Glide,RxJava 等吧,一定记得加入混淆规则。

4.3 AndroidMainfest中的类不混淆

四大组件和Application的子类和Framework层下所有的类默认不会进行混淆。自定义的View默认也不会被混淆;所以像网上贴的很多排除自定义View,或四大组件被混淆的规则在Android Studio中是无需加入的。
他的以后我会继续完善这一块,也希望大家多多指点.
推荐: