最近公司项目的库须要发布给第三方使用,代码安全的问题就暴露出来,原来都是交由内部的其余安卓团队处理,可是处理方式很是暴力就是直接不混淆咱们的库工程,这样形成代码很容易就被反编译了。我只好硬上研究了一波。android
本文记录如何进行安卓Libray工程混淆经验。安卓混淆上的确定是大名鼎鼎的 ProGuard, 那咱们开始吧。安全
网上关于混淆的学习记录文章已经不少了,这边我整理出了一些基本的配置选项app
# 指定代码的压缩级别,值在0-7之间。通常设置5足矣
-optimizationpasses 5
# 打印混淆信息
-verbose
# 代码优化选项,不加该行会将没有用到的类删除,发布的是代码库这个选项须要
# 在作混淆以前最开始会默认对代码进行压缩,为了增长反编译的难度能够选择不压缩
-dontshrink
# 保留参数的名称和方法,该选项能够保留调试级别的属性。
-keepparameternames
# 过滤泛型,出现类型转换错误时再启用这个。目前的项目暂时无泛型类型,我先注释了
#-keepattributes Signature
# 保护代码中的Annotation不被混淆
-keepattributes *Annotation*
# 指定不去忽略非公共的库的类的成员
-dontskipnonpubliclibraryclassmembers
# 指定不去忽略非公共的库类(不跳过library中的非public的类)
-dontskipnonpubliclibraryclasses
复制代码
若是有不合理或更好的选项记得告诉我哟😄很是感谢ide
关键字 | 描述 |
---|---|
keep | 保留类和类中的成员,防止被混淆或移除 |
keepnames | 保留类和类中的成员,防止被混淆,成员没有被引用会被移除 |
keepclassmembers | 只保留类中的成员,防止被混淆或移除 |
keepclassmembernames | 只保留类中的成员,防止被混淆,成员没有引用会被移除 |
keepclasseswithmembers | 保留类和类中的成员,防止被混淆或移除,保留指明的成员 |
keepclasseswithmembernames | 保留类和类中的成员,防止被混淆,保留指明的成员,成员没有引用会被移除 |
这边值得注意的是第一种关键字 keep
函数
若是你只想保留类的名字咱们能够直接配置以下:工具
# 以下配置只保留了类的名字MyClass,类的全部成员依然会被混淆
-keep class com.your.class.name.MyClass
# 只有写明具体类成员的匹配规则才能让身体里面的混淆规则生效。
-keep class com.your.class.name.MyClass {*;}
# 举个🌰(例子)以下配置是我用来处理库工程类,指望全部public方法都开放出来,不但愿被混淆的配置
-keep class com.your.class.name.MyClass {
public <fields>;
public <methods>;
public static final <fields>;
}
复制代码
通配符 | 描述 |
---|---|
<field> |
匹配类中的全部字段 |
<method> |
匹配类中全部的方法 |
<init> |
匹配类中全部的构造函数 |
* | 匹配任意长度字符,不包含包名分隔符(.) |
** | 匹配任意长度字符,包含包名分隔符(.) |
*** | 匹配任意参数类型 |
... | 其余 |
默认状况下就算写了@Keep仍是会被混淆的,由于默认状况下 Android Studio
并无开启该选项 在混淆的配置文件 proguard-rules.pro
中加入如下配置:学习
-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>(...);
}
复制代码
⚠️注意: 目前从网上的一些资料显示若是将 @Keep
添加到类上逻辑上是只不混淆类的名称,可是从实际使用结果上看,若是@Keep
添加到类名上,则整个类没法被混淆。测试
JNI层相关的方法理论上就算没有调用到,也是不能够混淆后直接被移除的,不然 NDK 中采用注册模式的JNI
方法的时候会出现注册失败的问题。优化
-keepclasseswithmembers class * {
native <methods>;
}
复制代码
结合Keep关键字说明,经过以上配置能够保留类和类中被指名的native
方法成员函数。ui
二次封装的库工程难免有些UI相关的工具类,这部分若是被混淆了,引入方就变成没法正常使用。咱们须要对这部分也进行不混淆的控制。
-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
-keepclassmembers class * extends android.app.Activity{
public void *(android.view.View);
}
-keep public class * extends android.view.View {
*** get*();
void set*(***);
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
# 不混淆资源部分的配置
-keep class **.R$* {*;}
复制代码
默认在Android Studio的配置下是没有启用的咱们须要手动开启。
android {
...
buildTypes {
release {
minifyEnabled true // open ProGuard
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
复制代码
可是这个只有在每次Release打包以后才生效,咱们有时候须要在Debug的时候就进行混淆测试,这时咱们能够添加一个新的buildType
android {
...
buildTypes {
release {
minifyEnabled true // open ProGuard
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debugMini {
initWith debug
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
matchingFallbacks = ['debug']
}
}
}
复制代码
这样咱们就能够在默认Run的时候选择buildVariants
指定咱们刚刚新加入的debugMini
。
这篇短短的文章应该能够帮助很多小伙伴跳出深坑,可是更多使用仍是须要在工做中去积累。一些小小的体会不求一条混淆命令吃了所有的混淆配置,咱们须要精细的配置,通用的配置总会有些不如意地方,那咱们就针对单个Class进行一对一的配置,我相信能够获得更好的混淆结果。
本次的分享大概就这些内容了欢迎你们留言一块儿讨论学习与进步。
谢谢你们。