2012-05-21 24 views
11

में स्ट्रिंग स्थिरांक बदलें मुझे एक तैनात जावा प्रोग्राम में स्ट्रिंग स्थिरांक बदलने की आवश्यकता है, यानी संकलित .class -files के अंदर मान। इसे पुनरारंभ किया जा सकता है, लेकिन आसानी से पुन: संकलित नहीं किया जा सकता है (हालांकि यह प्रश्न एक असुविधाजनक विकल्प है यदि यह प्रश्न कोई जवाब नहीं देता है)। क्या यह संभव है?संकलित कक्षा

अद्यतन: मैंने सिर्फ एक हेक्स संपादक के साथ फ़ाइल को देखा और ऐसा लगता है कि मैं आसानी से वहां स्ट्रिंग को बदल सकता हूं। क्या वह काम करेगा, यानी वह फ़ाइल के किसी प्रकार के हस्ताक्षर को अमान्य नहीं करेगा? पुरानी और नई स्ट्रिंग दोनों अल्फान्यूमेरिक हैं, और यदि आवश्यक हो तो वही लंबाई हो सकती है।

अपडेट 2: मैंने इसे ठीक किया। क्योंकि मुझे जिस विशिष्ट वर्ग को बदलने की जरूरत है वह बहुत छोटा है और प्रोजेक्ट के नए संस्करण में नहीं बदला है, इसलिए मैं इसे संकलित कर सकता हूं और वहां से नई कक्षा ले सकता हूं। अभी भी ऐसे उत्तर में रूचि है जिसमें शैक्षणिक उद्देश्यों के लिए संकलन शामिल नहीं है।

+0

एएसएम और बीसीईएल जैसी कुछ बाइटकोड मैनिपुलेशन लाइब्रेरीज़ हैं, जो आपको क्लासफ़ाइल को अपनी पसंद के अनुसार ट्विक करने की अनुमति देते हैं। बेहतर समाधान, आईएमओ, निरंतर संपत्ति के रूप में निकालने और निष्कर्षण के लिए जरूरी एक बार पुनर्मूल्यांकन की असुविधा के माध्यम से जाना है। –

+0

@ नाथन डी। रयान मैं निश्चित रूप से इसे निकालने जा रहा हूं और इसे भविष्य के लिए एक कॉन्फ़िगरेशन फ़ाइल में डाल रहा हूं, लेकिन इस विशिष्ट मामले में तैनात संस्करण को पुन: संकलित करने के लिए बहुत असुविधाजनक होगा, जो कि पुराना है। –

+0

प्रश्न में स्ट्रिंग एक संकलन-समय निरंतर है? –

उत्तर

7

आप इस वर्ग के लिए सूत्रों का कहना है है, तो मेरा दृष्टिकोण है:

  • JAR फ़ाइल
  • जाओ एकल वर्ग
  • के लिए स्रोत जाओ classpath पर जार के साथ स्रोत संकलित करें (इस तरह, आपको किसी और चीज को संकलित करने की आवश्यकता नहीं है; इससे कोई दिक्कत नहीं होती है कि जार में पहले से ही बाइनरी है)। आप इसके लिए नवीनतम जावा संस्करण का उपयोग कर सकते हैं; बस -source और -target का उपयोग कर कंपाइलर को डाउनग्रेड करें।
  • नया एक या एक चींटी कार्य एक चींटी कार्य के लिए

उदाहरण jar u उपयोग करने के साथ जार में वर्ग फ़ाइल बदलें:

 <jar destfile="${jar}" 
      compress="true" update="true" duplicate="preserve" index="true" 
      manifest="tmp/META-INF/MANIFEST.MF" 
     > 
      <fileset dir="build/classes"> 
       <filter /> 
      </fileset> 
      <zipfileset src="${origJar}"> 
       <exclude name="META-INF/*"/> 
      </zipfileset> 
     </jar> 

यहाँ मैं भी प्रकट अद्यतन करें। पहले नए वर्ग रखो और फिर मूल जेएआर से सभी फाइलें जोड़ें। duplicate="preserve" यह सुनिश्चित करेगा कि नया कोड ओवरराइट नहीं किया जाएगा।

यदि कोड हस्ताक्षरित नहीं है, तो आप बाइट्स को प्रतिस्थापित करने का प्रयास भी कर सकते हैं यदि नई स्ट्रिंग में पुरानी एक जैसी लंबाई है। जावा कोड पर कुछ चेक करता है लेकिन no checksum in the .class files है।

आपको लंबाई को संरक्षित करना होगा; अन्यथा वर्ग लोडर भ्रमित हो जाएगा।

+0

मैंने इसे स्रोत के साथ तय किया (प्रश्न देखें), लेकिन मुझे अभी भी नो-सोर्स उत्तर खोजने में दिलचस्पी है। एक हेक्स संपादक में कक्षा का निरीक्षण करते समय, मैंने देखा कि स्ट्रिंग की लंबाई इससे पहले एक या अधिक बाइट्स में संग्रहीत की जाती है। यदि मैं इसे भी अपडेट करता हूं, तो क्या मैं स्ट्रिंग की लंबाई बदल सकता हूं, या क्या आगे लम्बाई फ़ील्ड हैं जिन्हें संपादित करने की आवश्यकता है? –

+0

यदि आप स्ट्रिंग की लंबाई बदलते हैं, तो आपको स्ट्रिंग के बाद कई बाइट्स डालने/हटाने की आवश्यकता है क्योंकि क्लास रीडर स्ट्रिंग को पढ़ेगा और फिर अगले मान्य आइटम -> क्रैश –

+1

@Aaron की अपेक्षा करें, यह सही नहीं है। जब तक आप स्ट्रिंग की शुरुआत में लंबाई फ़ील्ड को अपडेट करते हैं, तब तक सब कुछ ठीक काम करेगा। ऐसा नहीं है कि क्लासलोडर में मैजिक ऑफसेट्स हार्डकोडेड हैं। – Antimony

1

आप कई बाइटकोड इंजीनियरिंग पुस्तकालयों का उपयोग कर क्लास को संशोधित कर सकते हैं। उदाहरण के लिए, javaassist का उपयोग करना।

हालांकि, यदि आप स्थिर अंतिम सदस्य को प्रतिस्थापित करने का प्रयास कर रहे हैं, तो यह आपको वांछित प्रभाव नहीं दे सकता है, क्योंकि संकलक इस स्थिरता को कहीं भी इनलाइन करते हैं।

javaassist.jar का उपयोग कर

नमूना कोड

//ConstantHolder.java

public class ConstantHolder { 

public static final String HELLO="hello"; 

public static void main(String[] args) { 
    System.out.println("Value:" + ConstantHolder.HELLO); 
} 
} 

//ModifyConstant.java

import java.io.IOException; 

import javassist.CannotCompileException; 
import javassist.ClassPool; 
import javassist.CtClass; 
import javassist.CtField; 
import javassist.NotFoundException; 

//ModifyConstant.java 
public class ModifyConstant { 
public static void main(String[] args) { 
    modifyConstant(); 
} 

private static void modifyConstant() { 
    ClassPool pool = ClassPool.getDefault(); 
    try { 
    CtClass pt = pool.get("ConstantHolder"); 
    CtField field = pt.getField("HELLO"); 
    pt.removeField(field); 
    CtField newField = CtField.make("public static final String HELLO=\"hell\";", pt); 
    pt.addField(newField); 
    pt.writeFile(); 
    } catch (NotFoundException e) { 
    e.printStackTrace();System.exit(-1); 
    } catch (CannotCompileException e) { 
    e.printStackTrace();System.exit(-1); 
    } catch (IOException e) { 
    e.printStackTrace();System.exit(-1); 
    } 
} 
} 

इस मामले में, कार्यक्रम को सफलतापूर्वक का मूल्य को संशोधित करता है नमस्ते "हैलो" से "नरक" तक। हालांकि, जब आप कॉन्स्टेंटहोल्डर क्लास चलाते हैं, तो यह अभी भी कंपाइलर द्वारा इनलाइन करने के कारण "वैल्यू: हैलो" प्रिंट करेगा।

उम्मीद है कि यह मदद करता है।

+0

हस्ताक्षर के लिए इसका क्या अर्थ है? –

+0

कृपया समझाएं। मैं आपके प्रश्न को नहीं समझा। – krishnakumarp

4

निरंतर पूल में एक स्ट्रिंग (तकनीकी रूप से एक यूटीएफ 8 आइटम) को संशोधित करते समय आवश्यक एकमात्र अतिरिक्त डेटा लंबाई क्षेत्र (डेटा से पहले 2 बाइट बड़े एंडियन) है। कोई अतिरिक्त चेकसम या ऑफसेट नहीं हैं जिन्हें संशोधन की आवश्यकता है।

  • स्ट्रिंग अन्य स्थानों में इस्तेमाल किया जा सकता है:

    दो चेतावनियां हैं। उदाहरण के लिए "कोड" का उपयोग विधि कोड विशेषता के लिए किया जाता है, इसलिए इसे बदलना फ़ाइल को तोड़ देगा।

  • स्ट्रिंग को संशोधित Utf8 प्रारूप में संग्रहीत किया जाता है। तो मूल विमान के बाहर नल बाइट्स और यूनिकोड वर्ण अलग-अलग एन्कोड किए गए हैं। लंबाई फ़ील्ड बाइट्स की संख्या है, वर्णों की संख्या नहीं है, और 65535 तक सीमित है।

यदि आप इसे करने की योजना बना रहे हैं, तो क्लास फ़ाइल संपादक टूल प्राप्त करना बेहतर है, लेकिन हेक्स संपादक उपयोगी है त्वरित परिवर्तन

+0

संकलित कक्षा को संपादित करने के लिए नमूना उपकरण है: http://sourceforge.net/projects/classeditor/ (आपको .class फ़ाइल चुनने की आवश्यकता है, यह जार नहीं खोल सकता है)। – Marcin

2

मैं हाल ही में मेरे अपने ConstantPool नक्शाकार लिखा था क्योंकि एएसएम और JarJar निम्न समस्याओं था:

  • धीमा करने के लिए
  • सभी वर्ग निर्भरता के बिना फिर से लिखने का समर्थन नहीं किया
  • स्ट्रीमिंग का समर्थन नहीं किया था
  • ट्री एपीआई मोड में रीमेपर का समर्थन नहीं किया
  • स्टैकमैप्स
का विस्तार और पतन करना था

public void process(DataInputStream in, DataOutputStream out, Function mapper) throws IOException { 
    int magic = in.readInt(); 
    if (magic != 0xcafebabe) throw new ClassFormatError("wrong magic: " + magic); 
    out.writeInt(magic); 

    copy(in, out, 4); // minor and major 

    int size = in.readUnsignedShort(); 
    out.writeShort(size); 

    for (int i = 1; i < size; i++) { 
     int tag = in.readUnsignedByte(); 
     out.writeByte(tag); 

     Constant constant = Constant.constant(tag); 
     switch (constant) { 
      case Utf8: 
       out.writeUTF(mapper.apply(in.readUTF())); 
       break; 
      case Double: 
      case Long: 
       i++; // "In retrospect, making 8-byte constants take two constant pool entries was a poor choice." 
       // See http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4.5 
      default: 
       copy(in, out, constant.size); 
       break; 
     } 
    } 
    Streams.copyAndClose(in, out); 
} 

private final byte[] buffer = new byte[8]; 

private void copy(DataInputStream in, DataOutputStream out, int amount) throws IOException { 
    in.readFully(buffer, 0, amount); 
    out.write(buffer, 0, amount); 
} 

और फिर

public enum Constant { 
    Utf8(1, -1), 
    Integer(3, 4), 
    Float(4, 4), 
    Long(5, 8), 
    Double(6,8), 
    Class(7, 2), 
    String(8, 2), 
    Field(9, 4), 
    Method(10, 4), 
    InterfaceMethod(11, 4), 
    NameAndType(12, 4), 
    MethodHandle(15, 3), 
    MethodType(16, 2), 
    InvokeDynamic(18, 4); 

public final int tag, size; 

Constant(int tag, int size) { this.tag = tag; this.size = size; } 

private static final Constant[] constants; 
static{ 
    constants = new Constant[19]; 
    for (Constant c : Constant.values()) constants[c.tag] = c; 
} 

public static Constant constant(int tag) { 
    try { 
     Constant constant = constants[tag]; 
     if(constant != null) return constant; 
    } catch (IndexOutOfBoundsException ignored) { } 
    throw new ClassFormatError("Unknown tag: " + tag); 
} 

बस सोचा था कि मैं पुस्तकालयों के बिना विकल्प दिखाने चाहते हैं के रूप में यह काफी एक अच्छी जगह से हैकिंग शुरू करने के लिए है:

मैं निम्नलिखित के साथ समाप्त हो गया। मेरा कोड javap से प्रेरित कोड