我正在使用ProGuard来混淆我的.jar程序。一切正常,但ProGuard不会在方法体中混淆局部变量。这是一个例子:
原材料:
混淆:
以黄色突出显示的变量名称应该被混淆,但它们不是。我怎样才能对它们进行模糊处理(将它们重命名为 a,b,c等。?)
这是我的ProGuard配置:http://pastebin.com/sb3DMRcC(上述方法不是来自其中一个排除的类)。
答案 0 :(得分:12)
为什么proguard不会混淆方法体?
因为它不能 编译时根本不存储方法参数和局部变量的名称 您正在查看的名称由您的反编译器生成。
对于已编译的代码,有两种方法可以在本地存储数据(即在方法中):
操作数堆栈实际上只是一个堆栈
有关堆栈运算符,请参阅Java VM规范中的Table 7.2
您可以弹出值(pop
),复制最高值(dup
),交换前两个值(swap
)以及略微改变的行为(pop2
, dup_x1
,dup_x2
,dup2
,dup2_x1
,dup2_x2
)。
大多数情况下,如果不是所有产生返回值的指令都会将所述值丢弃到堆栈中。
这个问题的重要之处在于如何引用堆栈上的内容,这与任何其他堆栈类似:
相对于顶部位置,并根据使用的指令
没有指定的号码或名称,它只是目前的任何内容。
现在,对于所谓的"局部变量":
将它们视为ArrayList
而不是Java中的变量
因为这正是您访问它们的方式:按索引
对于变量0到3,有特殊指令(即单字节),因为它们经常使用,所有其他变量只能通过双字节指令访问,其中第二个字节是索引。
再次见Table 7.2,"加载"和"商店"。
两个表中的前五个条目是每种数据类型的宽(双字节)存储/加载指令(请注意,对于单个值,boolean
,char
,byte
和{ {1}}全部转换为short
,仅将int
,int
和float
作为单一广告位值,Object
和long
作为双时隙1),接下来的20条指令是直接访问寄存器0到3的指令,最后8条指令用于访问数组索引(注意内部数组double
,boolean
,byte
和char
不转换为short
,不浪费空间,这就是为什么还有三个指令(不是四个,因为{{1} }和int
具有相同的大小))。
最大堆栈大小和局部变量数都是有限的,必须在每个方法的byte
属性的标题中给出,如Section 4.7.3中所定义(char
和Code
)。
关于局部变量的有趣之处在于它们是方法参数的两倍,这意味着局部变量的数量永远不会低于方法参数的数量。
请注意,在计算Java VM的值时,max_stack
和max_locals
类型的变量将被视为两个值,并且需要两个"插槽"因此,
另请注意,对于非静态方法,参数0将为long
,这需要另一个" slot"为了自己。
话虽如此,让我们看看一些代码!
示例:
double
此处我们有三个局部变量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[]
- 班级java.lang.NumberFormatException
- 方法java.lang.String
- 字段java.lang.Integer.parseInt
- 方法还有一些导出:java.lang.System.out
和java.io.PrintStream.println
,加上编译器为我们生成的默认构造函数。
所有常量,引用和导出都将导出到Constant Pool - 局部变量和参数名称不会。
编译和反汇编类(使用Test
)会产生:
main
除了默认构造函数之外,我们还可以逐步查看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
,myString
与astore_1
和aload_1
以及myInt
istore_2
访问iload_2
的方式}和myDouble
。
dstore_3
无法在任何地方访问,因此也没有字节码处理它,但在方法的开头,对String数组的引用将在本地变量1中,很快就会到来被dload_3
的引用覆盖。
myArgs
标志, "42"
也会显示常量池,但它并没有真正为输出添加任何值,因为来自常量池的所有相关信息无论如何都会显示在评论中。
但现在,让我们看一下反编译器会产生什么!
JD-GUI 0.3.5(JD-Core 0.6.2):
javap
Procyon 0.5.28:
-v
请注意导出到常量池的所有内容是如何保留的,而JD-GUI只是为局部变量选择一些名称,而Procyon则完全优化它们。
参数的名称 - 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);
}
}
vs class Test
{
public static void main(final String[] array) throws NumberFormatException {
System.out.println(Integer.parseInt("42") * 42.0);
}
}
(与原paramArrayOfString
相对) - 是一个完美的例子,表明没有&#34;正确&#34;再一次命名,反编译器只需要依赖一些选择名称的模式。
我不知道&#34; true&#34;您的反编译代码中的名称来自,但我相当确定它们不包含在jar文件中。
IDE的功能可能吗?