学单词
Pro
美 /proʊ/
n. 职业选手;益处;妓女;赞成者;赞成的意见
adj. (非正式)专业的,职业的
prep. 赞成的
adv. 赞成地
n. (Pro)(俄、美、意、印、西)普罗(人名)
Guard
美 /ɡɑːrd/
n. 卫兵,警卫;看守,守卫;列车长;保护装置;(英国的)禁卫军;(篮球)后卫;(拳击运动员的)防御姿势
v. 守卫,保卫;看守,监视;警惕,防范;保护,维护
【名】(Guard)(英)格尔德(人名)
n. (Guard)人名;(英)格尔德
1.简介
Android Studio
自身集成Java
语言的ProGuard
作为压缩,优化和混淆工具,配合Gradle
构建工具使用很简单,只需要在工程应用目录的gradle
文件中设置minifyEnabled
为true
即可。然后我们就可以到proguard-rules.pro
文件中加入我们的混淆规则了。
android {
...
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
学单词
Shrinking
美 /ˈʃrɪŋkɪŋ/
v. 缩水,收缩;变小,缩小;萎缩;退缩;回避,离开(shrink 的现在分词)
Shrink
美 /ʃrɪŋk/
v. (使)缩小,减少;(通常指布料、衣服等洗后)缩水,皱缩;退缩,畏缩;畏避,回避(工作、责任等)
n. <非正式> 精神病学家,心理学家;收缩;畏缩
Optimization
美 /,ɑptəmɪ'zeʃən/
n. 最佳化,最优化
Obfuscation
英 /ˌɒbfʌsˈkeɪʃn/
n. 困惑;模糊;昏迷
2.ProGuard作用
2.1压缩(Shrinking
):默认开启,用以减小应用体积,移除未被使用的类和成员,并且会在优化动作执行之后再次执行(因为优化后可能会再次暴露一些未被使用的类和成员)。
-dontshrink 关闭压缩
2.2优化(Optimization
):默认开启,在字节码级别执行优化,让应用运行的更快。
-dontoptimize 关闭优化
-optimizationpasses n 表示proguard对代码进行迭代优化的次数,Android一般为5
2.3混淆(Obfuscation
):默认开启,增大反编译难度,类和类成员会被随机命名,除非用keep保护。
-dontobfuscate 关闭混淆
混淆后默认会在工程目录app/build/outputs/mapping/release
下生成一个mapping.txt
文件,这就是混淆规则,我们可以根据这个文件把混淆后的代码反推回源本的代码,所以这个文件很重要,注意保护好。原则上,代码混淆后越乱越无规律越好,但有些地方我们是要避免混淆的,否则程序运行就会出错,所以就有了下面我们要教大家的,如何让自己的部分代码避免混淆从而防止出错。
2.查看混淆后的代码
2.1工具包
dex2jar 0.0.9.15
、JD-GUI
、apktool
https://pan.baidu.com/s/12ZApUf_7q0IZIK-x2C34VQ
提取码w000
2.2下载并解压
image.png
2.3使用dex2jar
反编译dex
文件
将需要反编译的APK
后缀名改为.rar
或则.zip
,解压看到目录
得到其中的classes.dex
文件(它就是java
文件编译再通过dx
工具打包而成的)
2.4将获取到的classes.dex
复制到解压出来的工具dex2jar
文件夹内,
2.5在命令行下,进入到dex2jar.bat
所在目录,输入命令:
dex2jar 0.0.9.15
输入dex2jar.bat classes.dex
dex2jar-2.0
输入:d2j-dex2jar.bat classes.dex
简单命令
//打开D盘
d:
//查看当前文件列表
dir
//打开软件目录
cd 软件
2.6运行结束后,在该目录下会生成一个classes_dex2jar.jar
的文件
2.7然后打开工具jd-gui
文件夹里的jd-gui.exe
,用该工具打开生成的classes_dex2jar.jar
文件,便可以看到源码
tip:
如果打开找不到自己写的源码,可以解压classes_dex2jar.jar
文件,可看到要找的源码,拖拽代码到jd-gui
中即可查看代码
3.混淆基本规则
3.1
-keep class com.example.myapplication.*
-keep class com.example.myapplication.**
*
一颗星表示只是保持该包下的类名,而子包下的类名还是会被混淆;
**
两颗星表示把本包和所含子包下的类名都保持;
用以上方法保持类后,你会发现类名虽然未混淆,但里面的具体方法和变量命名还是变了
eg:
混淆前
混淆后
*
一颗星
**
两颗星
3.2
如果既想保持类名,又想保持里面的内容不被混淆,我们就需要以下方法了
-keep class com.example.myapplication.* {*;}
-keep class com.example.myapplication.** {*;}
混淆后
* {*;}
一颗星
** {*;}
两颗星
3.3
在此基础上,我们也可以使用Java
的基本规则来保护特定类不被混淆,比如我们可以用extend
,implement
等这些Java
规则。如下例子就避免所有继承Activity
的类被混淆
-keep public class * extends android.app.Activity
混淆后
image.png3.4
如果我们要保留一个类中的内部类不被混淆则需要用$
符号,如下例子表示保持MainActivity
内部类User
中的所有public
内容不被混淆。
-keepclassmembers class com.example.myapplication.MainActivity$User {
public *;
}
eg
混淆前
image.png
没有对内部类处理的
image.png
对内部类处理的
image.png
3.5
再者,如果一个类中你不希望保持全部内容不被混淆,而只是希望保护类下的特定内容,就可以使用
<init>; //匹配所有构造器
<fields>; //匹配所有域
<methods>; //匹配所有方法方法
你还可以在<fields>
或<methods>
前面加上private
、public
、native
等来进一步指定不被混淆的内容,如
-keep class com.example.myapplication.entity.Person {
public <methods>;
}
表示Person
类下的所有public
方法都不会被混淆,当然你还可以加入参数,比如以下表示用java.lang.String
,int
作为入参的构造函数不会被混淆
-keep class com.example.myapplication.entity.Person {
public <init>(java.lang.String, int);
}
有时候你是不是还想着,我不需要保持类名,我只需要把该类下的特定方法保持不被混淆就好,那你就不能用keep
方法了,keep
方法会保持类名,而需要用keepclassmembers
,如此类名就不会被保持,为了便于对这些规则进行理解,官网给出了以下表格
保留 | 防止被移除或者被重命名 | 防止被重命名 |
---|---|---|
类和类成员 | -keep | -keepnames |
仅类成员 | -keepclassmembers | -keepclassmembernames |
如果拥有某成员,保留类和类成员 | -keepclasseswithmembers | -keepclasseswithmembernames |
移除是指在压缩(Shrinking
)时是否会被删除。
4.注意事项
1.jni
方法不可混淆,因为这个方法需要和native
方法保持一致;
-keepclasseswithmembernames class * { # 保持native方法不被混淆
native <methods>;
}
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);
}
4.出错后如何排查问题
4.1开启调试模式下混淆代码
debug {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
4.2查看mapping.txt
文件
它里面存放着源码到混淆之后的代码的映射信息,这个文件是在我们执行proguard之后产生的
4.3制造一个混淆代码的Crash
public class TestProGuard {
private static final String TAG = "TestProGuard";
public static void beforeProGuard(){
Log.d(TAG, "beforeProGuard");
createAnCrash();
}
private static void createAnCrash() {
throw new IllegalArgumentException("IllegalArgumentException");
}
}
public class MainActivity extends AppCompatActivity {
private Person person;
private User user;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TestProGuard.beforeProGuard();
...
运行之后Logcat
日志:
4.3如何还原
mapping
文件位置
我们来到sdk/tools/proguard/bin
目录下,打开proguardgui.bat
文件
双击打开后
image.png
到这里即可排查出问题
tip:
如果你还没有看到行号可在混淆文件中放入
-keepattributes SourceFile,LineNumberTable
image.png
完结
网友评论