转载请标明出处:
http://blog.csdn.net/sinat_15877283/article/details/50960690;
本文出自: 【温利东的博客】
本篇文章中介绍的混淆技术都是基于Android Studio的,Eclipse的用法也基本类似,但是就不再为Eclipse专门做讲解了。
混淆APK
在Android Studio当中混淆APK实在是太简单了,借助SDK中自带的Proguard工具,只需要修改build.gradle中的一行配置即可。可以看到,现在build.gradle中minifyEnabled的值是false,这里我们只需要把值改成true,打出来的APK包就会是混淆过的了。如下所示:
release {
minifyEnabled true //是否启用混淆
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' //用于选定混淆配置文件
}
其中minifyEnabled用于设置是否启用混淆,proguardFiles用于选定混淆配置文件。注意这里是在release闭包内进行配置的,因此只有打出正式版的APK才会进行混淆,Debug版的APK是不会混淆的。
混淆规则
默认混淆配置
其实就是刚才在build.gradle的release闭包下配置的proguard-android.txt文件,这个文件存放于< Android SDK>/tools/proguard目录下,我们打开来看一下:
# 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.
# Dont 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.**
这个就是默认的混淆配置文件了,我们来一起逐行阅读一下。
*-dontusemixedcaseclassnames*
表示混淆时不使用大小写混合类名。
*-dontskipnonpubliclibraryclasses*
表示不跳过library中的非public的类。
*-verbose*
表示打印混淆的详细信息。
*-dontoptimize*
表示不进行优化,建议使用此选项,因为根据proguard-android-optimize.txt中的描述,优化可能会造成一些潜在风险,不能保证在所有版本的Dalvik上都正常运行。
*-dontpreverify*
表示不进行预校验。这个预校验是作用在Java平台上的,Android平台上不需要这项功能,去掉之后还可以加快混淆速度。
*-keepattributes *Annotation**
表示对注解中的参数进行保留。
-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService
表示不混淆上述声明的两个类,这两个类我们基本也用不上,是接入Google原生的一些服务时使用的。
-keepclasseswithmembernames class * {
native <methods>;
}
表示不混淆任何包含native方法的类的类名以及native方法名。
-keepclassmembers public class * extends android.view.View {
void set*(***);
*** get*();
}
表示不混淆任何一个View中的setXxx()和getXxx()方法,因为属性动画需要有相应的setter和getter的方法实现,混淆了就无法工作了。
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
表示不混淆Activity中参数是View的方法,因为有这样一种用法,在XML中配置android:onClick=”buttonClick”属性,当用户点击该按钮时就会调用Activity中的buttonClick(View view)方法,如果这个方法被混淆的话就找不到了。
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
表示不混淆枚举中的values()和valueOf()方法,枚举我用的非常少,这个就不评论了。
-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}
表示不混淆Parcelable实现类中的CREATOR字段,毫无疑问,CREATOR字段是绝对不能改变的,包括大小写都不能变,不然整个Parcelable工作机制都会失败。
-keepclassmembers class **.R$* {
public static <fields>;
}
表示不混淆R文件中的所有静态字段,我们都知道R文件是通过字段来记录每个资源的id的,字段名要是被混淆了,id也就找不着了。
*-dontwarn android.support.***
表示对android.support包下的代码不警告,因为support包中有很多代码都是在高版本中使用的,如果我们的项目指定的版本比较低在打包时就会给予警告。不过support包中所有的代码都在版本兼容性上做足了判断,因此不用担心代码会出问题,所以直接忽略警告就可以了。
好了,这就是proguard-android.txt文件中所有默认的配置,而我们混淆代码也是按照这些配置的规则来进行混淆的。经过我上面的讲解之后,相信大家对这些配置的内容基本都能理解了。不过proguard语法中还真有几处非常难理解的地方,我自己也是研究了好久才搞明白,下面和大家分享一下这些难懂的语法部分。
混淆规则定义
proguard中一共有三组六个keep关键字,很多人搞不清楚它们的区别,这里我们通过一个表格来直观地看下:
关键字 | 描述 |
---|---|
keep | 保留类和类中的成员,防止它们被混淆或移除。 |
keepnames | 保留类和类中的成员,防止它们被混淆,但当成员没有被引用时会被移除。 |
keepclassmembers | 只保留类中的成员,防止它们被混淆或移除。 |
keepclassmembernames | 只保留类中的成员,防止它们被混淆,但当成员没有被引用时会被移除。 |
keepclasseswithmembers | 保留类和类中的成员,防止它们被混淆或移除,前提是指名的类中的成员必须存在,如果不存在则还是会混淆。 |
keepclasseswithmembernames | 保留类和类中的成员,防止它们被混淆,但当成员没有被引用时会被移除,前提是指名的类中的成员必须存在,如果不存在则还是会混淆。 |
除此之外,proguard中的通配符也比较让人难懂,proguard-android.txt中就使用到了很多通配符,我们来看一下它们之间的区别:
通配符 | 描述 |
---|---|
< field > | 匹配类中的所有字段 |
< method> | 匹配类中的所有方法 |
< init> | 匹配类中的所有构造函数 |
* | 匹配任意长度字符,但不含包名分隔符(.)。比如说我们的完整类名是com.example.test.MyActivity,使用com.,或者com.exmaple.* 都是无法匹配的,因为* 无法匹配包名中的分隔符,正确的匹配方式是com.exmaple.* .* ,或者com.exmaple.test.* ,这些都是可以的。但如果你不写任何其它内容,只有一个*,那就表示匹配所有的东西。 |
** | 匹配任意长度字符,并且包含包名分隔符(.)。比如proguard-android.txt中使用的-dontwarn android.support.**就可以匹配android.support包下的所有内容,包括任意长度的子包。 |
.* ** | 匹配任意参数类型。比如void set*(* * * )就能匹配任意传入的参数类型,* * * get*()就能匹配任意返回值的类型。 |
… | 匹配任意长度的任意类型参数。比如void test(…)就能匹配任意void test(String a)或者是void test(int a, String b)这些方法。 |
虽说上面表格已经解释的很详细了,但是很多人对于keep和keepclasseswithmembers这两个关键字的区别还是搞不懂。确实,它们之间用法有点太像了,我做了很多次试验它们的结果都是相同的。其实唯一的区别就在于类中声明的成员存不存在,我们还是通过一个例子来直接地看一下,先看keepclasseswithmember关键字:
-keepclasseswithmember class * {
native <methods>;
}
这段代码的意思其实很明显,就是保留所有含有native方法的类的类名和native方法名,而如果某个类中没有含有native方法,那就还是会被混淆。
但是如果改成keep关键字,结果会完全不一样:
-keep class * {
native <methods>;
}
使用keep关键字后,你会发现代码中所有类的类名都不会被混淆了,因为keep关键字看到class *就认为应该将所有类名进行保留,而不会关心该类中是否含有native方法。当然这样写只会保证类名不会被混淆,类中的成员还是会被混淆的。
比较难懂的用法大概就这些吧,掌握了这些内容之后我们就能继续前进了。
混淆的规则都是按照proguard-android.txt中默认的规则来的,当然我们也可以修改proguard-android.txt中的规则,但是直接在proguard-android.txt中修改会对我们本机上所有项目的混淆规则都生效,那么有没有什么办法只针对当前项目的混淆规则做修改呢?当然是有办法的了,你会发现任何一个Android Studio项目在app模块目录下都有一个proguard-rules.pro文件,这个文件就是用于让我们编写只适用于当前项目的混淆规则的。
提示:
- proguard-rules.pro文件名可以任意,只要在配置文件中指明即可。比如,从其它项目中复制一个proguard.cfg混淆规则文件,只需将上面的代码改成
proguardFiles getDefaultProguardFile('proguard-android.txt'),
即可,这句话表示使用项目中根目录下的proguard.cfg文件作为本项目的混淆规则文件。
'proguard.cfg' - 打包项目注意记得在Build Type:选项下选择release,否则只打包不会混淆。如下图:
推荐:《Android代码混淆技巧》、Android安全攻防战,反编译与混淆技术完全解析、Proguard简要语法手册
、https://segmentfault.com/a/1190000004461614