声明

这篇文章,借鉴参考了下面的两篇文章,算是一个自己对混淆这块的总结。
写给Android开发者的混淆使用手册
Android混淆打包那些事儿

混淆

简介

说到混淆,就要说到proGuard,Android的混淆是有proGuard来完成的,ProGuard是一个开源项目在SourceForge上进行维护。

流程

代码混淆是包含了代码压缩、优化、混淆等一系列行为的过程。如上图所示,混淆过程会有如下几个功能:

  • 压缩。移除无效的类、类成员、方法、属性等;
  • 优化。分析和优化方法的二进制代码,移除无用指令;根据proguard-android-optimize.txt中的描述,优化可能会造成一些潜在风险,不能保证在所有版本的Dalvik上都正常运行。
  • 混淆。把类名、属性名、方法名替换为简短且无意义的名称;
  • 预校验。添加预校验信息。这个预校验是作用在Java平台上的,Android平台上不需要这项功能,去掉之后还可以加快混淆速度。

这四个流程默认开启。

如何混淆

混淆配置

一般的,我们在build.gradle都会这样写:

buildTypes {release {zipAlignEnabled trueminifyEnabled true// 移除无用的resource文件shrinkResources trueproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'signingConfig signingConfigs.xxxxx}}
  1. minifyEnabled true设为true开启混淆,
  2. proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'设置混淆文件的配置。其中proguard-android.txt是Android自带的混淆配置,我们可以在SDK的sdk\tools\proguard目录找到该文件,里边配置了一些基本需要的混淆。proguard-rules.pro是我们要添加的自己的混淆配置,比如项目中引用了第三方的项目等。

proguard-android.txt的内容:

# This is a configuration file for ProGuard.
# http://proguard.sourceforge.net/index.html#manual/usage.html-dontusemixedcaseclassnames
-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).
-dontoptimize
-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*
-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
-keepclasseswithmembernames class * {    native <methods>;
}# keep setters in Views so that animations can still work.
# see http://proguard.sourceforge.net/manual/examples.html#beans
-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
-keepclassmembers class * extends android.app.Activity {   public void *(android.view.View);
}# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
-keepclassmembers enum * {
    public static **[] values();public static ** valueOf(java.lang.String);
}-keepclassmembers class * implements android.os.Parcelable {  public static final android.os.Parcelable$Creator CREATOR;
}-keepclassmembers class **.R$* {    public static <fields>;
}# The support library contains references to newer platform versions.
# Don't warn about those in case this app is linking against an older
# platform version.  We know about them, and they are safe.
-dontwarn android.support.**

# Understand the @Keep support annotation.
-keep class android.support.annotation.Keep

-keep @android.support.annotation.Keep class * {*;}

-keepclasseswithmembers class * {    @android.support.annotation.Keep <methods>;
}-keepclasseswithmembers class * {    @android.support.annotation.Keep <fields>;
}-keepclasseswithmembers class * {    @android.support.annotation.Keep <init>(...);
}

一般的proguard-android.txt文件中已经有的混淆配置,我们可以不再添加,当然写了也没有问题!

查看混淆结果

混淆过的包必须进行检查,避免因混淆引入的bug。

一方面,需要从代码层面检查。

使用上文的配置进行混淆打包后在·app/build/outputs/mapping/release/目录下会输出以下文件:

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

我们可以根据 seeds.txt 文件检查未被混淆的类和成员中是否已包含所有期望保留的,再根据 usage.txt 文件查看是否有被误移除的代码。

另一方面,需要从测试方面检查

将混淆过的包进行全方面测试,检查是否有 bug 产生。这时候解出混淆栈

混淆后的类、方法名等等难以阅读,这固然会增加逆向工程的难度,但对追踪线上 crash 也造成了阻碍。我们拿到 crash 的堆栈信息后会发现很难定位,这时需要将混淆反解。

sdk/tools/proguard/路径下有附带的的反解工具(Window 系统为 proguardgui.bat,Mac 或 Linux 系统为 proguardgui.sh)。

这里以 Window 平台为例。双击运行proguardgui.bat后,可以看到左侧的一行菜单。点击 ReTrace,选择该混淆包对应的 mapping 文件(混淆后在 app/build/outputs/mapping/release/路径下会生成 mapping.txt文件,它的作用是提供混淆前后类、方法、类成员等的对照表),再将 crash 的 stack trace 黏贴进输入框中,点击右下角的 ReTrace ,混淆后的堆栈信息就显示出来了。

以上使用 GUI 程序进行操作,另一种方式是利用该路径下的 retrace 工具通过命令行进行反解,命令是

retrace.bat|retrace.sh [-verbose] mapping.txt [<stacktrace_file>]

例如:

retrace.bat -verbose mapping.txt obfuscated_trace.txt

自定义的混淆配置

保持元素不被混淆的相关命令

  • -keep 防止类和成员被移除或者被重命名
  • -keepnames 防止类和成员被重命名,但当成员没有被引用时会被移除
  • -keepclassmembers 防止成员被移除或者被重命名
  • -keepclassmembernames 防止成员被重命名,但当成员没有被引用时会被移除
  • -keepclasseswithmembers 防止拥有该成员的类和成员被移除或者被重命名
  • -keepclasseswithmembernames 防止拥有该成员的类和成员被重命名

保持元素不被混淆的相关规则

规则形如:

[保持命令] [类] {[成员]
}

保持命令:就是指上边说的几个保持命令
:类相关的限定条件,它将最终定位到某些符合该限定条件的类。它的内容可以使用:

  • 具体的类
  • 访问修饰符(public、protected、private)
  • 通配符*,匹配任意长度字符,但不含包名分隔符(.)
  • 通配符**,匹配任意长度字符,并且包含包名分隔符(.)
  • extends,即可以指定类的基类
  • implement,匹配实现了某接口的类
  • $,内部类

成员:代表类成员相关的限定条件,它将最终定位到某些符合该限定条件的类成员。它的内容可以使用:

  • <init> 匹配所有构造器
  • <fields> 匹配所有域
  • <methods> 匹配所有方法
  • 通配符*,匹配任意长度字符,但不含包名分隔符(.)
  • 通配符**,匹配任意长度字符,并且包含包名分隔符(.)
  • 通配符***,匹配任意参数类型
  • ,匹配任意长度的任意类型参数。比如void test(…)就能匹配任意 void test(String a) 或者是 void test(int a, String b) 这些方法。
  • 访问修饰符(public、protected、private)

通用的混淆规则

混淆的时候,可以直接复制:

#不优化输入的类文件
-dontoptimize

-dontwarn android.support.**

#keep相关注解
-keep class android.support.annotation.Keep

-keep @android.support.annotation.Keep class * {*;}

-keepclasseswithmembers class * {    @android.support.annotation.Keep <methods>;
}-keepclasseswithmembers class * {    @android.support.annotation.Keep <fields>;
}-keepclasseswithmembers class * {    @android.support.annotation.Keep <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

#将文件来源重命名为“SourceFile”字符串
-renamesourcefileattribute SourceFile

# 抛出异常时保留代码行号
-keepattributes SourceFile,LineNumberTable

# 保留所有的本地native方法不被混淆
-keepclasseswithmembernames class * {    native <methods>;
}#把混淆类中的方法名也混淆了
-useuniqueclassmembernames

#优化时允许访问并修改有修饰符的类和类的成员
-allowaccessmodification

# 保留了继承自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
#Fragment不需要在AndroidManifest.xml中注册,需要额外保护下
-keep public class * extends android.support.v4.app.Fragment
-keep public class * extends android.app.Fragment

# 保持测试相关的代码
-dontnote junit.framework.**
-dontnote junit.runner.**
-dontwarn android.test.**
-dontwarn android.support.test.**
-dontwarn org.junit.**

# 保留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.test.beans.** {*;}

#忽略get和set方法
-keep class com.test.beans.** {    public void set*(***);public *** get*();public *** is*();
}
#以上两种任意一种都行#对于内部类,$就是用来分割内嵌类和母体的标志
#-keep class com.test.**$*{*;}#保留support下的所有类及其内部类
-keep class android.support.** {*;}

#不需要提示 警告
-dontwarn android.support.**

# support-v7-appcompat
-keep public class android.support.v7.widget.** { *; }
-keep public class android.support.v7.internal.widget.** { *; }
-keep public class android.support.v7.internal.view.menu.** { *; }
-keep public class * extends android.support.v4.view.ActionProvider {    public <init>(android.content.Context);
}# support-design
-dontwarn android.support.design.**
-keep class android.support.design.** { *; }
-keep interface android.support.design.** { *; }
-keep public class android.support.design.R$* { *; }

其他混淆规则

OkHttp3

-dontwarn okhttp3.logging.**
-keep class okhttp3.internal.**{*;}
-dontwarn okio.**

Retrofit2.x

-dontwarn retrofit2.**
-keep class retrofit2.** { *; }

RxJava RxAndroid

-dontwarn sun.misc.**
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {long producerIndex;long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {rx.internal.util.atomic.LinkedQueueNode producerNode;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {rx.internal.util.atomic.LinkedQueueNode consumerNode;
}

Gson

-keep class com.google.gson.** { *; }
-keepattributes EnclosingMethod

Glide

-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {**[] $VALUES;public *;
}
# for DexGuard only
-keepresourcexmlelements manifest/application/meta-data@value=GlideModule

ButterKnife

-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }

-keepclasseswithmembernames class * {    @butterknife.* <fields>;
}-keepclasseswithmembernames class * {    @butterknife.* <methods>;
}

资源压缩

资源压缩将移除项目及依赖的库中未被使用的资源,这在减少 apk 包体积上会有不错的效果,一般在打realease包的时候建议开启。
具体做法是在 build.grade 文件中,将shrinkResources属性设置为 true。需要注意的是,只有在用minifyEnabled true开启了代码压缩后,资源压缩才会生效。

资源压缩包含了“合并资源”和“移除资源”两个流程。

合并资源

“合并资源”流程中,名称相同的资源被视为重复资源会被合并。需要注意的是,这一流程不受shrinkResources属性控制,也无法被禁止, gradle 必然会做这项工作,因为假如不同项目中存在相同名称的资源将导致错误。gradle 在四处地方寻找重复资源:
①src/main/res/ 路径
②不同的构建类型(debug、release等等)
③不同的构建渠道
④项目依赖的第三方库

合并资源时按照如下优先级顺序:
依赖 -> main -> 渠道 -> 构建类型
举个例子,
假如重复资源同时存在于main文件夹和不同渠道中,gradle 会选择保留渠道中的资源。同时,如果重复资源在同一层次出现,比如src/main/res/src/main/res2/,则 gradle 无法完成资源合并,这时会报资源合并错误。

移除资源

资源移除的时候,跟代码混淆一样,也可以定义哪些资源需要被保留

保持某些资源不被移除

shrinkResources true开启资源压缩后,所有未被使用的资源默认被移除。假如你需要定义哪些资源必须被保留,在 res/raw/ 路径下创建一个 xml 文件,例如 keep.xml。

通过一些属性的设置可以实现定义资源保持的需求,可配置的属性有:

  • tools:keep 定义哪些资源需要被保留(资源之间用“,”隔开)
  • tools:discard 定义哪些资源需要被移除(资源之间用“,”隔开)
  • tools:shrinkMode 开启严格模式

当代码中通过 Resources.getIdentifier() 用动态的字符串来获取并使用资源时,普通的资源引用检查就可能会有问题。例如,如下代码会导致所有以“img_”开头的资源都被标记为已使用。

String name = String.format("img_%1d", angle + 1);
res = getResources().getIdentifier(name, "drawable", getPackageName());

我们可以设置 tools:shrinkMode为 strict 来开启严格模式,使只有确实被使用的资源被保留。

以上就是自定义资源保持规则相关的配置,举个例子:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"tools:keep="@layout/l_used*_c,@layout/l_used_a,@layout/l_used_b*"tools:discard="@layout/unused2"tools:shrinkMode="strict"/>

移除替代资源

一些替代资源,例如多语言支持的 strings.xml,多分辨率支持的 layout.xml等,在我们不需要使用又不想删除掉时,可以使用资源压缩将它们移除。

我们使用 resConfig 属性来指定需要支持的属性,例如

android {defaultConfig {...resConfigs "en", "fr"}
}

其他未显式声明的语言资源将被移除。

好了,Android混淆相关就下总结到这里!
淘到一个在线的混淆的网站,可以参考一下!
https://proguard.herokuapp.com/

Android打包混淆压缩相关推荐

  1. Android 打包混淆

    转载地址:http://blog.sina.com.cn/s/blog_821e2bb10102vjya.html 第一步:开启混淆功能​ 取消project.properties里面关于progua ...

  2. android 打包 混淆配置_玩转代码混淆工具:ProGuard

    维基百科对ProGuard的介绍是:ProGuard是一个压缩(shrink),优化(optimize)与混淆(Obfuscate)Java代码的开源命令行工具.也就是说混淆只是ProGuard的其中 ...

  3. android 打包 混淆配置_android 实际项目中混淆文件的配置(参考做法)

    之前对项目混淆一直了解不是很透,趁着打包正式项目的时候有特意的去了解了一番,做些记录,备忘! -optimizationpasses 5 -dontusemixedcaseclassnames -do ...

  4. Android打包混淆----APP加密防破解 + 重新签名

    ① 准备一个打包过的app文件包 . ②  登陆移动APP加密防破解爱加密官网http://www.ijiami.cn,没有账号的需要先注册账号(登陆后才能上传App文件进行加密). ③ 注册完后,登 ...

  5. Android打包混淆----APP加密防破解 + 重新签名

    ~ ~  APP加密(混淆打包防反编译)的步骤:~ ~ ① 准备一个打包过的app文件包 . ②  登陆移动APP加密防破解爱加密官网http://www.ijiami.cn,没有账号的需要先注册账号 ...

  6. android studio防止反编译,防反编译利器-Android studio混淆代码压缩apk包体积

    前言 打包混淆对初学者小冷来说是很抗拒的,刚开始我只是知道在build.gradle文件 minifyEnabled下设置为true,可是proguard-rules.pro文件里面什么都没有,就这样 ...

  7. 带你彻底明白 Android Studio 打包混淆

    前言 在使用Android Studio混淆打包时,该IDE自身集成了Java语言的ProGuard作为压缩,优化和混淆工具,配合Gradle构建工具使用很简单.只需要在工程应用目录的gradle文件 ...

  8. Android studio 混淆、打包、验证是否成功

    大家好,跟大家分享一个MAC上混淆打包的经验: 前言: 单挑Android项目,最近即时通讯用到环信,集成sdk的时候 官方有一句 在 ProGuard 文件中加入以下 keep. ? 1 2 -ke ...

  9. android 混淆打包教程,Android studio 混淆打包

    AndroidStudio中的项目可以用compile的形式引入github上的开源项目,可以引用module,而不一定都要用libs文件夹中添加jar包的形式. 在最终realease打包时,混淆的 ...

最新文章

  1. Dlib库中实现正脸人脸关键点(landmark)检测的测试代码
  2. python多线程读取文件的问题_Python多线程同步---文件读写控制方法
  3. Java Swing:JPanel添加边框
  4. RHEL5系列之三:GNOME桌面的简单管理应用(1)
  5. 获得供应商最近一次报价:OVER(PARTITION BY)函数用法的实际用法
  6. GitLab修改用户密码
  7. python求定积分的函数_手搓计算化学(GTO积分by python)
  8. 2021中国低代码市场研究报告
  9. 【6】测试用例设计-输入域+输出域+异常分析+错误出错法
  10. 动手学习数据分析(一)——数据探索性分析
  11. 个人记录 Repast S network与触发器引发的bug
  12. 通用数据权限的设计思路
  13. SQLite 生成随机数字、字符串、日期、验证码以及 UUID
  14. Photoshop入门学习
  15. 第二章 SysML概览
  16. 中首清算|大数据助力灵活用工保驾护航
  17. em在聊天中是什么意思_emmmm是什么意思 聊天中emmmm是什么意思什么梗
  18. 谈谈水印实现的几种方式
  19. java 三原色_opencv3_java 提取图像的RGB三原色分割图像Split Core.split
  20. 浪潮服务器NF5280m5 配置,raid和系统 安装纪录

热门文章

  1. 盘点英特尔、苹果、高通、AMD 处理器重大 Bug,硬件的坑软件能填?
  2. Postgres 数据存储位置
  3. 实验六计算机网络通信Socket编程,计算机网络socket编程实验报告(3页)-原创力文档...
  4. DiscuzX3.1数据库字典(含之前)
  5. 相机标定推到与zc求解
  6. 利用Python爬虫网站数据直接导入mysql数据库中
  7. 提升自信心的十五个心理暗示法
  8. 共享单车登录显示服务器未响应,ofo共享单车出故障了吗?ofo共享单车无法登陆、连接不上、无法结算怎么回事?[图]...
  9. 拔毒化腐生肌药题库【1】
  10. 数据库-------关系数据库标准语言SQL