proguard混淆

作者: Jinwong | 来源:发表于2016-04-18 14:13 被阅读1964次

    为了防止开发APP的重要code 被泄露,我们往往需要混淆(Obfuscation code ), 也就是把方法,字段,包和类这些java 元素的名称改成无意义的名称,这样代码结构没有变化,还可以运行,但是想弄懂代码的架构却很难。 proguard 就是这样的混淆工具,它可以分析一组class 的结构,根据用户的配置,然后把这些class 文件的可以混淆java 元素名混淆掉。在分析class 的,同时他还有其他两个功能,删除无效代码(Shrinking 收缩),和代码进行优化 (Optimization Options)。

    缺省情况下,proguard 会混淆所有代码,但是有些情况我们是不能改变java 元素的名称,否则就会这样就会导致程序出错。例如,用到反射的地方,我们代码依赖于系统的接口,比如被系统代码调用的回调方法,这种情况最复杂。这就需要我们在proguard-rules文件中告诉proguard 哪些java 元素是不能混淆的。

    1. ProGuard配置

    ProGuard关键字

    保留选项(配置不进行处理的内容)

    -keep {Modifier} {class_specification} 保留类和类中的成员,防止它们被混淆或移除。
    -keepclassmembers {modifier} {class_specification} 保护指定类的成员,如果此类受到保护他们会保护的更好
    -keepclasseswithmembers {class_specification} 保留类和类中的成员,防止它们被混淆或移除,前提是指名的类中的成员必须存在,如果不存在则还是会混淆。
    -keepnames {class_specification} 保留类和类中的成员,防止它们被混淆,但当成员没有被引用时会被移除。
    -keepclassmembernames {class_specification} 只保留类中的成员,防止它们被混淆,但当成员没有被引用时会被移除。
    -keepclasseswithmembernames {class_specification} 保留类和类中的成员,防止它们被混淆,但当成员没有被引用时会被移除,前提是指名的类中的成员必须存在,如果不存在则还是会混淆。

    压缩

    -dontshrink 不压缩输入的类文件
    -whyareyoukeeping {class_specification}

    优化

    -dontoptimize 不优化输入的类文件
    -assumenosideeffects {class_specification} 优化时假设指定的方法,没有任何副作用
    -allowaccessmodification 优化时允许访问并修改有修饰符的类和类的成员

    混淆

    -dontobfuscate 不混淆输入的类文件
    -obfuscationdictionary {filename} 使用给定文件中的关键字作为要混淆方法的名称
    -overloadaggressively 混淆时应用侵入式重载
    -useuniqueclassmembernames 确定统一的混淆类的成员名称来增加混淆
    -flattenpackagehierarchy {package_name} 重新包装所有重命名的包并放在给定的单一包中
    -repackageclass {package_name} 重新包装所有重命名的类文件中放在给定的单一包中
    -dontusemixedcaseclassnames 混淆时不会产生形形色色的类名
    -keepattributes {attribute_name,…} 保护给定的可选属性,例如LineNumberTable, LocalVariableTable, SourceFile, Deprecated, Synthetic, Signature, and InnerClasses.
    -renamesourcefileattribute {string} 设置源文件中给定的字符串常量

    输出:

    -printseeds {filename} 列出类和类的成员-keep选项的清单,标准输出到给定的文件
    -printusage {filename}

    通配符

    通配符 描述
    <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)这些方法。
    • 在android中在android Manifest文件中的activity,service,provider, receviter,等都不能进行混淆。一些在xml中配置的view也不能进行混淆,android提供的默认配置中都有。
    • keep和keepclasseswithmembers区别在于:keep关键字看到class *就认为应该将所有类名进行保留,不关心{}中的参数。而keepclasseswithmembers前提是{}的类中的成员必须存在,如果不存在则还是会混淆

    不能混淆的东西

    (1)Android 程序 ,下面这样代码混淆的时候要注意保留。
    (2)Android系统组件,系统组件有固定的方法被系统调用。
    (3) 被Android Resource 文件引用到的。名字已经固定,也不能混淆,比如自定义的View 。
    (4)Android Parcelable ,需要使用android 序列化的。
    (5) android.app.backup.BackupAgentHelper
    (6) android.preference.Preference
    (7) com.android.vending.licensing.ILicensingService
    (8) Java序列化方法,系统序列化需要固定的方法。
    (9) 枚举 ,系统需要处理枚举的固定方法。
    (10)native 本地方法,不能修改本地方法名
    (11)annotations 注释
    (12)数据库驱动
    (13)有些resource 文件
    (14)用到反射的地方,比如调用aidl

    常见问题

    1、打包过程中,提示 Warning: can't find superclass or interface/ Warning: can't find referenced class等警告信息!
    解决方法:
    (1) 确保你的代码是否使用这个类,如果使用了,查看对应的第三方包有没有加上去,并且是否拉下来存在项目中
    (2)如果该类存在,工程中确实使用了这个类,就在proguard中加上-keep class com.xx.yy.** { *;},让当前类不混淆。
    (3) 确保报错的类没有在你的项目中使用到,可以使用"-dontwarn 类名正则表达式"屏蔽警告。
    2、程序中使用泛型导致运行错误!
    使用的泛型需要在混淆配置文件加了一个过滤泛型的语句,如下。
    -keepattributes Signature

    ProGuard的输出文件及用处

    混淆之后,会给我们输出一些文件,在gradle方式下是在<project_dir>/build/proguard/</project_dir>目录下,ant是在<project_dir>/bin/proguard</project_dir>目录,eclipse构建在<project_dir>/proguard</project_dir>目录像。
    分别有以下文件:

    • dump.txt 描述apk文件中所有类文件间的内部结构。
    • mapping.txt 列出了原始的类,方法,和字段名与混淆后代码之间的映射。
    • seeds.txt 列出了未被混淆的类和成员
    • usage.txt 列出了从apk中删除的代码

    当我们发布的release版本的程序出现bug时,可以通过以上文件(特别时mapping.txt)文件找到错误原始的位置,进行bug修改。同时,可能一开始的proguard配置有错误,也可以通过错误日志,根据这些文件,找到哪些文件不应该混淆,从而修改proguard的配置。

    注意:重新release编译后,这些文件会被覆盖,所以每次发布程序,最好都保存一份配置文件。一般去友盟统计等统计网站分析错误时,需要上传对应的mapping.txt才能看到错误信息对应的app原始类、方法、变量名。

    proguard-android.txt 说明

    # This is a configuration file for ProGuard.
    
    # http://proguard.sourceforge.net/index.html#manual/usage.html
    
    #混淆时不使用大小写混合类名
    -dontusemixedcaseclassnames
    
    #不跳过library中的非public的类
    -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).
    
    #不进行优化,建议使用此选项,因为根据proguard-android-optimize.txt中的描述,
    #优化可能会造成一些潜在风险,不能保证在所有版本的Dalvik上都正常运行。
    -dontoptimize
    
    #不进行预校验。这个预校验是作用在Java平台上的,Android平台上不需要这项功能,去掉之后还可以加快混淆速度。
    -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*
    
    #不混淆下面声明的两个类,这两个类我们基本也用不上,是接入Google原生的一些服务时使用的。
    -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
    
    #不混淆任何包含native方法的类的类名以及native方法名。
    -keepclasseswithmembernames class * {
        native <methods>;
    }
    
    # keep setters in Views so that animations can still work.
    
    # see http://proguard.sourceforge.net/manual/examples.html#beans
    
    #不混淆任何一个View中的setXxx()和getXxx()方法,因为属性动画需要有相应的setter和getter的方法实现,混淆了就无法工作了
    -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
    
    #不混淆Activity中参数是View的方法,因为有这样一种用法,在XML中配置android:onClick=”buttonClick”属性,
    #当用户点击该按钮时就会调用Activity中的buttonClick(View view)方法,如果这个方法被混淆的话就找不到了。
    -keepclassmembers class * extends android.app.Activity {
       public void *(android.view.View);
    }
    
    # For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
    
    #不混淆枚举中的values()和valueOf()方法,枚举我用的非常少
    -keepclassmembers enum * {
        public static **[] values();
        public static ** valueOf(java.lang.String);
    }
    
    #不混淆Parcelable实现类中的CREATOR字段,毫无疑问,CREATOR字段是绝对不能改变的,包括大小写都不能变,不然整个Parcelable工作机制都会失败
    -keepclassmembers class * implements android.os.Parcelable {
    
      public static final android.os.Parcelable$Creator CREATOR;
    
    }
    
    #不混淆R文件中的所有静态字段,我们都知道R文件是通过字段来记录每个资源的id的,字段名要是被混淆了,id也就找不着了
    -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.
    
    #对android.support包下的代码不警告,因为support包中有很多代码都是在高版本中使用的,如果我们的项目指定的版本比较低在打包时就会给予警告。不过support包中所有的代码都在版本兼容性上做足了判断,因此不用担心代码会出问题,所以直接忽略警告就可以了。
    -dontwarn android.support.**
    

    相关文章

      网友评论

        本文标题:proguard混淆

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