java proguard混淆示例和结果

本文详细解析了Proguard混淆工具的-keep和-keepclassmembers等关键规则,通过实例展示了如何保护类、成员变量和方法不被混淆和优化,包括公共类、私有方法、类名以及消除警告等不同场景的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

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

官方指南


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

-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
}

你在启用混淆编译时便会看到本小结一开始的错误。
解决版本:

  1. 关闭警告信息(及其危险):在你的混淆文件添加-dontwarn org.jsoup.*

在这里插入图片描述

  1. 给你的依赖添加jsoup工件:
dependencies {
 
  implementation 'org.example:library:3.0-SNAPSHOT'
  implementation group: 'org.jsoup', name: 'jsoup', version: '1.14.3'

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值