前言
看了很多混淆的文章但是对于混淆的前后结果语法对比很模糊,因此写下这篇文章。
-keep
保持类或者成员不被混淆(obfuscate)或者移除(shrink)
案例1
我们工程下就两个类
//MySubLib.java
public class MySubLib {
public void test() {
}
public String name = "hehlo";
public int age =3;
}
//MySubLib2.java
public class MySubLib2 {
public String hello = "";
private int age ;
public String msgPrint() {
return hello + "----";
}
public static void main(String[] args) {
}
}
执行以下混淆规则
#MySubLib2这个类不会被混淆移除但是其成员会
-keep public class com.example.mymodule.MySubLib2 {
}
结果
除了MySubLib2
其他类都被移除,并且MySubLib2
属性都不存在了
案例2
执行以下混淆规则
#MySubLib2这个类不会被混淆移除但是其成员会
-keep public class com.example.mymodule.MySubLib2 {
}
原始类
public class MySubLib2 {
public String hello = "";
private String hello2 = "";
private int age ;
public String msgPrint() {
return hello + "----";
}
public static void main(String[] args) {
}
}
结果
public class MySubLib2 {
public String hello = "";
public MySubLib2() {
}
}
案例3
执行以下混淆规则
#MySubLib2这个类不会被混
-keep public class com.example.mymodule.MySubLib2 {
#所有public属性和方法都不混被混淆优化裁剪
public <fields>;
public <methods>;
}
原始类
public class MySubLib2 {
public String hello = "";
private String hello2 = "";
private int age;
public String msgPrint() {
return hello + "----";
}
private String msgPrint2() {
return hello + "----";
}
public static void main(String[] args) {
}
}
结果
public class MySubLib2 {
public String hello = "";
public MySubLib2() {
}
public static void main(String[] var0) {
}
public String msgPrint() {
return this.hello + "----";
}
}
案例4
混淆前
执行混淆:
#com.example.mymodule这个包下的类不会被混淆,但是属性会,并且子包也会被混淆裁剪
-keep public class com.example.mymodule.* {
}
结果
当前语法会导致子包类会被移除
同样环境我们执行一下混淆语法
#com.example.mymodule这个包和子包的类不会被混淆,但是属性方法会被优化移除,
-keep public class com.example.mymodule.** {
}
结果
同样环境我们执行一下混淆语法
#com.example.mymodule这个包和子包的类不会被混淆,并且属性方法都不会被优化和移除,
-keep public class com.example.mymodule.** {
*;
}
结果
案例5
/**
* 途混淆测试专用
*/
public class MySubLib2 {
public String hello = "";
private String hello2 = "";
private int age;
public String msgPrint() {
return hello + "----";
}
private String msgPrint2() {
return hello + "----";
}
private String msgPrint2(int i) {
return hello + "----";
}
public static void main(String[] args) {
}
}
执行混淆:
#MySubLib2类不会被混淆
-keep public class com.example.mymodule.MySubLib2 {
#所有返回String私有函数 都不会被混淆,不考虑参数
private java.lang.String *(...);
}
结果:
public class MySubLib2 {
public MySubLib2() {
}
private String msgPrint2() {
return "" + "----";
}
private String msgPrint2(int var1) {
return "" + "----";
}
}
同样的环境执行下面的混淆
#MySubLib2类不会被混淆
-keep public class com.example.mymodule.MySubLib2 {
#所有返回String私有函数并且第一个参数为int不会被混淆
private java.lang.String *(int);
}
结果:
public class MySubLib2 {
public MySubLib2() {
}
private String msgPrint2(int var1) {
return "" + "----";
}
}
案例6
我们对比下这两个规则
#会保护所有public类和内部类。但是默认保护的class不会被保护
-keep public class com.example.myapplication.fmy.**{
*;
}
;保护所有public类和非bulic类以及内部类
-keep class com.example.myapplication.fmy.**{
*;
}
我们首先使用public版本
//MyDefaultClass .java
package com.example.myapplication.fmy;
class MyDefaultClass {
String name="MyDefaultClass";
public void init() {
System.out.printf(name);
MyDefaultClass myDefaultClass = new MyDefaultClass();
MyPublicClass myPublicClass = new MyPublicClass();
}
}
//MyPublicClass.java
package com.example.myapplication.fmy;
public class MyPublicClass {
String name="MyPublicClass";
public void init(){
System.out.printf(name);
MyDefaultClass myDefaultClass = new MyDefaultClass();
myDefaultClass.init();
}
}
反编译之后
我们的MyDefaultClass 被改名为b1.a类了
-keepclassmembernames
简短名称 -keepclassmembers
当前语法会让其声明的类成员或者方法名不会被混淆(类名依然会被修改),但是前提是这个属性或者方法在shrink阶段存货下来
环境如下:
//MySubLib.java
public class MySubLib {
MySubLib2 mySubLib2 = new MySubLib2();
public void test() {
//故意引用其内部的一个函数
System.out.println(mySubLib2.msgPrint2());
}
public String name = "hehlo";
public int age = 3;
}
//MySubLib2.java
public class MySubLib2 {
public String hello = "xx";
private String hello2 = "111";
private int age;
public String msgPrint() {
return hello + "----";
}
public String msgPrint2() {
return "zz"+ hello + "----";
}
private String msgPrint2(int i) {
return hello + "----";
}
public static void main(String[] args) {
}
public void test(java.lang.String d){
}
}
执行如下混淆:
#利用MySubLib保证MySubLib2不会被shrink阶段移走
#MySubLib内部会引用MySubLib2
-keep public class com.example.mymodule.MySubLib{
*;
}
-keepclassmembers public class com.example.mymodule.MySubLib2 {
#这个函数名如果在shrink阶段被保留下来 那么应该保证其函数名不变
public java.lang.String msgPrint2();
public java.lang.String msgPrint();
}
结果:
你可以看到MySubLib2
类名被改为a
,但是msgPrint2
函数依然没被混淆,另外注意msgPrint
函数被移除了。
同样的环境执行如下混淆:
#利用MySubLib保证MySubLib2不会被shrink阶段移走
#MySubLib内部会引用MySubLib2
-keep public class com.example.mymodule.MySubLib{
*;
}
-keepclassmembers public class com.example.mymodule.MySubLib2 {
#成员函数运行随意混淆
}
结果:
public class MySubLib {
public a mySubLib2;
public String name;
public int age;
public MySubLib() {
MySubLib var10000 = this;
MySubLib var10001 = this;
MySubLib var10002 = this;
super();
a var1;
var1 = new a.<init>();
var10002.mySubLib2 = var1;
var10001.name = "hehlo";
var10000.age = 3;
}
public void test() {
PrintStream var10000 = System.out;
a var1;
(var1 = this.mySubLib2).getClass();
var10000.println("zz" + var1.a + "----");
}
}
//a.java
public class a {
public String a = "xx";
public a() {
}
}
你可以看到函数体都被优化没了,变成了内联
-keepclasseswithmembernames
简短名称 -keepclasseswithmembers
当前语法会让其声明的类成员或者方法名不会被混淆并且类名也不会被混淆,但是前提是这个类和属性或者方法在shrink阶段存活下来
public class MySubLib {
MySubLib2 mySubLib2 = new MySubLib2();
public void test() {
//故意引用其内部的一个函数
System.out.println(mySubLib2.msgPrint2());
}
public String name = "hehlo";
public int age = 3;
}
public class MySubLib2 {
public String hello = "xx";
private String hello2 = "111";
private int age;
public String msgPrint() {
return hello + "----";
}
public String msgPrint2() {
return "zz"+ hello + "----";
}
private String msgPrint2(int i) {
return hello + "----";
}
public static void main(String[] args) {
}
public void test(java.lang.String d){
}
}
结果:
//MySubLib2.java
public class MySubLib2 {
public String hello = "xx";
public String hello2 = "111";
public MySubLib2() {
}
public String msgPrint2() {
return "zz" + this.hello + "----";
}
}
//MySubLib.java
public class MySubLib {
public MySubLib2 mySubLib2;
public String name;
public int age;
public MySubLib() {
MySubLib var10000 = this;
MySubLib var10001 = this;
MySubLib var10002 = this;
super();
MySubLib2 var1;
var1 = new MySubLib2.<init>();
var10002.mySubLib2 = var1;
var10001.name = "hehlo";
var10000.age = 3;
}
public void test() {
System.out.println(this.mySubLib2.msgPrint2());
}
}
你可以看到MySubLib2
这个类名和其成员函数都没有被混淆
-assumenosideeffects
高危险操作,慎用.用于移除指定类的函数的调用
原环境
public class MySubLib {
MySubLib2 mySubLib2 = new MySubLib2();
@SuppressLint("LogUsage")
public void test() {
System.out.printf("----");
Log.e("hello","hello");
System.out.printf("++");
}
public String name = "hehlo";
public int age = 3;
}
执行以下混淆
#利用MySubLib不会被shrink阶段移走
-keep public class com.example.mymodule.MySubLib{
*;
}
# assume no side effects
#删除Log.e等方法的调用
-assumenosideeffects class android.util.Log {
public static boolean isLoggable(java.lang.String, int);
public static int v(...);
public static int i(...);
public static int w(...);
public static int d(...);
public static int e(...);
}
结果
public class MySubLib {
public a mySubLib2;
public String name;
public int age;
public MySubLib() {
MySubLib var10000 = this;
MySubLib var10001 = this;
MySubLib var10002 = this;
super();
a var1;
var1 = new a.<init>();
var10002.mySubLib2 = var1;
var10001.name = "hehlo";
var10000.age = 3;
}
@SuppressLint({"LogUsage"})
public void test() {
System.out.printf("----");
System.out.printf("++");
}
}
你可以可以清晰的看到test
中间的Log.e
代码被删除
-dontwarn
用于关闭引用的类未找到的错误.
类型的错误信息如下:
AGPBI: {"kind":"warning","text":"Missing classes detected while running R8. Please add the missing classes or apply additional keep rules that are generated in /Users/fmy/AndroidStudioProjects/ProguardDemo/app/build/outputs/mapping/debug/missing_rules.txt.\n","sources":[{}]}
AGPBI: {"kind":"warning","text":"Missing class org.jsoup.Jsoup (referenced from: void fmy.test.MyProguardTest.test(boolean))","sources":[{}],"tool":"R8"}
上面的大致的意思在你的代码中使用了org.jsoup.Jsoup
这个类,但是在类路径却找不到这个类。
我们看看这个到底什么意思呢?
我们首先新建一个maven
工件,坐标信息org.example:artifactId:library:3.0-SNAPSHOT
。
这个工件里面仅有一个类如下:
//MyProguardTest.java
public class MyProguardTest {
public static void test(boolean show) {
if (show) {
System.out.println("" + Jsoup.class.getSimpleName());
} else {
System.out.println("Hello world!");
}
}
}
我们不会吧jsoup依赖放入jar所以使用如下声明
dependencies {
compileOnly group: 'org.jsoup', name: 'jsoup', version: '1.14.3'
}
我们另一个使用工程去引用这个工件:
dependencies {
// 上面写的组件
implementation 'org.example:library:3.0-SNAPSHOT'
//...其他依赖略,但注意我们没有依赖jsoup
}
你在启用混淆编译时便会看到本小结一开始的错误。
解决版本:
- 关闭警告信息(及其危险):在你的混淆文件添加
-dontwarn org.jsoup.*
- 给你的依赖添加jsoup工件:
dependencies {
implementation 'org.example:library:3.0-SNAPSHOT'
implementation group: 'org.jsoup', name: 'jsoup', version: '1.14.3'
}