AAR包混淆方案
- 将所有需要对外暴露的方法所在类统一移动到一个文件夹中。
- 对于暴露出来的包名,类名及方法名,需手动修改,例如:per.amon.lib.api
- 在 proguard-rules.pro 文件中,添加如下代码
-keep class per.amon.module.lib.** { *; }
- 开启代码混淆
开启代码混淆方式
buildTypes {
release {
// 1、是否进行混淆
minifyEnabled true
// 2、开启zipAlign可以让安装包中的资源按4字节对齐,这样可以减少应用在运行时的内存消耗
zipAlignEnabled true
// 3、移除无用的resource文件:当ProGuard 把部分无用代码移除的时候,
// 这些代码所引用的资源也会被标记为无用资源,然后系统通过资源压缩功能将它们移除。
// 需要注意的是目前资源压缩器目前不会移除values/文件夹中定义的资源(例如字符串、尺寸、样式和颜色)
// 目前项目中用不到,设置成false 或不添加下方属性,仅做了解
// shrinkResources false
// 4、混淆文件的位置,其中 proguard-android.txt 为sdk默认的混淆配置,
// 它的位置位于android-sdk/tools/proguard/proguard-android.txt,
// 此外,proguard-android-optimize.txt 也为sdk默认的混淆配置,
// 但是它默认打开了优化开关。并且,我们可在配置混淆文件将android.util.Log置为无效代码,
// 以去除apk中打印日志的代码。而 proguard-rules.pro 是该模块下的混淆配置。
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
代码反编译查看混淆结果
选择release版本
Android studio 左侧工具栏 -》Build Variants -》 主模块的 Active Build Variant 选择 release
构建APK或AAR
Android studio 顶部工具栏 -》Build -》 Build Bundle(s)/APK(s) -> Build APK
或
Android studio 右侧工具栏 -》Gradle -》双击运行 项目名/模块名/Tasks/build/assemble
混淆结果映射表路径
在app/build/outputs/mapping/release/目录下
mapping.txt提供混淆前后类、方法、类成员等的对照表
seeds.txt列出没有被混淆的类和成员
usage.txt列出被移除的代码
混淆结果查看方式
- 结果在 app/build/outputs/apk/release/app-release.apk 或 app/build/outputs/aar/module-release.aar
- 在文件管理器中打开,遇到aar或apk等,直接把后缀改成zip解压,之后把class.dex或class.jar用android studio打开
- 混淆结果
暂时无法在飞书文档外展示此内容 - …
代码混淆相关知识
代码混淆是包含了代码压缩、优化、混淆等一系列行为的过程。如上图所示,混淆过程会有如下几个功能:
- 压缩。移除无效的类、类成员、方法、属性等;默认开启,用以减小应用体积,移除未被使用的类和成员,并且会在优化动作执行之后再次执行(因为优化后可能会再次暴露一些未被使用的类和成员)
-dontshrink 关闭压缩 - 优化。分析和优化方法的二进制代码;根据proguard-android-optimize.txt中的描述,优化可能会造成一些潜在风险,不能保证在所有版本的Dalvik上都正常运行。
-dontoptimize 关闭优化
-optimizationpasses n 表示proguard对代码进行迭代优化的次数,Android一般为5 - 混淆。把类名、属性名、方法名替换为简短且无意义的名称;
-dontobfuscate 关闭混淆 - 预校验。添加预校验信息。这个预校验是作用在Java平台上的,Android平台上不需要这项功能,去掉之后还可以加快混淆速度。
proguardFiles getDefaultProguardFile(‘proguard-android.txt’), ‘proguard-rules.pro’
这行代码定义了混淆规则由两部分构成:位于 SDK 的 tools/proguard/ 文件夹中的 proguard-android.txt 的内容以及默认放置于模块根目录的 proguard-rules.pro 的内容。前者是 SDK 提供的默认混淆文件(内容见附录1),后者是开发者自定义混淆规则的地方。
混淆配置文件区别
- proguard-android-optimize.txt:这是一个用于混淆和优化Android应用程序的配置文件。它包含了一系列的规则和配置选项,旨在通过删除未使用的代码、优化代码结构和减小输出文件的大小来提高应用程序的性能。
- proguard-android.txt:这是一个用于混淆Android应用程序的配置文件。它包含了一系列的规则和配置选项,用于对应用程序的代码进行混淆,以防止反编译和代码分析。混淆会对类名、方法名和字段名进行重命名,使得代码变得难以理解和逆向工程。
- proguard-project.txt:这是一个通用的ProGuard配置文件,可以用于任何类型的Java项目,包括Android项目。它提供了一些基本的混淆规则和配置选项,但可能需要根据具体项目的需求进行自定义。
常见混淆命令
暂时无法在飞书文档外展示此内容
保持元素不参与混淆的规则
形如:
[保持命令] [类] {[成员]
}
“类”代表类相关的限定条件,它将最终定位到某些符合该限定条件的类。它的内容可以使用:- 具体的类- 访问修饰符(public、protected、private)- 通配符*,匹配任意长度字符,但不含包名分隔符(.)- 通配符**,匹配任意长度字符,并且包含包名分隔符(.)- extends,即可以指定类的基类- implement,匹配实现了某接口的类- $,内部类
“成员”代表类成员相关的限定条件,它将最终定位到某些符合该限定条件的类成员。它的内容可以使用:- 匹配所有构造器- 匹配所有域- 匹配所有方法- 通配符*,匹配任意长度字符,但不含包名分隔符(.)- 通配符,匹配任意长度字符,并且包含包名分隔符(.)- 通配符*,匹配任意参数类型- …,匹配任意长度的任意类型参数。比如void test(…)就能匹配任意 void test(String a) 或者是 void test(int a, String b)这些方法。- 访问修饰符(public、protected、private)
举个例子,假如需要将name.huihui.test包下所有继承Activity的public类及其构造函数都保持住,可以这样写:
-keep public class name.huihui.test.**
extends Android.app.Activity {<init>}
常用的自定义混淆规则
- 不混淆某个类
-keep public class name.huihui.example.Test { *; }
- 不混淆某个包所有的类
-keep class name.huihui.test.** { *; }
- 不混淆某个类的子类
-keep public class * extends name.huihui.example.Test { *; }
- 不混淆所有类名中包含了“model”的类及其成员
-keep public class .*model*. {*;}
- 不混淆某个接口的实现
-keep class * implements name.huihui.example.TestInterface { *; }
- 不混淆某个类的构造方法
-keepclassmembers class name.huihui.example.Test {
public <init>();
}
- 不混淆某个类的特定的方法
-keepclassmembers class name.huihui.example.Test {
public void test(java.lang.String);
}
# 保持所有类和成员的名称不被混淆
-keep class * {
public protected private *;
}
# 忽略所有警告
-dontwarn *
# 防止删除未直接使用的类和成员
-dontshrink
# 禁用优化
#-dontoptimize
# 不进行混淆
#-dontobfuscate
自定义混淆规则
在 app module 下默认生成了项目的自定义混淆规则文件 proguard-rules.pro,多方调研后,一份适用于大部分项目的混淆规则最佳实践如下:
#指定压缩级别
-optimizationp
asses 5
#不跳过非公共的库的类成员
-dontskipnonpubliclibraryclassmembers
#混淆时采用的算法
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
#把混淆类中的方法名也混淆了
-useuniqueclassmembernames
#优化时允许访问并修改有修饰符的类和类的成员
-allowaccessmodification
#将文件来源重命名为“SourceFile”字符串
-renamesourcefileattribute SourceFile
#保留行号
-keepattributes SourceFile,LineNumberTable
#保持所有实现 Serializable 接口的类成员
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();}
#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.**
注意事项
1,jni方法不可混淆,因为这个方法需要和native方法保持一致;
-keepclasseswithmembernames class * { # 保持native方法不被混淆
native ;}
2,反射用到的类不混淆(否则反射可能出现问题);
3,AndroidMainfest中的类不混淆,所以四大组件和Application的子类和Framework层下所有的类默认不会进行混淆。自定义的View默认也不会被混淆;所以像网上贴的很多排除自定义View,或四大组件被混淆的规则在Android Studio中是无需加入的;
4,与服务端交互时,使用GSON、fastjson等框架解析服务端数据时,所写的JSON对象类不混淆,否则无法将JSON解析成对应的对象;
5,使用第三方开源库或者引用其他第三方的SDK包时,如果有特别要求,也需要在混淆文件中加入对应的混淆规则;
6,有用到WebView的JS调用也需要保证写的接口方法不混淆,原因和第一条一样;
7,Parcelable的子类和Creator静态成员变量不混淆,否则会产生Android.os.BadParcelableException异常;
-keep class * implements Android.os.Parcelable { # 保持Parcelable不被混淆 public static final Android.os.Parcelable$Creator *;}
8,使用enum类型时需要注意避免以下两个方法混淆,因为enum类的特殊性,以下两个方法会被反射调用,见第二条规则。
-keepclassmembers enum * {
public static [] values();
public static valueOf(java.lang.String);
}
参考文档 https://www.jianshu.com/p/cba8ca7fc36d