java proguard混淆示例和结果

Posted 不会写代码的丝丽

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java proguard混淆示例和结果相关的知识,希望对你有一定的参考价值。

前言

混淆基础知识
混淆基础语法

官方指南

看了很多混淆的文章但是对于混淆的前后结果语法对比很模糊,因此写下这篇文章。

-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 "" + "----";
    }
}

-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代码被删除

以上是关于java proguard混淆示例和结果的主要内容,如果未能解决你的问题,请参考以下文章

如何准确解码 ProGuard 的混淆代码?

Android/Java 混淆:R8 与(ProGuard 或 DexGuard)?

Java代码混淆器ProGuard

使用混淆ProGuard压缩代码和资源/减少方法数量

ProGuard 代码混淆

JAVA之代码混淆proguard