2015-07-20 15 views
6

मैं अपने .jar प्रोग्राम को obfuscate करने के लिए ProGuard का उपयोग कर रहा हूँ। सब कुछ ठीक काम करता है, इस तथ्य को छोड़कर कि ProGuard विधि निकायों में स्थानीय चर को खराब नहीं करता है।क्यों प्रोजेगार्ड विधि शरीर को खराब नहीं करता है?

कच्चे:

enter image description here

Obfuscated: यहाँ एक उदाहरण है

enter image description here

चर के नामों का पीला थानेदार में हाइलाइट किया जाता uld obfuscated हो, लेकिन वे नहीं हैं। कैसे मैं उन्हें बहुत अंधेरा कर सकते हैं (उनमें एक, ख, ग आदि नाम दिया है?)

यहाँ मेरी ProGuard config है: http://pastebin.com/sb3DMRcC (उपरोक्त विधि बाहर रखा वर्गों में से एक से नहीं है)।

+0

क्या "obfuscated" कोड असली जावा कोड या जावा कोड है जिसे डिकंपिल्ड किया गया है? मेरी समझ यह है कि एक बाइटकोड फ़ाइल बस विधि पैरामीटर और स्थानीय चर के नाम रिकॉर्ड नहीं करता है। (यदि यह ProGuard द्वारा उत्सर्जित स्रोत कोड है, तो इसे संकलित करने का प्रयास करें। फिर .class फ़ाइल को डीकंपाइल करें ... या javap का उपयोग करके इसे देखें।) –

+0

@StephenC यह डीकंपिल्ड जावा कोड है। मैंने .jar फ़ाइल को obfuscated (proguard के साथ, .jar संकलित करने के बाद), फिर पहले obfuscated .jar decompiled। .jar फ़ाइलें (बाइटकोड) मूल स्रोत कोड से लगभग सभी डेटा स्टोर करती है (टिप्पणियों और वाक्यविन्यास स्वरूपण को छोड़कर)। – Victor2748

उत्तर

11

क्यों प्रोजेगार्ड विधि शरीर को खराब नहीं करता है?

क्योंकि यह नहीं कर सकता है।
विधि तर्क और स्थानीय चर के नाम संकलित करते समय बस संग्रहीत नहीं होते हैं।
जो नाम आप देख रहे हैं वे आपके डिकंपेलर द्वारा जेनरेट किए गए हैं।

  • संकार्य ढेर पर
  • स्थानीय चर

में संकार्य ढेर वास्तव में है:

संकलित कोड के लिए, वहाँ स्थानीय स्तर पर डाटा स्टोर करने के दो तरीके (एक विधि के भीतर यानी) कर रहे हैं बस एक ढेर
स्टैक ऑपरेटरों के लिए जावा वीएम विशिष्टता से Table 7.2 देखें।
आप मूल्यों पॉप कर सकते हैं (pop), शीर्ष मूल्य (dup) डुप्लिकेट, शीर्ष दो मान (swap) और थोड़ा बदल व्यवहार (pop2, dup_x1, dup_x2, dup2, dup2_x1, dup2_x2) के साथ एक ही स्वैप।
और सबसे अधिक, यदि रिटर्न वैल्यू उत्पन्न करने वाले सभी निर्देश स्टैक पर मूल्य को छोड़ देंगे।

इस प्रश्न के लिए महत्वपूर्ण बात यह है कि स्टैक पर चीजें किस प्रकार संदर्भित की जाती हैं, जो किसी अन्य स्टैक के साथ होती है:
शीर्ष स्थिति से संबंधित, और उपयोग किए गए निर्देशों के आधार पर।
कोई असाइन नंबर या नाम नहीं हैं, यह वही है जो वर्तमान में वहां है।

अब, तथाकथित "स्थानीय चर" के लिए: के रूप में अधिक जावा में चर से एक ArrayList उनमें से

Think।
क्योंकि यह बिल्कुल ठीक है कि आप उन्हें कैसे एक्सेस करते हैं: अनुक्रमणिका द्वारा।
0 से 3 के चर के लिए, विशेष निर्देश हैं (यानी।एकल बाइट) क्योंकि इन्हें अक्सर उपयोग किया जाता है, अन्य सभी चर केवल दो-बाइट निर्देश के माध्यम से पहुंचा जा सकता है, जहां दूसरा बाइट इंडेक्स है।
Table 7.2 फिर से, "लोड" और "स्टोर" देखें।
दोनों तालिकाओं में पहले पांच प्रविष्टियों,, चौड़ा (दो-बाइट) की दुकान/लोड प्रत्येक डेटा प्रकार के लिए निर्देश (ध्यान दें कि, एकल मूल्यों के लिए boolean, char, byte और short सभी int में बदल दिए जाते हैं केवल int छोड़ने float और Object सिंगल-स्लॉट मान और long और double डबल-स्लॉट वाले के रूप में), अगले बीस निर्देश रजिस्ट्रार 0 से 3 तक सीधे पहुंच के लिए निर्देश हैं, और अंतिम आठ निर्देश सरणी सूचकांक तक पहुंचने के लिए हैं (ध्यान दें कि सरणी के अंदर , boolean, byte, char और shortमें परिवर्तित, अंतरिक्ष को बर्बाद न करने के लिए, यही कारण है कि तीन और निर्देश हैं (चार नहीं, byte और char के समान आकार हैं))।

दोनों अधिकतम ढेर आकार और स्थानीय चर की संख्या सीमित कर रहे हैं, और के रूप में Section 4.7.3 (max_stack और max_locals) में परिभाषित, प्रत्येक विधि के Code विशेषता के शीर्षक में दिया जाना चाहिए।

स्थानीय चर के बारे में दिलचस्प बात यह है कि वे विधि तर्क के रूप में दोगुनी हैं, जिसका अर्थ है कि स्थानीय चर की संख्या विधि तर्कों की संख्या से कम कभी नहीं हो सकती है।
ध्यान दें कि जावा वीएम के मानों की गणना करते समय, 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 है। - वर्ग

  • java.lang.NumberFormatException - वर्ग
  • java.lang.String - वर्ग
  • java.lang.Integer.parseInt - विधि
    • java.lang.String[]:
      इसके अलावा, हम दो स्थिरांक "42" और 42.0d, और बाहरी संदर्भ का एक बहुत है java.lang.System.out - फ़ील्ड
    • ,210 - विधि

    और कुछ निर्यात: 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 विधि, चरण दर चरण देख सकते हैं।
    नोट कैसे myStringdstore_3 और dload_3 साथ istore_2 और iload_2, और myDouble साथ astore_1 और aload_1, myInt के साथ पहुँचा है।
    myArgs कहीं भी नहीं पहुंचाया गया है, इसलिए इसके साथ कोई बाइटकोड नहीं है, लेकिन विधि की शुरुआत में, स्ट्रिंग सरणी का संदर्भ स्थानीय चर 1 में होगा, जो "42" के संदर्भ में जल्द ही अधिलेखित हो जाता है।

    javap भी अगर आप इसे -v ध्वज पारित आप लगातार पूल दिखाई देगा, लेकिन यह वास्तव में के बाद से लगातार पूल से सभी प्रासंगिक जानकारी वैसे भी टिप्पणी में प्रदर्शित किया जाता है, उत्पादन के लिए सार्थक नहीं है।

    लेकिन अब, देखते हैं कि डीकंपलर क्या उत्पादन करते हैं!

    जद-जीयूआई 0.3.5 (जद-कोर 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); 
        } 
    } 
    

    नोट कैसे सब कुछ है कि लगातार पूल करने के लिए निर्यात किया गया था बनी रहती है, जबकि जेडी-जीयूआई स्थानीय चर के लिए कुछ नाम चुनता है, और प्रोसीन पूरी तरह से उन्हें अनुकूलित करता है।
    तर्क का नाम - paramArrayOfString बनाम array (मूल myArgs बनाम) - यह एक आदर्श उदाहरण है, हालांकि, यह दिखाने के लिए कि अब कोई "सही" नाम नहीं है, और decompilers को बस चुनने के कुछ पैटर्न पर निर्भर होना है नाम।

    मुझे नहीं पता कि आपके अपूर्ण कोड में "सत्य" नाम कहां से आ रहे हैं, लेकिन मुझे काफी यकीन है कि वे जार फ़ाइल में निहित नहीं हैं।
    शायद आपके आईडीई की सुविधा?

  • +0

    अच्छा जवाब, अच्छी तरह से किया। –

    +0

    धन्यवाद! सबसे अच्छा जवाब कभी! – Victor2748

    संबंधित मुद्दे