ProGuard 精粹

背景

总结ProGuard混淆日常的使用方式,常见的混淆片段等。

更多内容ProGuard

ProGuard is a free Java class file shrinker, optimizer, obfuscator, and preverifier. It detects and removes unused classes, fields, methods, and attributes. It optimizes bytecode and removes unused instructions. It renames the remaining classes, fields, and methods using short meaningless names. Finally, it preverifies the processed code for Java 6 or higher, or for Java Micro Edition.
Some uses of ProGuard are:
Creating more compact code, for smaller code archives, faster transfer across networks, faster loading, and smaller memory footprints.
Making programs and libraries harder to reverse-engineer.
Listing dead code, so it can be removed from the source code.
Retargeting and preverifying existing class files for Java 6 or higher, to take full advantage of their faster class loading.

原则

现在面向对象都讲究原则,做事也讲究原则。

  • 反射用到的类不混淆
  • JNI方法不混淆
  • Manifest中类不混淆
  • 四大组件和Application子类、Framework层下所有类默认不混淆
  • Parcelable子类和Creator静态成员变量不混淆(BadParcelableException,怕了吧)
  • GSon、FastJson等库时,Json对象不混淆(注解可解决此问题)
  • 第三方开源库或引用其他第三方SDK,加入对应混淆规则(或者库在打包aar时有指定consumerProguardFiles
  • WebView的JS调用接口不混淆

使用

AndroidStudio

新建项目后,默认会在模块目录下新建build.gradleproguard-rules.pro文件,修改build.gradle文件中的proguardFiles配置:

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

如果你是引用库,那么不要忘了给库配上混淆,详细参考搞定Android Proguard或者AndroidKnife/proguard-config

1
consumerProguardFiles

其他方式

在SDK目录下,有tools/proguard/,这里即proguard的大本营,包括上面的getDefaultProguardFile方法也是到此目录获取proguard-android.txt文件。

proguard目录下有bin目录,可以直接启动界面配置,当然也可以保存配置等。

其他类似ANT或者IDE类似Eclipse等,基本大同小异,自行摸索。

配置

基本配置

常用语法:

  • -libraryjars class_path:应用的依赖包,如android-support-v4
  • -keep [,modifier,…] class_specification`:不混淆某些类
  • -keepclassmembers [,modifier,...] class_specification:不混淆类的成员
  • -keepclasseswithmembers [,modifier,...] class_specification:不混淆类及其成员
  • -keepnames class_specification:不混淆类及其成员名
  • -keepclassmembernames class_specification:不混淆类的成员名
  • -keepclasseswithmembernames class_specification:不混淆类及其成员名
  • -assumenosideeffects class_specification:假设调用不产生任何影响,在代码优化时会将该调用remove掉。如system.out.printlnLog.v等等
  • -dontwarn [class_filter]:不提示warnning

更多关于ProGuard混淆规则和语法,参考ProGuard

常见使用

不混淆类构造方法,需指定函数参数类型:

1
2
3
-keepclassmembers class com.hwangjr.utils.Telephony {
public <init>(int);
}

不混淆某个包所有/某个类、接口:

1
-keep class com.hwangjr.utils.** { *; }

不混淆某个方法:

1
2
3
-keepclassmembers class com.hwangjr.utils.Telephony {
public String getTelephone(java.lang.String);
}

不混淆Parcelable子类:

1
2
3
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}

使用assumenosideeffects移除代码:
正常可通过BuildConfig.DEBUG变量控制日志输出,DEBUG模式可查看到日志。但ProGuard也可压缩和优化不必要的代码,包括debug日志,在发布时,会自动从源码中删除。

1
2
3
4
5
6
7
-assumenosideeffects class android.util.Log {
public static *** v(...);
public static *** i(...);
public static *** d(...);
public static *** w(...);
public static *** e(...);
}

反射:

1
2
-keepattributes Signature
-keepattributes EnclosingMethod

Serializable类:

1
2
3
4
5
6
7
8
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}

JS接口:

1
2
3
4
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
public *;
}
-keep class com.package.name.** { *; }

混淆后

混淆后文件

Android Studio发布版本之后,会自动生成混淆之后文件(build/outputs/mapping/channel/release/):
mapping.txt:混淆前后代码对照,在混淆之后如果出现问题,需要查找此文件进行定位。
dump.txt:APK内所有class文件结构
seeds.txt:没有被混淆的类和成员
usage.txt:源代码中被删除的代码

所以,养成保存mapping.txt文件的习惯。

混淆后验证

反编译应用,大部分都变成abc...

混淆后调试

问题分为代码问题和混淆问题。

代码问题
通过查看mapping.txt文件,反推定位对应代码问题来进行解决。混淆后$为匿名内部类。

混淆问题
最常见ClassNotFoundExceptionBadParcelableException及JSON解析错误。
ClassNotFoundException通过mapping逆推找到类,在混淆中加入不混淆此类即可:

1
-keep class package.name.class { *; }

Parcelable混淆错误加入:

1
2
3
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}

V4包、V7包等包混淆问题(sdk/tools/proguard/proguard-android.txt中已经明确):

1
2
3
4
# The support library contains references to newer platform versions.
# Don't warn about those in case this app is linking against an older
# platform version. We know about them, and they are safe.
-dontwarn android.support.**

更多问题可以参考:ProGuard TroubleShooting

ProGuard-Android

参考示例sdk/tools/proguard/proguard-android.txt

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
# This is a configuration file for ProGuard.
# http://proguard.sourceforge.net/index.html#manual/usage.html

-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-verbose

# Optimization is turned off by default. Dex does not like code run
# through the ProGuard optimize and preverify steps (and performs some
# of these optimizations on its own).
-dontoptimize
-dontpreverify
# Note that if you want to enable optimization, you cannot just
# include optimization flags in your own project configuration file;
# instead you will need to point to the
# "proguard-android-optimize.txt" file instead of this one from your
# project.properties file.

-keepattributes *Annotation*
-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService

# For native methods, see http://proguard.sourceforge.net/manual/examples.html#native
-keepclasseswithmembernames class * {
native <methods>;
}

# keep setters in Views so that animations can still work.
# see http://proguard.sourceforge.net/manual/examples.html#beans
-keepclassmembers public class * extends android.view.View {
void set*(***);
*** get*();
}

# We want to keep methods in Activity that could be used in the XML attribute onClick
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}

# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}

-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}

-keepclassmembers class **.R$* {
public static <fields>;
}

# The support library contains references to newer platform versions.
# Don't warn about those in case this app is linking against an older
# platform version. We know about them, and they are safe.
-dontwarn android.support.**

参考示例sdk/tools/proguard/proguard-android-optimize.txt

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
# This is a configuration file for ProGuard.
# http://proguard.sourceforge.net/index.html#manual/usage.html

# Optimizations: If you don't want to optimize, use the
# proguard-android.txt configuration file instead of this one, which
# turns off the optimization flags. Adding optimization introduces
# certain risks, since for example not all optimizations performed by
# ProGuard works on all versions of Dalvik. The following flags turn
# off various optimizations known to have issues, but the list may not
# be complete or up to date. (The "arithmetic" optimization can be
# used if you are only targeting Android 2.0 or later.) Make sure you
# test thoroughly if you go this route.
-optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
-optimizationpasses 5
-allowaccessmodification
-dontpreverify

# The remainder of this file is identical to the non-optimized version
# of the Proguard configuration file (except that the other file has
# flags to turn off optimization).

-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-verbose

-keepattributes *Annotation*
-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService

# For native methods, see http://proguard.sourceforge.net/manual/examples.html#native
-keepclasseswithmembernames class * {
native <methods>;
}

# keep setters in Views so that animations can still work.
# see http://proguard.sourceforge.net/manual/examples.html#beans
-keepclassmembers public class * extends android.view.View {
void set*(***);
*** get*();
}

# We want to keep methods in Activity that could be used in the XML attribute onClick
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}

# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}

-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}

-keepclassmembers class **.R$* {
public static <fields>;
}

# The support library contains references to newer platform versions.
# Don't warn about those in case this app is linking against an older
# platform version. We know about them, and they are safe.
-dontwarn android.support.**

参考sdk/tools/proguard/proguard-project.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# To enable ProGuard in your project, edit project.properties
# to define the proguard.config property as described in that file.
#
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in ${sdk.dir}/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the ProGuard
# include property in project.properties.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# Add any project specific keep options here:

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

Ref

ProGuard
ProGuard | Android Developers
ProGuard的作用
AndroidStudio下ProGuard混淆打包.md