我正在使用ProGuard來模糊我的.jar程序。一切工作正常,除了ProGuard不會混淆方法體中的局部變量。下面是一個例子:爲什麼proguard不會混淆方法體?
原材料:
混淆:
了在黃色突出顯示笑的變量名它被混淆了,但它們不是。我怎麼能混淆他們太(讓他們改名爲A,B,C等?)
這裏是我的ProGuard配置:http://pastebin.com/sb3DMRcC(上述方法不能從排除類之一)。
我正在使用ProGuard來模糊我的.jar程序。一切工作正常,除了ProGuard不會混淆方法體中的局部變量。下面是一個例子:爲什麼proguard不會混淆方法體?
原材料:
混淆:
了在黃色突出顯示笑的變量名它被混淆了,但它們不是。我怎麼能混淆他們太(讓他們改名爲A,B,C等?)
這裏是我的ProGuard配置:http://pastebin.com/sb3DMRcC(上述方法不能從排除類之一)。
爲什麼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
和short
都轉換爲int
,只留下int
, float
和Object
作爲單時隙值,long
和double
作爲雙時隙值),接下來的20條指令是直接訪問寄存器0到3的指令,最後8條指令訪問數組索引(注意在數組內部,boolean
,byte
,char
和short
是不是轉換成int
,不要浪費空間,這就是爲什麼還有三條指令(不是四條,因爲byte
和char
具有相同的大小))。
兩個最大堆棧大小和局部變量的數目是有限的,並且必須在每個方法的Code
屬性的報頭給出,如在Section 4.7.3(max_stack
和max_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[]
- 類java.lang.NumberFormatException
- 類java.lang.String
- 類java.lang.Integer.parseInt
- 方法java.lang.System.out
- 字段java.io.PrintStream.println
- 方法並有部分出口:Test
和main
,加上默認構造函數,編譯器會爲我們生成。
所有常量,參考和導出將被導出到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
方法,一步一步來。
注myString
是如何被訪問與astore_1
和aload_1
,myInt
與istore_2
和iload_2
,並myDouble
與dstore_3
和dload_3
。
myArgs
不能在任何地方訪問,所以也沒有處理它的字節碼,但是在該方法開始時,對String數組的引用將位於局部變量1中,並很快被對"42"
的引用覆蓋。
javap
也會向您顯示常量池,如果您將其傳遞給-v
標誌,但它不會真正爲輸出添加任何值,因爲來自常量池的所有相關信息都會顯示在註釋中。
但現在,我們來看看反編譯器產生的東西!
JD-GUI 0.3.5(JD-核心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);
}
}
南河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的功能可能?
好的答案,做得好。 –
謝謝!最好的答案! – Victor2748
「混淆」代碼真正的Java代碼或已反編譯的Java代碼?我的理解是,字節碼文件根本不記錄方法參數和局部變量的名稱。 (如果這是由ProGuard發出的源代碼,請嘗試編譯它,然後反編譯.class文件或使用javap查看它。) –
@StephenC它是反編譯的Java代碼。我對.jar文件進行了模糊處理(在編譯.jar之後使用了proguard),然後反編譯了較早的模糊化的.jar。 .jar文件(字節碼)幾乎存儲了原始源代碼的所有數據(註釋和語法格式除外)。 – Victor2748