1、代码混淆配置
如下图,默认情况下,混淆是关闭的,我们只需要将minifyEnabled置为true就可以开启混淆了。
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
其中:
minifyEnabled表示是否开启混淆
proguardFiles用于选定混淆配置文件,getDefaultProguardFile(‘proguard-android.txt’) 方法可从 Android SDK tools/proguard/ 文件夹获取默认的 ProGuard 设置,proguard-rules.pro 文件用于添加自定义 ProGuard 规则。默认情况下,该文件位于模块根目录(build.gradle 文件旁)。
需要注意的是,上面加载了两个混淆配置文件,一个是默认的,一个是自定义的,所以这里告诉我们我们可以同时指定多个混淆配置文件,另外我们还可以分开来写,如下:
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt')
proguardFiles 'proguard-rules.pro'
}
}
为啥进行了上述的配置就可以进行代码的混淆呢?
在$android-sdk-linux/tools/proguard/lib/目录下面有一个proguard.jar,我们代码混淆的工作其实就是这个jar完成的。
Linux或者Mac平台下,在android-sdk-linux/tools/proguard/bin/文件夹中有两个脚本文件,他们就是运行这个jar文件的脚本,proguard.sh是使用命令行操作的混淆脚本,proguardgui.sh是带有可视化操作界面的混淆脚本,背后实质运行的都是这个proguard.jar工具。
所以我们如果想单独对某一个jar文件进行混淆,其实也是可以的,我们直接运行这个工具就可以操作了。
2、混淆规则
在Android SDK tools/proguard/ 文件夹下默认有三个文件:
(1)、proguard-android.txt 默认的Proguard配置文件(未优化)
(2)、proguard-android-optimize.txt 默认的Proguard配置文件(已优化)
(3)、proguard-project.txt 默认的用户定制Proguard配置文件。
下面我们来查看下proguard-android.txt中的默认配置规则
# This is a configuration file for ProGuard.
# http://proguard.sourceforge.net/index.html#manual/usage.html
#表示混淆时不使用大小写混合类名
-dontusemixedcaseclassnames
#表示不跳过library中的非public的类
-dontskipnonpubliclibraryclasses
#表示打印混淆的详细信息
-verbose
# 表示不进行优化
-dontoptimize
表示不进行预校验
-dontpreverify
# 表示对注解中的参数进行保留
-keepattributes *Annotation*
#表示不混淆类ILicensingService
-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService
#表示不混淆任何包含native方法的类的类名以及native方法名
-keepclasseswithmembernames class * {
native <methods>;
}
#表示不混淆任何一个View中的setXxx()和getXxx()方法
-keepclassmembers public class * extends android.view.View {
void set*(***);
*** get*();
}
# 表示不混淆Activity中参数是View的方法
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
# 表示不混淆枚举中的values()和valueOf()方法
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
#表示不混淆Parcelable实现类中的CREATOR字段
-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}
#表示不混淆R文件中的所有静态字段
-keepclassmembers class **.R$* {
public static <fields>;
}
#表示对android.support包下的代码不警告
-dontwarn android.support.**
#不混淆Keep注解类
-keep class android.support.annotation.Keep
#不混淆带有Keep注解的类
-keep @android.support.annotation.Keep class * {*;}
#不混淆带有Keep注解的成员方法以及类名
-keepclasseswithmembers class * {
@android.support.annotation.Keep <methods>;
}
#不混淆带有Keep注解的成员变量以及类名
-keepclasseswithmembers class * {
@android.support.annotation.Keep <fields>;
}
#不混淆带有Keep注解的构造方法以及类名
-keepclasseswithmembers class * {
@android.support.annotation.Keep <init>(...);
}
上面可以看到,对于Keep注解的类、成员变量、成员方法是不会被混淆的,因此我们可以使用@Keep注解来防止混淆
例如:
//防止混淆类
@Keep
public class Person {}
//防止混淆变量
@Keep
public String name;
//防止混淆方法
@Keep
public int getAge(){}
下面对关键词和通配符进行一个总结
关于优化的几个规则
-dontoptimize
关闭优化项,如果没有这项默认优化项是开启的
-optimizations optimization_filter
根据optimization_filter指定要优化的文件
-optimizationpasses n
优化数量 n
-assumenosideeffects class_specification
在优化阶段,ProGuard会将指定的class_specification代码移除。这里可以用做Log代码的移除
-assumenosideeffects class android.util.Log {
public static *** e(...);
public static *** d(...);
public static *** w(...);
public static *** v(...);
public static *** i(...);
}
-allowaccessmodification
优化时允许访问并修改类和类的成员的访问修饰符,可能作用域会变大。
-mergeinterfacesaggressively
合并接口,即使它们的实现类未实现合并后接口的所有方法。
理解了上面优化项之后,我们就可以看看proguard-android-optimize.txt和proguard-android.txt到底有什么区别,他们的区别其实就是在优化项上,对比下他们里面的内容,我们发现proguard-android.txt包含下面内容:
# Optimization is turned off by default. Dex does not like code run
# through the ProGuard optimize
-dontoptimize
# 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.
上面注释翻译如下:关闭优化功能,不会进行优化,另外,如果你想开启优化功能,建议使用proguard-android-optimize.txt配置文件
可以看到在proguard-android.txt明确将优化项关闭了。
下面来看看proguard-android-optimize.txt文件
# 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
# 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).
上面注解大致意思为:如果不想进行优化,建议使用proguard-android.txt配置文件,另外,优化会存在风险,不能保证在所有版本的Dalvik上都正常运行。
另外,上面没有-dontoptimize项,也就是说它默认开启优化项,并且上面明确指明了优化相关的配置信息。
3、混淆结果
每次构建时 ProGuard 都会输出下列文件:
dump.txt 说明 APK 中所有类文件的内部结构。
mapping.txt 提供原始与混淆过的类、方法和字段名称之间的转换。
seeds.txt 列出未进行混淆的类和成员。
usage.txt 列出从 APK 移除的代码。
这些文件保存在 <module-name>/build/outputs/mapping/release/
中。
4、解出混淆栈
混淆后的类、方法名等等难以阅读,拿到 crash 的堆栈信息后会发现很难定位,这时需要将混淆反解。
在 <sdk-root>/tools/proguard/bin
路径下有附带的的反解工具,,Mac 或 Linux 系统为 proguardgui.sh,运行脚本
点击 ReTrace,选择该混淆包对应的 mapping 文件(混淆后在 <module-name>/build/outputs/mapping/release/
路径下会生成 mapping.txt 文件,它的作用是提供混淆前后类、方法、类成员等的对照表),再将 crash 的 stack trace 黏贴进输入框中,点击右下角的 ReTrace ,混淆后的堆栈信息就显示出来了。
以上使用 GUI 程序进行操作,另一种方式是利用该路径下的 retrace 工具通过命令行进行反解,命令是
retrace.sh [-verbose] mapping.txt [<stacktrace_file>]
参考文章:
ProGuard manual
写给 Android 开发者的混淆使用手册
Android进阶之ProGuard代码混淆
Android安全攻防战,反编译与混淆技术完全解析(下)