Android混淆实现

今天被一位小伙伴问混淆的问题了,他原来是使用360加固。其实混淆的好处很明显:

1.代码混淆后可阅读性降低,反编译后破译程序难度提高(第三方有被脱壳的风险)
2.混淆后字节数减少,减少了应用了体积(第三方加固会增加几十到几百K不等的体积)
所以我把混淆的具体实现总结了出来与大家分享。

一、build.gradle的配置

对于proguardFiles这部分有两段,前一部分代表系统默认的android程序的混淆文件,该文件已经包含了基本的混淆声明,免去了我们很多事,这个文件的目录在 /tools/proguard/proguard-android.txt , 后一部分是我们项目里的自定义的混淆文件,目录就在 app/proguard-rules.txt

下面是gradle配置:(注意,minifyEnabled置为false的情况下,zipAlignEnabled 和 shrinkResources 也要置为false,否则会编译报错)

buildTypes {
    release {
        // 是否进行混淆
        minifyEnabled true
        //不显示log
        buildConfigField "boolean", "LOG_DEBUG", "false"
        //Zipalign优化
        zipAlignEnabled true
        // 移除无用的resource文件
        shrinkResources true
        //签名文件
        signingConfig signingConfigs.release //在gradle中引用的签名文件
        //对应的混淆文件
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
    debug{
        debuggable true
        signingConfig signingConfigs.debug
    }

}


二、Android 混淆原则

1.AndroidMainfest中的类不混淆,四大组件和Application的子类和Framework层下所有的类默认不会进行混淆。

2.反射用到的类不混淆
3.JNI方法不混淆

4.Parcelable的子类和Creator的静态成员变量不能混淆,否则会产生BadParcelableException异常。

5.有用到webView的JS调用也需要保证写的接口方法不混淆。

6.使用GSON、fastjson等框架时,所写的JSON对象类(即bean包下的所有类)不混淆,否则无法将JSON解析成对应的对象。

7.使用第三方开源库或者引用其他第三方的SDK包时,需要在混淆文件中加入对应的混淆规则。

8.R类里及其所有内部static类中的所有static变量字段不混淆。

备注:ProGuard在线说明手册


三、混淆的常用语法

1.-libraryjars class_path 应用的依赖包,如android-support-v4
2.-keep [,modifier,...] class_specification 不混淆某些类
3.-keepclassmembers [,modifier,...] class_specification不混淆类的成员
4.-keepclasseswithmembers [,modifier,...]class_specification 不混淆类及其成员
5.-keepnames class_specification 不混淆类及其成员名
6.-keepclassmembernames class_specification 不混淆类的成员名
7.-keepclasseswithmembernames class_specification 不混淆类及其成员名
8.-assumenosideeffects class_specification 假设调用不产生任何影响,在proguard代码优化时会将该调用remove掉。如system.out.println和Log.v等等
9.-dontwarn [class_filter] 不提示warnning


四、混淆产生的文件

1. mapping.txt

-printmapping mapping.txt

表示混淆前后代码的对照表,这个文件非常重要。如果你的代码混淆后会产生bug的话,log提示中是混淆后的代码,希望定位到源代码的话就可以根据mapping.txt反推。
每次发布都要保留它方便该版本出现问题时调出日志进行排查,它可以根据版本号或是发布时间命名来保存或是放进代码版本控制中。

2. dump.txt

-dump class_files.txt

描述apk内所有class文件的内部结构。

3. seeds.txt

-printseeds seeds.txt

列出了没有被混淆的类和成员。

4. usage.txt

-printusage unused.txt

列出了源代码中被删除在apk中不存在的代码。

以上文件需要在android工程中混淆配置文件:proguard-rules.pro中配置

五、 项目混淆配置

proguard-rules.pro 文件的配置如下:
#指定代码的压缩(proguard对代码进行迭代优化的次数0-7)等级Android中一般设置为5
-optimizationpasses 5
#包名不使用大小写混合
-dontusemixedcaseclassnames
# 不混淆第三方引用的库
-dontskipnonpubliclibraryclasses
# 不做预校验
-dontpreverify
# 忽略警告
-ignorewarning
#apk 包内所有 class 的内部结构
-dump class_files.txt
#未混淆的类和成员
-printseeds seeds.txt
#列出从 apk 中删除的代码
-printusage unused.txt
#混淆前后的映射
-printmapping mapping.txt
# 保持Activity类及其子类不被混淆
-keep public class * extends android.app.Activity
# 保持Application类及其不被混淆
-keep public class * extends android.app.Application
# 保持Service类及其子类不被混淆
-keep public class * extends android.app.Service
# 保持BroadcastReceiver类及其不被混淆
-keep public class * extends android.content.BroadcastReceiver
# 保持ContentProvider类及其子类不被混淆
-keep public class * extends android.content.ContentProvider
# 保持BackupAgentHelper类及其子类不被混淆
-keep public class * extends android.app.backup.BackupAgentHelper
# 保持Preference类及其子类不被混淆
-keep public class * extends android.preference.Preference
# 保持Fragment类及其子类不被混淆
-keep public class * extends android.app.Fragment
#保持引用v4包下Fragment及其子类不被混淆
-keep public class * extends android.support.v4.app.Fragment
#保持 Serializable类及其实现类不被混淆
-keepnames class * implements java.io.Serializable
# 如果引用了v4或者v7-dontwarn android.support.**
# 保持 native 方法不被混淆
-keepclasseswithmembernames class * {
    native <methods>;
}
#保护注解
-keepattributes *Annotation*
-keep class * extends java.lang.annotation.Annotation {*;}
# 不混淆内部类
-keepattributes InnerClasses
#不混淆资源类即R文件
-keepclassmembers class **.R$* {
    public static <fields>;
}
#避免混淆泛型 如果混淆报错建议关掉
-keepattributes Signature
# 保持枚举 enum 类不被混淆
-keepclassmembers enum * {
   public static **[] values();
   public static ** valueOf(java.lang.String);
}
# 保持自定义控件类不被混淆
-keepclasseswithmembers class * {
   public <init>(android.content.Context, android.util.AttributeSet);
}
# 保持自定义控件类不被混淆
-keepclasseswithmembers class * {
   public <init>(android.content.Context, android.util.AttributeSet, int);
}
# 保持Activity中参数类型为View的所有方法
-keepclassmembers class * extends android.app.Activity {
   public void *(android.view.View);
}
#保持所有bean类(即bean包下所有类)都不被混淆
-keep class android.launcher.bean.**{
    *;
}
#所有View的子类及其子类的getset方法都不进行混淆
-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*(...);
  }
# 保持包含org.json.JSONObject参数的构造方法的类
-keepclassmembers class * {
   public <init> (org.json.JSONObject);
}
# 有用到WEBViewJS调用接口不被混淆
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
  public *;
}
#移除log 测试了下没有用还是建议自己定义一个开关控制是否输出日志
-assumenosideeffects class android.util.Log {
         public static boolean isLoggable(java.lang.String, int);
         public static int v(...);
         public static int i(...);
         public static int w(...);
         public static int d(...);
         public static int e(...);
  }
#友盟推送的混淆配置
-dontwarn com.taobao.**
-dontwarn anet.channel.**
-dontwarn anetwork.channel.**
-dontwarn org.android.**
-dontwarn org.apache.thrift.**
-dontwarn com.xiaomi.**
-dontwarn com.huawei.**
-keep class com.taobao.** {*;}
-keep class org.android.** {*;}
-keep class anetwork.channel.**{*;}
-keep class anet.channel.** {*;}
-keep class com.umeng.** {*;}
-keep class com.xiaomi.** {*;}
-keep class com.huawei.** {*;}
-keep class org.apache.thrift.** {*;}
-keep public class **.R$*{
   public static final int *;
}
#友盟统计的混淆配置1R文件的混淆保护2、枚举混淆3JSON混淆
#因为上面都有配置故不做重复配置

#展示互动混淆配置
-dontwarn com.gensee.**
-keep class com.gensee.**{*;}
#universal-image-loader-keep class com.nostra13.universalimageloader.** { *; }
#CC视频 jar-dontwarn com.bokecc.sdk.mobile.**
-keep public class com.bokecc.sdk.mobile.**{*;}
-keep public interface com.bokecc.sdk.mobile.**{*;}
#混淆保护自己项目的部分代码以及引用的第三方jarlibrary
#-libraryjars libs/universal-image-loader-1.9.5.jar
#-libraryjars libs/opensdk.jar
#-libraryjars libs/butelCommConnect.jar
#自定义的下拉刷新GridView
-keep class com.chanven.lib.cptr.**{*;}
#Volley
-dontwarn com.android.volley.jar.**
-keep class com.android.volley.**{*;}

注意:javabean文件一定要keep掉,不能被混淆。


六、多渠道打包方法

我们打开要打包的module下的build配置文件这里会有两个方法,一个普通方法,一个快捷方法。

1. 普通方法:

roductFlavors {

                wandoujia {

                            manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"]

                }

                baidu {

                           manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"]

                }

                c360 {

                           manifestPlaceholders = [UMENG_CHANNEL_VALUE: "c360"]

                }

                uc {

                         manifestPlaceholders = [UMENG_CHANNEL_VALUE: "uc"]

                 }

     }

2:快捷方法

productFlavors { 
            wandoujia {}  
            baidu {}
            c360 {}
            ...
            roductFlavors.all { flavor ->
                flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
            } 
       }

注意:UMENG_CHANNEL_VALUE ,就是动态修改Androidmanifest中的value,分不同的渠道。

七、 命令行打包配置

首先要配置好gradle的环境变量方便我们在Terminal的方式(cmd应该也可以,没试过),运行打包命令,如果不知道的话,我先贴出配置方法吧(其实跟java配置环境变量一样一样的)

先找到gralde的根目录,在系统变量里添加两个环境变量:
  变量名为:GRADLE_HOME,变量值就为gradle的根目录;
  所以变量值为:C:\Users\ping\.gradle\wrapper\dists\gradle-2.1-all\27drb4udbjf4k88eh2ffdc0n55\gradle-2.1
  还有一个在系统变量里PATH里面添加gradle的bin目录
  我的就是C:\Users\ping\.gradle\wrapper\dists\gradle-2.1-all\27drb4udbjf4k88eh2ffdc0n55\gradle-2.1\bin
这里配置完成了,接着在Terminal中敲下 gradle assembleRelease 就可以一次性生成所有的渠道包了。所有生成的apk在项目的 build\outputs\apk 下。


八、 基线版本保存

在打包完成后需要保存基线版本,关于基线版本需要保存的文件说明:

1.如果项目集成了tinker那么需要保留app/build/bakApk文件夹,留作热部署时的基线参考。

2.项目如果集成了友盟统计那么需要保留app/build/outputs文件夹,主要保留mapping文件,因为mapping文件存储了代码混淆对应。通过友盟统计上传对应版本的mapping文件能准确定位发生bug的位置。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值