目录
1. Proguard 介绍
Android Studio 使用Proguard进行混淆,其是一个压缩、优化和混淆java字节码文件的一个工具。
功能:
- Shrinking(压缩);
- Optimization(优化);
- Obfuscattion(混淆);
- Preverification(预校验);
说明:
- shrink:检测并移除没有用到的类,变量,方法和属性;
- optimize:优化代码,非入口节点类会加上
private
/static
/final
, 没有用到的参数会被删除,一些方法可能会变成内联代码; - obfuscate:使用短又没有语义的名字重命名非入口类的类名,变量名,方法名。入口类的名字保持不变;
- preverify:预校验代码是否符合Java1.6或者更高的规范(唯一一个与入口类不相关的步骤);
除了proguard之外,还有一个DexGuard,是专门用来优化混淆Android应用的。它的功能包括资源混淆,字符串加密,类加密和dex文件分割等。它是在android编译的时候直接产生Dalvik字节码。
优点:
- 删除项目无用的资源,有效减小apk大小;
- 删除无用的类、类成员、方法和属性,还可以删除无用的注释,最大限度的优化字节码文件;
- 使用简短无意义的名称重命名已存在的类、方法、属性等,增加逆向工程的难度;
2. 配置
保留 | 防止被移除或者被重命名 | 防止被重命名 |
---|---|---|
类和类成员 | -keep | -keepnames |
仅类成员 | -keepmembers | -keepmembernames |
如果拥有某成员,保留类和类成员 | -keepclasseswithmembers | -keepclasseswithmembernames |
如果不确定自己该用哪个的话,就用-keep
吧,它能保证匹配的类在压缩这一阶段不被移除,并且在混淆阶段不会被重新命名。
- 如果只声明保护一个类,并没有指定受保护的成员。proguard只会保护它的类名和它的无参构造函数。其它成员依旧会被压缩、优化、混淆。
- 如果声明保护一个方法,proguard会把它当作程序的入口点,方法名不会变,但它里面的代码依旧会被优化、混淆。
proguard 中一共有三组六个keep关键字,很多人搞不清楚它们的区别,通过一个表格来直观地看下:
关键字 | 描述 |
---|---|
keep | 保留类和类中的成员,防止它们被混淆或移除 |
keepnames | 保留类和类中的成员,防止它们被混淆,但当成员没有被引用时会被移除 |
keepclassmembers | 只保留类中的成员,防止它们被混淆或移除 |
keepclassmembernames | 只保留类中的成员,防止它们被混淆,但当成员没有被引用时会被移除 |
keepclasseswithmembers | 保留类和类中的成员,防止它们被混淆或移除,前提是指名的类中的成员必须存在,如果不存在则还是会混淆 |
keepclasseswithmembernames | 保留类和类中的成员,防止它们被混淆,但当成员没有被引用时会被移除,前提是指名的类中的成员必须存在,如果不存在则还是会混淆 |
除此之外,proguard中的通配符也比较让人难懂,proguard-android.txt中就使用到了很多通配符,我们来看一下它们之间的区别:
通配符 | 描述 |
---|---|
<field> | 匹配类中的所有字段 |
<method> | 匹配类中的所有方法 |
<init> | 匹配类中的所有构造函数 |
* | 匹配任意长度字符,但不含包名分隔符(.)
|
** | 匹配任意长度字符,并且包含包名分隔符(.) |
*** | 匹配任意参数类型 |
… | 匹配任意长度的任意类型参数 |
buildTypes {
release {
// true - 打开混淆
minifyEnabled true
// true - 打开资源压缩
shrinkResources true
// 用于设置 Proguard的规划路径
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro',
'../xxxModules/proguard-rules.pro',
'../yyyModules/proguard-rules.pro',
'../zzzModules/proguard-rules.pro'
}
}
- proguard-android.txt:其中proguard-android.txt 是系统默认的混淆文件,具体在../sdk/tools/proguard/ 目录下,其中包含了 android 最基本的混淆,一般不需要改动;
- proguard-rules.pro:是我们需要配置的规则;
- 如果要配置多个Module的混淆文件,只需要后面添加逗号跟混淆文件路径;
2.1 基础混淆配置
# 代码混淆压缩比,在0~7之间,默认为5,一般不做修改
-optimizationpasses 5
# 混合时不使用大小写混合,混合后的类名为小写
-dontusemixedcaseclassnames
# 指定不去忽略非公共库的类
-dontskipnonpubliclibraryclasses
# 指定不去忽略非公共库的类成员
-dontskipnonpubliclibraryclassmembers
# 这句话能够使我们的项目混淆后产生映射文件
# 包含有类名->混淆后类名的映射关系
-verbose
# 不做预校验,preverify是proguard的四个步骤之一,Android不需要preverify,去掉这一步能够加快混淆速度。
-dontpreverify
# 保留Annotation不混淆
-keepattributes *Annotation*,InnerClasses
# 避免混淆泛型
-keepattributes Signature
# 抛出异常时保留代码行号
-keepattributes SourceFile,LineNumberTable
# 指定混淆是采用的算法,后面的参数是一个过滤器
# 这个过滤器是谷歌推荐的算法,一般不做更改
-optimizations !code/simplification/cast,!field/*,!class/merging/*
# 忽略警告
-ignorewarnings
# 设置是否允许改变作用域
-allowaccessmodification
# 把混淆类中的方法名也混淆了
-useuniqueclassmembernames
# apk 包内所有 class 的内部结构
-dump class_files.txt
# 未混淆的类和成员
-printseeds seeds_txt
# 列出从apk中删除的代码
-printusage unused.txt
# 混淆前后的映射
-printmapping mapping.txt
2.2 不能混淆的清单项
- 反射中使用的元素,需要保证类名、方法名、属性名不变,否则反射会有问题;
- 最好不让一些bean 类混淆;
- 四大组件不能混淆,四大组件必须在 manifest 中注册声明,而混淆后类名会发生更改,这样不符合四大组件的注册机制;
- 注解不能混淆,很多场景下注解被用于在进行时反射一些元素;
- 不能混淆枚举中的value和valueOf方法,因为这两个方法是静态添加到代码中进行,也会被反射使用,所以无法混淆这两种方法。应用使用枚举将添加很多方法,增加了包中的方法数,将增加 dex 的大小;
- JNI 调用 Java 方法,需要通过类名和方法名构成的地址形成;
- Java 使用 Native 方法,Native 是C/C++编写的,方法是无法一同混淆的;
- JS 调用Java 方法;
- WebView 中 JavaScript 的调用方法不能混淆(WebView 引用的是哪个包名下的);
- 第三方可建议使用其自身混淆规则;
- Parcelable 的子类和 Creator 的静态成员变量不混淆,否则会出现 android.os.BadParcelableExeception 异常;
- Serializable 接口类和反序列化;
- Gson 的序列号和反序列化,其实质上是使用反射获取类解析的;
3. File Filters
就像普通的匹配器一样,可以使用通配符来过滤文件名:
- ? 代表文件名中的一个字符;
- * 代表文件名中的一部分,不包括文件分隔符;
- ** 代表文件名中的一部分,包括文件分隔符;
- ! 放在文件名前面表示将某文件排除在外;
4. 参考
- ProGuard 官网;
- DexGuard 官网;
- Android安全攻防战,反编译与混淆技术完全解析(上);
- Android安全攻防战,反编译与混淆技术完全解析(下);
- Android Proguard(混淆);
- Android studio 混淆配置;