2012-01-25 9 views
6

द्वारा एक और समारोह बाइट प्रति अधिलेखित की मैं एक समारोह है लगता है:सी: बाइट

int f1(int x){ 
// some more or less complicated operations on x 
return x; 
} 

और है कि मैं एक और कार्य हो

int f2(int x){ 
// we simply return x 
return x; 
} 

मैं निम्नलिखित की तरह कुछ करने के लिए सक्षम होने के लिए चाहते हैं :

char* _f1 = (char*)f1; 
char* _f2 = (char*)f2; 
int i; 
for (i=0; i<FUN_LENGTH; ++i){ 
f1[i] = f2[i]; 
} 

आईई मैं f1 और f2 को कच्चे बाइट एरे के रूप में और "f1 बाइट बाइट द्वारा ओवरराइट" की व्याख्या करना चाहता हूं और इस प्रकार इसे f2 द्वारा प्रतिस्थापित करें।

मुझे पता है कि आमतौर पर कॉल करने योग्य कोड लिखने-संरक्षित है, हालांकि, मेरी विशेष स्थिति में, आप केवल स्मृति स्थान को ओवरराइट कर सकते हैं जहां f1 स्थित है। यही है, मैं बाइट्स को f1 पर कॉपी कर सकता हूं, लेकिन बाद में, अगर मैं f1 पर कॉल करता हूं, तो पूरी चीज दुर्घटनाग्रस्त हो जाती है।

तो, सिद्धांत में मेरा दृष्टिकोण संभव है? या क्या कुछ मशीन/कार्यान्वयन/जो कुछ भी निर्भर मुद्दों पर विचार करना है?

+0

मेरा मानना ​​है कि "कॉल करने योग्य कोड लिखने-संरक्षित है" यह जवाब क्यों है कि यह असफल रहा है। मुझे संदेह है कि मैं यह कहने वाला पहला व्यक्ति हूं, लेकिन स्वयं संशोधित कोड आमतौर पर एक भयानक विचार या बग का लक्षण होता है। – DwB

+0

@DwB जैसा कि मैंने अपने प्रश्न में उल्लेख किया है, मुझे पता चला है कि मैं * उस अनुभाग में लिख सकता हूं जहां कार्यों को संग्रहीत किया जाता है। बस * कॉलिंग * ओवरराइट संस्करण का परिणाम क्रैश में होता है। – phimuemue

+0

भी, इस बात को ध्यान में रखें कि f1 शायद f2 से अधिक लंबा है ... यानी अधिक बाइट्स –

उत्तर

8

f1 के पहले कुछ बाइट्स को jumpf2 की शुरुआत के लिए निर्देश के साथ बदलना आसान होगा। इस तरह, आपको किसी भी संभावित कोड स्थानांतरण मुद्दों से निपटना नहीं होगा।

इसके अलावा, इस बारे में जानकारी कि फ़ंक्शन पर कितने बाइट्स हैं (FUN_LENGTH आपके प्रश्न में) आमतौर पर रनटाइम पर उपलब्ध नहीं है। jump का उपयोग करना उस समस्या से भी बच जाएगा।

x86 के लिए, आपको संबंधित रिश्तेदार कूद निर्देश ऑपोड E9 (here के अनुसार) है। यह एक 32-बिट रिश्तेदार कूद है, जिसका अर्थ है कि आपको f2 और f1 के बीच सापेक्ष ऑफसेट की गणना करने की आवश्यकता है। इस कोड को यह कर सकता है:

int offset = (int)f2 - ((int)f1 + 5); // 5 bytes for size of instruction 
char *pf1 = (char *)f1; 
pf1[0] = 0xe9; 
pf1[1] = offset & 0xff; 
pf1[2] = (offset >> 8) & 0xff; 
pf1[3] = (offset >> 16) & 0xff; 
pf1[4] = (offset >> 24) & 0xff; 

ऑफसेट अंत जेएमपी शिक्षा का से लिया जाता है, तो यह है कि यही कारण है कि वहाँ 5 ऑफसेट गणना में f1 का पता करने के लिए जोड़ा है।

यह सुनिश्चित करने के लिए कि आप सही बाइट्स को पोक कर रहे हैं, एक असेंबली स्तर डीबगर के साथ परिणाम के माध्यम से कदम उठाना एक अच्छा विचार है। बेशक, यह सभी मानक अनुरूप नहीं हैं, इसलिए यदि यह टूट जाता है तो आप दोनों टुकड़े रखने के लिए मिलता है।

+0

यह निश्चित रूप से एक विकल्प होगा। क्या आप जानते हैं कि सी प्रोग्राम में इसे कैसे किया जाए? – phimuemue

+0

निश्चित रूप से, 'f1' का पता लें और इसे' char * 'पर डालें। फिर, अपने आर्किटेक्चर के लिए उपयुक्त मशीन निर्देश के साथ पहले कुछ बाइट्स को ओवरराइट करें जो 'f2' पर कूद का कारण बनता है। आप एक पूर्ण पते का उपयोग करने में सक्षम हो सकते हैं या आपको रिश्तेदार कूद ऑफसेट की गणना करने और उस का उपयोग करने की आवश्यकता हो सकती है। आप किस वास्तुकला का उपयोग कर रहे हैं उसे जानने के बिना अधिक विस्तार से नहीं कह सकते हैं। –

+1

हम किस प्रकार के सीपीयू के बारे में बात कर रहे हैं? –

0

अधिकांश मेमोरी आर्किटेक्चर आपको फ़ंक्शन कोड पर लिखने से रोक देंगे। यह दुर्घटनाग्रस्त हो जाएगा .... लेकिन कुछ एम्बेडेड डिवाइस, आप इस तरह की चीज कर सकते हैं, लेकिन यह तब तक खतरनाक है जब तक आपको पता न हो कि पर्याप्त जगह है, कॉलिंग ठीक होने जा रही है, ढेर ठीक होने जा रहा है आदि। ..

सबसे अधिक संभावना समस्या हल करने के लिए एक बेहतर तरीका है।

1

आपका दृष्टिकोण सी मानक के लिए अपरिभाषित व्यवहार है।

और कई ऑपरेटिंग सिस्टम (उदा।लिनक्स), आपका उदाहरण क्रैश हो जाएगा: फ़ंक्शन कोड ईएलएफ निष्पादन योग्य के केवल .text सेगमेंट (और सेक्शन) को पढ़ने के अंदर है, और वह सेगमेंट (क्रमबद्ध) mmap-केवल पढ़ने के लिए execve (या dlopen द्वारा या गतिशील लिंकर), तो आप इसके अंदर नहीं लिख सकते हैं।

+1

हां, और न सिर्फ इसलिए कि यह कोड को संशोधित करने का प्रयास करता है। फ़ंक्शन पॉइंटर को 'char *' में कनवर्ट करने से व्यवहार को अपरिभाषित किया गया है। –

+0

हालांकि, नवीनतम पॉज़िक्स मानक में 'शून्य *' को कास्टिंग फ़ंक्शन पॉइंटर की अनुमति है (लेकिन सी मानक में नहीं, और कुछ अजीब आर्किटेक्चर के पास पॉइंटर के लिए फ़ंक्शन और पॉइंटर के लिए अलग-अलग आकार होते हैं)। –

1
इसके बजाय समारोह अधिलेखित करने के लिए कोशिश कर के

(आप पहले से ही मिल गया है, जिस पर सबसे अच्छा नाजुक है), मैं एक समारोह के लिए एक सूचक उपयोग करने पर विचार करेंगे:

int complex_implementation(int x) { 
    // do complex stuff with x 
    return x; 
} 

int simple_implementation(int x) { 
    return x; 
} 

int (*f1)(int) = complex_implementation; 

आप की तरह यह कुछ का उपयोग करेंगे:

for (int i=0; i<limit; i++) { 
    a = f1(a); 
    if (whatever_condition) 
     f1 = simple_implementation; 
} 

... और काम के बाद, बुला f1 बस इनपुट मूल्य लौट आते हैं।

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

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