为什么要混淆?
增大反编译难度,类和类成员会被随机命名。
混淆的四个功能:
我们通常说的proguard包括四个功能,shrinker
(压缩), optimizer
(优化),obfuscator
(混淆),preverifier
(预校验)。
- shrink压缩: 检测并移除没有用到的类,变量,方法和属性;默认开启,用以减小应用体积,移除未被使用的类和成员,并且会在优化动作执行之后再次执行(因为优化后可能会再次暴露一些未被使用的类和成员)。
- optimize优化: 优化代码,非入口节点类会加上private/static/final, 没有用到的参数会被删除,一些方法可能会变成内联代码。默认开启,在字节码级别执行优化,让应用运行的更快。
- obfuscate混淆: 使用短又没有语义的名字重命名非入口类的类名,变量名,方法名。入口类的名字保持不变。默认开启,增大反编译难度,类和类成员会被随机命名,除非用keep保护。
- preverify预校验: 预校验代码是否符合Java1.6或者更高的规范(唯一一个与入口类不相关的步骤)
配置文件
在工程应用目录的gradle文件,经常会看到以下配置。这表明对release版本进行混淆处理。(minifyEnabled true),在工程目录下会有一个proguard-rules.pro文件,我们就可以在其中加入混淆规则。
android {
...
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
keep配置-避免混淆
原则上,代码混淆后越乱越无规律越好,但有些地方我们是要避免混淆的,否则程序运行就会出错,所以我们要在需要的地方避免混淆。这里我们主要用到的配置为-keep
,用这个保证一些需要的地方避免混淆。
- 一颗星表示只是保持该包下的类名,而子包下的类名还是会被混淆
-keep class com.tencent.qq.*
- 两颗星表示把本包和所含子包下的类名都保持。
-keep class com.tencent.qq.**
用以上方法保持类后,你会发现类名虽然未混淆,但里面的具体方法和变量命名还是变了
- 既想保持类名,又想保持里面的内容不被混淆
-keep class com.tencent.qq.* {*;}
-keep class com.tencent.qq.** {*;}
- 避免所有继承Activity的类被混淆
-keep public class * extends android.app.Activity
- 避免所有实现 android.os.Parcelable的类被混淆
-keep class * implements android.os.Parcelable
- 如果一个类中你不希望保持全部内容不被混淆
- 保留一个类中的内部类不被混淆用符号$
- 只是希望保护类下的特定内容,就可以使用<init>; //匹配所有构造器<fields>; //匹配所有域<methods>; //匹配所有方法方法
- 还可以在<fields>或<methods>前面加上private 、public、native等来进一步指定不被混淆的内容。
例子
1.表示保持ScriptFragment内部类JavaScriptInterface中的所有public内容不被混淆
-keep class com.tencent.qq.fragment.ScriptFragment$JavaScriptInterface {
public *;
}
2.表示One类下的所有public方法都不会被混淆
-keep class cn.hadcn.test.One {
public <methods>;
}
3.加入参数,比如以下表示用JSONObject作为入参的构造函数不会被混淆
-keep class cn.hadcn.test.One {
public <init>(org.json.JSONObject);
}
- -keep是会保持类名的,如果不需要保持类名,只需要把该类下的特定方法保持不被混淆就好,那你就不能用-keep方法。需要用
keepclassmembers
这样就可以混淆类名的同时,只混淆某类的特定方法。
其他配置
-dontshrink
声明不压缩输入文件 (默认所有没有被引用到的类都会被删除,除了-keep相关配置指定的类。)
-dontoptimize
声明不优化输入文件。默认情况下,优化选项是开启的,并且所有的优化都是在字节码层进行的。
-dontobfuscate
声明不混淆。默认情况下,混淆是开启的。除了keep配置中声明的类,其它的类或者类的成员混淆后会改成简短随机的名字。
-optimizationpasses n
指定执行几次优化,默认情况下,只执行一次优化。执行多次优化可以提高优化的效果,但是,如果执行过一次优化之后没有效果,就会停止优化,剩下的设置次数不再执行。这个选项只在optimizate阶段有效
混淆注意事项
有一些是不能混淆的
- 关于反射:需要把反射调用的类,变量,方法设置为不混淆
- 与服务端交互时,使用GSON、fastjson等框架解析服务端数据时,所写的JSON对象类不混淆,否则无法将JSON解析成对应的对象;
- 使用第三方开源库或者引用其他第三方的SDK包时,如果有特别要求,也需要在混淆文件中加入对应的混淆规则;
- 有用到WebView的JS调用也需要保证写的接口方法不混淆,jni方法不可混淆。原因是这种方法需要和native方法保持一致。
- Parcelable的子类和Creator静态成员变量不混淆,否则会产生Android.os.BadParcelableException异常;
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
- 使用enum类型时需要注意避免以下两个方法混淆,因为enum类的特殊性,以下两个方法会被反射调用。
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
反编译看源代码
混淆后再次生成apk时,默认会在工程目录app/build/outputs/mapping/release下生成一个mapping.txt文件,这就是混淆规则,我们可以根据这个文件把混淆后的代码反推回源本的代码。
网友评论