2015-07-20 34 views
6

我正在使用ProGuard來模糊我的.jar程序。一切工作正常,除了ProGuard不會混淆方法體中的局部變量。下面是一個例子:爲什麼proguard不會混淆方法體?

原材料:

enter image description here

混淆:

enter image description here

了在黃色突出顯示笑的變量名它被混淆了,但它們不是。我怎麼能混淆他們太(讓他們改名爲A,B,C等?)

這裏是我的ProGuard配置:http://pastebin.com/sb3DMRcC(上述方法不能從排除類之一)。

+0

「混淆」代碼真正的Java代碼或已反編譯的Java代碼?我的理解是,字節碼文件根本不記錄方法參數和局部變量的名稱。 (如果這是由ProGuard發出的源代碼,請嘗試編譯它,然後反編譯.class文件或使用javap查看它。) –

+0

@StephenC它是反編譯的Java代碼。我對.jar文件進行了模糊處理(在編譯.jar之後使用了proguard),然後反編譯了較早的模糊化的.jar。 .jar文件(字節碼)幾乎存儲了原始源代碼的所有數據(註釋和語法格式除外)。 – Victor2748

回答

11

爲什麼proguard不會混淆方法體?

因爲它不能。
編譯時,根本不存儲方法參數和局部變量的名稱。
您看到的名稱是由您的反編譯器生成的。

對於編譯代碼,也有在本地存儲數據的方式(中的一個方法,即):

  • 在操作數棧
  • 在局部變量

操作數堆棧真的只是一堆。
有關堆棧運算符的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的指令,最後8條指令訪問數組索引(注意在數組內部,boolean,byte,charshort不是轉換成int,不要浪費空間,這就是爲什麼還有三條指令(不是四條,因爲bytechar具有相同的大小))。

兩個最大堆棧大小和局部變量的數目是有限的,並且必須在每個方法的Code屬性的報頭給出,如在Section 4.7.3max_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方法,一步一步來。
myString是如何被訪問與astore_1aload_1myIntistore_2iload_2,並myDoubledstore_3dload_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的功能可能?

+0

好的答案,做得好。 –

+0

謝謝!最好的答案! – Victor2748