Android打包混淆压缩
声明
这篇文章,借鉴参考了下面的两篇文章,算是一个自己对混淆这块的总结。
写给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}}
minifyEnabled true
设为true开启混淆,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打包混淆压缩相关推荐
- Android 打包混淆
转载地址:http://blog.sina.com.cn/s/blog_821e2bb10102vjya.html 第一步:开启混淆功能 取消project.properties里面关于progua ...
- android 打包 混淆配置_玩转代码混淆工具:ProGuard
维基百科对ProGuard的介绍是:ProGuard是一个压缩(shrink),优化(optimize)与混淆(Obfuscate)Java代码的开源命令行工具.也就是说混淆只是ProGuard的其中 ...
- android 打包 混淆配置_android 实际项目中混淆文件的配置(参考做法)
之前对项目混淆一直了解不是很透,趁着打包正式项目的时候有特意的去了解了一番,做些记录,备忘! -optimizationpasses 5 -dontusemixedcaseclassnames -do ...
- Android打包混淆----APP加密防破解 + 重新签名
① 准备一个打包过的app文件包 . ② 登陆移动APP加密防破解爱加密官网http://www.ijiami.cn,没有账号的需要先注册账号(登陆后才能上传App文件进行加密). ③ 注册完后,登 ...
- Android打包混淆----APP加密防破解 + 重新签名
~ ~ APP加密(混淆打包防反编译)的步骤:~ ~ ① 准备一个打包过的app文件包 . ② 登陆移动APP加密防破解爱加密官网http://www.ijiami.cn,没有账号的需要先注册账号 ...
- android studio防止反编译,防反编译利器-Android studio混淆代码压缩apk包体积
前言 打包混淆对初学者小冷来说是很抗拒的,刚开始我只是知道在build.gradle文件 minifyEnabled下设置为true,可是proguard-rules.pro文件里面什么都没有,就这样 ...
- 带你彻底明白 Android Studio 打包混淆
前言 在使用Android Studio混淆打包时,该IDE自身集成了Java语言的ProGuard作为压缩,优化和混淆工具,配合Gradle构建工具使用很简单.只需要在工程应用目录的gradle文件 ...
- Android studio 混淆、打包、验证是否成功
大家好,跟大家分享一个MAC上混淆打包的经验: 前言: 单挑Android项目,最近即时通讯用到环信,集成sdk的时候 官方有一句 在 ProGuard 文件中加入以下 keep. ? 1 2 -ke ...
- android 混淆打包教程,Android studio 混淆打包
AndroidStudio中的项目可以用compile的形式引入github上的开源项目,可以引用module,而不一定都要用libs文件夹中添加jar包的形式. 在最终realease打包时,混淆的 ...
最新文章
- Dlib库中实现正脸人脸关键点(landmark)检测的测试代码
- python多线程读取文件的问题_Python多线程同步---文件读写控制方法
- Java Swing:JPanel添加边框
- RHEL5系列之三:GNOME桌面的简单管理应用(1)
- 获得供应商最近一次报价:OVER(PARTITION BY)函数用法的实际用法
- GitLab修改用户密码
- python求定积分的函数_手搓计算化学(GTO积分by python)
- 2021中国低代码市场研究报告
- 【6】测试用例设计-输入域+输出域+异常分析+错误出错法
- 动手学习数据分析(一)——数据探索性分析
- 个人记录 Repast S network与触发器引发的bug
- 通用数据权限的设计思路
- SQLite 生成随机数字、字符串、日期、验证码以及 UUID
- Photoshop入门学习
- 第二章 SysML概览
- 中首清算|大数据助力灵活用工保驾护航
- em在聊天中是什么意思_emmmm是什么意思 聊天中emmmm是什么意思什么梗
- 谈谈水印实现的几种方式
- java 三原色_opencv3_java 提取图像的RGB三原色分割图像Split Core.split
- 浪潮服务器NF5280m5 配置,raid和系统 安装纪录
热门文章
- 盘点英特尔、苹果、高通、AMD 处理器重大 Bug,硬件的坑软件能填?
- Postgres 数据存储位置
- 实验六计算机网络通信Socket编程,计算机网络socket编程实验报告(3页)-原创力文档...
- DiscuzX3.1数据库字典(含之前)
- 相机标定推到与zc求解
- 利用Python爬虫网站数据直接导入mysql数据库中
- 提升自信心的十五个心理暗示法
- 共享单车登录显示服务器未响应,ofo共享单车出故障了吗?ofo共享单车无法登陆、连接不上、无法结算怎么回事?[图]...
- 拔毒化腐生肌药题库【1】
- 数据库-------关系数据库标准语言SQL