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

标签 java variables jvm proguard obfuscation

我正在使用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/

    相关文章:

    Java gson 数组的数组 : java. lang.IllegalStateException

    java - 扫描 lwjgl 中的键

    java - 如何将新的货币代码添加到 Java?

    java - 安装 java 8 后, javac -version 可在我的 mac 上运行,但不能在 java -version 上运行

    java - 如何在启动时在 JVM 中预加载 Web 应用程序类?

    java - 依次调用不同类的 get 方法

    java - 在带有动画的 Java 应用程序中使用多个计时器

    c - 我们如何将包含预定义宏的字符串存储到 C 中的变量中?

    c++在该类中声明该类的变量

    php - 如何从字符串中生成条件语句