Java 为什么proguard不混淆方法体?

Java 为什么proguard不混淆方法体?,java,variables,jvm,proguard,obfuscation,Java,Variables,Jvm,Proguard,Obfuscation,我正在使用ProGuard混淆我的.jar程序。除了ProGuard不会混淆方法体中的局部变量之外,一切都正常工作。以下是一个例子: 未加工: 模糊化: 以黄色突出显示的变量名称应该是模糊的,但它们不是。我怎样才能混淆它们(使它们重命名为a、b、c等?) 这是我的ProGuard配置:(上面的方法不是来自一个排除的类) 为什么proguard不混淆方法体 因为它不能 编译时不会存储方法参数和局部变量的名称。 您看到的名称是由反编译器生成的 对于编译后的代码,有两种方法在本地(即在方法内)存储

我正在使用ProGuard混淆我的.jar程序。除了ProGuard不会混淆方法体中的局部变量之外,一切都正常工作。以下是一个例子:

未加工:

模糊化:

以黄色突出显示的变量名称应该是模糊的,但它们不是。我怎样才能混淆它们(使它们重命名为a、b、c等?)

这是我的ProGuard配置:(上面的方法不是来自一个排除的类)

为什么proguard不混淆方法体

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

对于编译后的代码,有两种方法在本地(即在方法内)存储数据:

  • 在操作数堆栈上
  • 在局部变量中
操作数堆栈实际上只是一个堆栈。
请参阅Java VM规范中的堆栈运算符。
您可以弹出值(
pop
),复制最上面的值(
dup
),交换最上面的两个值(
swap
),以及稍微改变行为的值(
pop2
dup\ux1
dup\ux2
dup2\ux1
dup2\ux2
)。
大多数,如果不是所有产生返回值的指令,都会将该值放到堆栈上

对于这个问题,重要的是如何引用堆栈上的内容,这与任何其他堆栈一样:
相对于顶部位置,并基于使用的指令。
没有指定的数字或名称,只是当前存在的任何内容

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

与Java中的变量相比,它们更像是一个
ArrayList

因为这正是您访问它们的方式:通过索引。
对于变量0到3,有特殊指令(即单字节指令),因为它们经常使用,所有其他变量只能通过双字节指令访问,其中第二个字节是索引。
请再次参阅“加载”和“存储”。
两个表中的前五个条目是每种数据类型的宽(双字节)存储/加载指令(注意,对于单值,
布尔值
字符
字节
都转换为
int
,只剩下
int
浮点值
对象
作为单槽值,而
作为双槽值),接下来的二十条指令是直接访问寄存器0到3的指令,最后八条指令是访问数组索引的指令(请注意,在数组内部,
boolean
byte
char
short
转换为
int
,以避免浪费空间,这就是为什么还有三条指令(不是四条,因为
byte
char
的大小相同))

最大堆栈大小和局部变量的数量都是有限的,必须在每个方法的
code
属性的标题中给出,如(
max\u stack
max\u locals
中所定义)

然而,关于局部变量的有趣之处在于,它们兼作方法参数,这意味着局部变量的数量永远不能低于方法参数的数量。
请注意,在计算Java VM的值时,
long
double
类型的变量被视为两个值,并需要相应的两个“插槽”。
还请注意,对于非静态方法,参数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);
    }
}
这里我们有三个局部变量
myString
myInt
myDouble
,加上一个参数
myArgs

此外,我们还有两个常量
“42”
42.0d
,以及许多外部引用:

  • java.lang.String[]
    -class
  • java.lang.NumberFormatException
    -class
  • java.lang.String
    -class
  • java.lang.Integer.parseInt
    -方法
  • java.lang.System.out
    -字段
  • java.io.PrintStream.println
    -方法
还有一些导出:
Test
main
,以及编译器将为我们生成的默认构造函数

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

编译和反汇编类(使用
javap-c Test
)会产生:

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
(vs原始的
myArgs
)-是一个完美的例子,它表明不再有“正确”的名称,反编译器只需依靠某种模式来选择名称

我不知道反编译代码中的“真实”名称来自何处,但我相当确定它们不包含在jar文件中。

IDE的功能可能?

是“模糊化”代码还是真正的Java代码或已反编译的Java代码?我的理解是字节码文件根本不记录方法参数和局部变量的名称。(如果这是ProGuard发出的源代码,请尝试编译它。然后反编译.class文件…或使用javap查看它。)@StephenC这是反编译的Java代码。我对.jar文件进行了模糊处理(在编译.jar之后使用ProGuard),然后反编译了较早的模糊处理的.jar.jar文件(字节码)
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);
  }
}
class Test
{
    public static void main(final String[] array) throws NumberFormatException {
        System.out.println(Integer.parseInt("42") * 42.0);
    }
}