美文网首页
Android混淆规则

Android混淆规则

作者: 132f026dbd31 | 来源:发表于2017-12-15 23:19 被阅读0次

    简介

    Java代码是非常容易反编译的。为了很好的保护Java源代码,我们往往会对编译好的class文件进行混淆处理。
    ProGuard是一个混淆代码的开源项目。它的主要作用就是混淆,当然它还能对字节码进行缩减体积、优化等,但那些对于我们来说都算是次要的功能。
    官网地址:http://proguard.sourceforge.net/

    原理

    Java 是一种跨平台的、解释型语言,Java 源代码编译成中间”字节码”存储于 class 文件中。由于跨平台的需要,Java 字节码中包括了很多源代码信息,如变量名、方法名,并且通过这些名称来访问变量和方法,这些符号带有许多语义信息,很容易被反编译成 Java 源代码。为了防止这种现象,我们可以使用 Java 混淆器对 Java 字节码进行混淆。

    混淆就是对发布出去的程序进行重新组织和处理,使得处理后的代码与处理前代码完成相同的功能,而混淆后的代码很难被反编译,即使反编译成功也很难得出程序的真正语义。被混淆过的程序代码,仍然遵照原来的档案格式和指令集,执行结果也与混淆前一样,只是混淆器将代码中的所有变量、函数、类的名称变为简短的英文字母代号,在缺乏相应的函数名和程序注释的况下,即使被反编译,也将难以阅读。同时混淆是不可逆的,在混淆的过程中一些不影响正常运行的信息将永久丢失,这些信息的丢失使程序变得更加难以理解。

    混淆器的作用不仅仅是保护代码,它也有精简编译后程序大小的作用。由于以上介绍的缩短变量和函数名以及丢失部分信息的原因, 编译后 jar 文件体积大约能减少25% ,这对当前费用较贵的无线网络传输是有一定意义的。

    Android 混淆

    Android 混淆代码直接在Android Studio 中配置

    android {
        buildTypes {
            release {
                shrinkResources true
                minifyEnabled true
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    }
    

    由于混淆会增加编译时间,并且debug时编译器无法定位到具体位置,所以debug环境下不建议打开。

    • 代码混淆
      配置如 minifyEnabled true
      代码混淆包括四个步骤,代码压缩,优化,混淆,预校验。

      1. 压缩(shrink) : 移除无效的类,类成员,方法,属性等。
      2. 优化(optimize) : 分析和优化二进制代码,根据proguard-android-optimize.txt中的描述,优化可能会造成一定的风险,不能保证在所有版本的Dalvik上正常运行。因此android项目建议关闭该项。
      3. 混淆(obfuscate) : 把类名,属性名, 方法名替换为简短且无意义的名称。
      4. 预校验(previrfy) : 添加预校验信息。 这个预校验是作用在java 平台的, Android 平台上不需要这项功能,去掉之后还可以加快混淆速度。因此android 项目关闭该项。

      这四个流程默认是开启的, 我们需要在Android混淆配置文件中关闭代码优化,和预校验功能,即-dontoptimize, -dontpreverify. 当然proguard-android.txt中默认已经关闭这两项了。

      项目混淆工作流程图
    • 资源混淆
      配置如 shrinkResources true
      压缩资源将移除项目及依赖库中未被使用的资源,可以减少apk的大小,一般建议开启。
      资源处理包括合并资源,移除资源两个流程。

      1. Android Studio 打包时会自动进行merge资源,不受该参数控制。
      2. 移除资源,需要配置该参数。不过建议可以使用lint先自行过一遍。

      还有一种为资源名称的混淆。由facebook开发的redex。
      参考 基于 Facebook Redex 实现 Android APK 的压缩和优化

    java 混淆

    如果要将输出的jar包进行混淆,可以用proguard工具进行操作。分为命令行式和图像界面工具。可以去网上下载或者 android sdk提供了一个图形化工具proguard,可以进行混淆。路径如下 /sdk/tools/proguard/lib/proguardgui.jar 双击打开即可。
    在其中配置相关的混淆参数即可。

    代码混淆规则

    Android项目中配置如下proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 。 由android sdk 中提供的默认混淆规则proguard-android.txt 和 用户自定义的混淆规则proguard-rules.pro 两部分组成。

    命令 作用
    -keep 防止类和成员被移除或者被重命名
    -keepnames 防止类和成员被重命名
    -keepclassmembers 防止成员被移除或者被重命名
    -keepclassmembersname 防止成员被重命名
    -keepclasseswithmembers 防止拥有该成员的类和成员被移除或者被重命名
    -keepclasseswithmembernames 防止拥有该成员的类和成员被重命名
    类通配符 * 匹配任意长度字符,但不含包名分隔符(.)
    类通配符 ** 匹配任意长度字符,并且包含包名分隔符(.)
    类extends 即可以指定类的基类
    类implements 匹配实现了某接口的类
    类$ 内部类
    成员(方法)通配符 * 匹配任意长度字符,但不含包名分隔符(.)
    成员(方法)通配符 ** 匹配任意长度字符,并且包含包名分隔符(.)
    成员(方法)通配符 *** 匹配任意参数类型
    成员(方法)通配符 ... 匹配任意长度的任意类型参数
    成员(方法)通配符 <> 匹配方法名,eg. <init>
    • 常见混淆命令
    # 代码混淆压缩比,在0~7之间,默认为5,一般不下需要修改
    -optimizationpasses 5
    
    # 混淆时不使用大小写混合,混淆后的类名为小写
    # windows下的同学还是加入这个选项吧(windows大小写不敏感)
    -dontusemixedcaseclassnames
    
    # 指定不去忽略非公共的库的类
    # 默认跳过,有些情况下编写的代码与类库中的类在同一个包下,并且持有包中内容的引用,此时就需要加入此条声明
    -dontskipnonpubliclibraryclasses
    
    # 指定不去忽略非公共的库的类的成员
    -dontskipnonpubliclibraryclassmembers
    
    # 不做预检验,preverify是proguard的四个步骤之一
    # Android不需要preverify,去掉这一步可以加快混淆速度
    -dontpreverify
    
    # 有了verbose这句话,混淆后就会生成映射文件
    # 包含有类名->混淆后类名的映射关系
    # 然后使用printmapping指定映射文件的名称
    -verbose
    -printmapping priguardMapping.txt
    
    # 指定混淆时采用的算法,后面的参数是一个过滤器
    # 这个过滤器是谷歌推荐的算法,一般不改变
    -optimizations !code/simplification/artithmetic,!field/*,!class/merging/*
    
    # 保护代码中的Annotation不被混淆
    # 这在JSON实体映射时非常重要,比如fastJson
    -keepattributes *Annotation*
    
    # 避免混淆泛型
    # 这在JSON实体映射时非常重要,比如fastJson
    -keepattributes Signature
    
    # 抛出异常时保留代码行号
    -keepattributes SourceFile,LineNumberTable
    
    • keep 元素不进行混淆规则
    # 保留所有的本地native方法不被混淆
    -keepclasseswithmembernames class * {
        native <methods>;
    }
    
    # 保留了继承自Activity、Application这些类的子类
    # 因为这些子类有可能被外部调用
    # 比如第一行就保证了所有Activity的子类不要被混淆
    -keep public class * extends android.app.Activity
    -keep public class * extends android.app.Application
    -keep public class * extends android.app.Service
    -keep public class * extends android.content.BroadcastReceiver
    -keep public class * extends android.content.ContentProvider
    -keep public class * extends android.app.backup.BackupAgentHelper
    -keep public class * extends android.preference.Preference
    -keep public class * extends android.view.View
    -keep public class com.android.vending.licensing.ILicensingService
    
    # 如果有引用android-support-v4.jar包,可以添加下面这行
    -keep public class com.null.test.ui.fragment.** {*;}
    
    # 保留Activity中的方法参数是view的方法,
    # 从而我们在layout里面编写onClick就不会影响
    -keepclassmembers class * extends android.app.Activity {
        public void * (android.view.View);
    }
    
    # 枚举类不能被混淆
    -keepclassmembers enum * {
        public static **[] values();
        public static ** valueOf(java.lang.String);
    }
    
    # 保留自定义控件(继承自View)不能被混淆
    -keep public class * extends android.view.View {
        public <init>(android.content.Context);
        public <init>(android.content.Context, android.util.AttributeSet);
        public <init>(android.content.Context, android.util.AttributeSet, int);
        public void set*(***);
        *** get* ();
    }
    
    # 保留Parcelable序列化的类不能被混淆
    -keep class * implements android.os.Parcelable{
        public static final android.os.Parcelable$Creator *;
    }
    
    # 保留Serializable 序列化的类不被混淆
    -keepclassmembers class * implements java.io.Serializable {
       static final long serialVersionUID;
       private static final java.io.ObjectStreamField[] serialPersistentFields;
       !static !transient <fields>;
       private void writeObject(java.io.ObjectOutputStream);
       private void readObject(java.io.ObjectInputStream);
       java.lang.Object writeReplace();
       java.lang.Object readResolve();
    }
    
    # 对R文件下的所有类及其方法,都不能被混淆
    -keepclassmembers class **.R$* {
        *;
    }
    
    # 对于带有回调函数onXXEvent的,不能混淆
    -keepclassmembers class * {
        void *(**On*Event);
    }
    
    • 其他自定义混淆规则
    #内部类
    -keep class com.null.test.MainActivity$* {
        *;
    }
    #webview
    -keepclassmembers class * extends android.webkit.WebViewClient {
        public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
        public boolean *(android.webkit.WebView, java.lang.String);
    }
    -keepclassmembers class * extends android.webkit.WebViewClient {
        public void *(android.webkit.WebView, java.lang.String);
    }
    
    #第三方库
    -libraryjars ./libs/android-support-v4.jar
    -dontwarn android.support.v4.** 
    -dontwarn **CompatHoneycomb
    -dontwarn **CompatHoneycombMR2
    -dontwarn **CompatCreatorHoneycombMR2
    -keep interface android.support.v4.app.** { *; }
    -keep class android.support.v4.** { *; }
    -keep public class * extends android.support.v4.**
    -keep public class * extends android.app.Fragment
    

    资源保持规则

    用shrinkResources true开启资源压缩后,所有未被使用的资源默认被移除。假如你需要定义哪些资源必须被保留,在 res/raw/ 路径下创建一个 xml 文件,例如 keep.xml。
    通过一些属性的设置可以实现定义资源保持的需求,可配置的属性有:

    • tools:keep 定义哪些资源需要被保留(资源之间用“,”隔开)

    • tools:discard 定义哪些资源需要被移除(资源之间用“,”隔开)

    • tools:shrinkMode 开启严格模式

    mapping文件

    混淆过的包必须进行检查,避免因混淆引入的bug。
    混淆后会在 路径/build/outputs/mapping/release/ 目录下会输出以下文件:

    • dump.txt 描述apk文件中所有类的内部结构
    • mapping.txt 提供混淆前后类,方法,类成员等的对照表
    • seeds.txt 列出没有被混淆的类和成员
    • usage.txt 列出被移除的代码

    一般发布包的时候都要保留mapping.txt 文件。 目的有有两个, 1. 当出现线上crash时候,可以根据该mapping文件对照源码找到对应的位置。2. 当发布补丁包时候,也会指定该mapping文件,已避免补丁包无法使用。

    • 解出混淆栈
      sdk/tools/proguard路径下附带了反解工具proguardgui.sh 或者 proguardgui.jar.打开即可,看到左侧的retrace, 选择混淆包对应的mapping.txt 文件, 再将crash 的 stack trace 粘贴到输入框中, 点击retrace 混淆前的堆栈信息即可显示。
      命令行操作为:
      retrace.bat|retrace.sh [-verbose] mapping.txt [<stacktrace_file>]
      eg. retace.sh -verbose mapping.txt log.txt

    • 指定mapping文件
      增加proguard-path-rules.pro , 在其中指定-applymapping mapping.txt.

    相关文章

      网友评论

          本文标题:Android混淆规则

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