Android--ProGuard混淆

作者: 芒果味的你呀 | 来源:发表于2017-12-11 17:00 被阅读37次

    为什么要混淆?

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

    混淆的四个功能:

    我们通常说的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文件,这就是混淆规则,我们可以根据这个文件把混淆后的代码反推回源本的代码。

    相关文章

      网友评论

        本文标题:Android--ProGuard混淆

        本文链接:https://www.haomeiwen.com/subject/ylciixtx.html