gpt4 book ai didi

java - 为什么proguard不混淆方法主体?

转载 作者:塔克拉玛干 更新时间:2023-11-03 04:09:48 27 4
gpt4 key购买 nike

我正在使用ProGuard混淆我的.jar程序。一切正常,除了ProGuard不会混淆方法主体中的局部变量。这是一个例子:

原始:

混淆:

以黄色突出显示的变量名称应该被混淆,但不是。我如何也对它们进行混淆(将它们重命名为a,b,c等?)

这是我的ProGuard配置:http://pastebin.com/sb3DMRcC(以上方法不是来自排除的类之一)。

最佳答案

Why proguard does not obfuscate method body?



因为它不能。
方法参数和局部变量的名称在编译时根本不会存储。
您看到的名称是由反编译器生成的。

对于已编译的代码,有两种方法可以在本地(即在方法内)存储数据:
  • 在操作数堆栈上
  • 在局部变量


  • 操作数堆栈实际上只是一个堆栈。
    有关堆栈运算符,请参见Java VM规范中的 Table 7.2
    您可以弹出值( pop),复制最上面的值( dup),交换最上面的两个值( swap),并且行为略有不同(行为 pop2dup_x1dup_x2dup2dup2_x1dup2_x2)。
    而且,如果不是所有产生返回值的指令,大多数都会将其返回到堆栈中。

    对于此问题,重要的是如何引用堆栈中的内容,就像其他任何堆栈一样:
    相对于最高位置,并基于所使用的说明。
    没有分配的编号或名称,仅是当前存在的编号或名称。

    现在,对于所谓的“局部变量”:

    将它们更多地看作是 ArrayList,而不是Java中的变量。
    因为这正是您访问它们的方式:按索引。
    对于变量0到3,有特殊的指令(即单字节),因为它们经常使用,所有其他变量只能通过两字节指令访问,其中第二个字节是索引。
    再次参见 Table 7.2,“加载”和“存储”。
    两个表中的前五个条目是每种数据类型的宽(两字节)存储/加载指令(请注意,对于单个值, booleancharbyteshort都转换为 int,仅保留 intfloatObject是单槽值, longdouble是双槽值),接下来的20条指令是用于直接访问寄存器0到3的指令,最后八条指令是用于访问数组索引的(请注意,内部数组 booleanbytecharshort都是 而不是转换为 int,以不浪费空间,这就是为什么还要多三个指令(而不是四个指令,因为 bytechar具有相同的大小))。

    最大堆栈大小和局部变量数量都受到限制,并且必须在每种方法的 Code属性的 header 中给出,如 Section 4.7.3( max_stackmax_locals)中所定义。

    但是,有关局部变量的有趣之处在于它们兼用作方法参数,这意味着局部变量的数量永远不能少于方法参数的数量。
    请注意,在为Java VM计数值时,类型 longdouble的变量被视为两个值,因此需要两个“槽”。
    还要注意,对于非静态方法,参数0将是 this,它本身需要另一个“槽”。

    话虽如此,让我们看一些代码!

    例子:
    class Test
    {
    public static void main(String[] myArgs) throws NumberFormatException
    {
    String myString = "42";
    int myInt = Integer.parseInt(myString);
    double myDouble = (double)myInt * 42.0d;
    System.out.println(myDouble);
    }
    }

    在这里,我们有三个局部变量 myStringmyIntmyDouble,还有一个参数 myArgs
    另外,我们有两个常量 "42"42.0d,还有很多外部引用:
  • java.lang.String[]-类
  • java.lang.NumberFormatException-类
  • java.lang.String-类
  • java.lang.Integer.parseInt-方法
  • java.lang.System.out-字段
  • java.io.PrintStream.println-方法

  • 以及一些导出: Testmain,以及编译器将为我们生成的默认构造函数。

    所有常量,引用和导出都将导出到 Constant Pool-本地变量和参数名称将不会导出。

    编译和反汇编该类(使用 javap -c Test)将产生:
    Compiled from "Test.java"
    class Test {
    Test();
    Code:
    0: aload_0
    1: invokespecial #1 // Method java/lang/Object."<init>":()V
    4: return

    public static void main(java.lang.String[]) throws java.lang.NumberFormatException;
    Code:
    0: ldc #2 // String 42
    2: astore_1
    3: aload_1
    4: invokestatic #3 // Method java/lang/Integer.parseInt:(Ljava/lang/String;)I
    7: istore_2
    8: iload_2
    9: i2d
    10: ldc2_w #4 // double 42.0d
    13: dmul
    14: dstore_3
    15: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
    18: dload_3
    19: invokevirtual #7 // Method java/io/PrintStream.println:(D)V
    22: return
    }

    除了默认的构造函数,我们可以逐步看到 main方法。
    注意如何使用 myStringastore_1访问 aload_1,如何使用 myIntistore_2访问 iload_2以及使用 myDoubledstore_3访问 dload_3myArgs在任何地方都无法访问,因此也没有字节码可以处理它,但是在方法开始时,对String数组的引用将在局部变量1中,该引用很快就会被对 "42"的引用所覆盖。

    如果您向其传递 javap标志,则 -v还将向您显示常量池,但是它实际上并没有为输出添加任何值,因为常量池中的所有相关信息始终会显示在注释中。

    但是现在,让我们看一下反编译器产生的结果!

    JD-GUI 0.3.5(JD-Core 0.6.2):
    import java.io.PrintStream;

    class Test
    {
    public static void main(String[] paramArrayOfString)
    throws NumberFormatException
    {
    String str = "42";
    int i = Integer.parseInt(str);
    double d = i * 42.0D;
    System.out.println(d);
    }
    }

    Procyon 0.5.28:
    class Test
    {
    public static void main(final String[] array) throws NumberFormatException {
    System.out.println(Integer.parseInt("42") * 42.0);
    }
    }

    请注意,导出到常量池的所有内容如何保持不变,而JD-GUI只是为局部变量选择了一些名称,而Procyon则对其进行了完全优化。
    但是,自变量的名称 paramArrayOfString vs array(与原始 myArgs相比)是一个完美的例子,它表明不再有“正确的”名称,并且反编译器只需要依靠某种选择名称的方式即可。

    我不知道反编译代码中的“真实”名称是从哪里来的,但是我很确定它们不包含在jar文件中。
    您的IDE的功能可能是?

    关于java - 为什么proguard不混淆方法主体?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31508230/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com