Android--ProGuard混淆

为什么要混淆?

增大反编译难度,类和类成员会被随机命名。

混淆的四个功能:

我们通常说的proguard包括四个功能,shrinker(压缩), optimizer(优化),obfuscator(混淆),preverifier(预校验)。

Android--ProGuard混淆_第1张图片
混淆的过程

  • 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
  • 如果一个类中你不希望保持全部内容不被混淆
    • 保留一个类中的内部类不被混淆用符号$
    • 只是希望保护类下的特定内容,就可以使用; //匹配所有构造器; //匹配所有域; //匹配所有方法方法
    • 还可以在前面加上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 ;
}
3.加入参数,比如以下表示用JSONObject作为入参的构造函数不会被混淆
-keep class cn.hadcn.test.One {
   public (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文件,这就是混淆规则,我们可以根据这个文件把混淆后的代码反推回源本的代码。

你可能感兴趣的:(Android--ProGuard混淆)