2010-07-18 6 views
5

मेरी कोड है को प्रभावित करता है):LD_PRELOAD नए बच्चे unsetenv ("LD_PRELOAD") के बाद भी इस प्रकार

gcc preload.c -shared -Wl,-soname,mylib -o mylib.so -fPIC 
    LD_PRELOAD=./mylib.so bash 

!!! आखिरी कमांड के साथ सावधान रहें, जिसके परिणामस्वरूप "sh -c ls" फोर्किंग के अंतहीन पाश का परिणाम होगा।^सी के साथ 2 सेकंड के बाद इसे रोकें, (या बेहतर^ज़ेड और फिर पीएस देखें)।

और जानकारी

  1. यह समस्या किसी तरह से पार्टी की योजना बनाई से संबंधित हैं; या तो उपयोगकर्ता द्वारा चलाए जाने वाले आदेश के रूप में, या पॉप को निष्पादित करने के रूप में।
  2. अतिरिक्त कुंजी कारक: 1) प्री-लोडेड लाइब्रेरी से पॉपन निष्पादित करें, 2) शायद लाइब्रेरी के प्रारंभिक अनुभाग में पॉपन करने की आवश्यकता है।
  3. अगर आप का उपयोग करें:

    LD_DEBUG=all LD_DEBUG_OUTPUT=/tmp/ld-debug LD_PRELOAD=./mylib.so bash 
    
    पिछले आदेश के बजाय

    , आप /tmp/ld-debug.* नामित कई ld-डिबग फ़ाइलें प्राप्त होगा। प्रत्येक फोर्क प्रक्रिया के लिए एक। इन सभी फाइलों में आप देखेंगे कि प्रतीकों को पहली बार mylib.so में खोजा गया है, भले ही एलडी_PRELOAD को पर्यावरण से हटा दिया गया हो।

+0

हम किस भाषा के बारे में बात कर रहे हैं? – mvds

+0

हम सी भाषा – avner

+0

@ user395074 के बारे में बात कर रहे हैं, तो शायद, आपको भाषा को प्रतिबिंबित करने के लिए अपने टैग को समायोजित करना चाहिए था ("संपादन" लिंक पर क्लिक करें)। साथ ही, [प्रीलोडर] टैग ओएस घटक को प्रतिबिंबित करने जैसा प्रतीत नहीं होता है जिस पर हम चर्चा कर रहे हैं। –

उत्तर

8

संपादित करें: तो समस्या/प्रश्न वास्तव में था: howcome नहीं कर सकते bash के भीतर से प्रीलोडेड main_init() का उपयोग करके LD_PRELOAD विश्वसनीय रूप से अनसेट करें।

कारण यह है कि execve, जो आप popen, से (शायद)

extern char **environ; 

जो कुछ वैश्विक राज्य चर कि अपने वातावरण ओर इशारा करता है पर्यावरण लेता है के बाद कहा जाता है। unsetenv() सामान्य रूप से आपके पर्यावरण को संशोधित करता है और इसलिए **environ की सामग्री पर प्रभाव डालता है।

यदि bash पर्यावरण के साथ कुछ विशेष करने की कोशिश करता है (अच्छा ... क्या यह एक खोल होगा?) तो आप परेशानी में हो सकते हैं।

अपरिवर्तनीय, bashunsetenv()main_init() से पहले भी अधिभारित करता है। उदाहरण कोड को बदलना:

extern char**environ; 

int __attribute__((constructor)) main_init(void) 
{ 
int i; 
printf("Unsetting LD_PRELOAD: %x\n",unsetenv("LD_PRELOAD")); 
printf("LD_PRELOAD: \"%s\"\n",getenv("LD_PRELOAD")); 
printf("Environ: %lx\n",environ); 
printf("unsetenv: %lx\n",unsetenv); 
for (i=0;environ[i];i++) printf("env: %s\n",environ[i]); 
fflush(stdout); 
FILE *fp = popen("ls", "r"); 
pclose(fp); 
} 

समस्या दिखाता है।

unsetenv: 7f4c78fd5290 
unsetenv: 7f1127317290 
unsetenv: 7f1ab63a2290 

हालांकि, bash या sh चल:

unsetenv: 46d170 

तो, वहाँ तुम्हारे पास है सामान्य रन में मैं unsetenv के इस संस्करण मिल (cat, ls, आदि चल रहे)। bash आप ;-) मूर्ख बनाया गया है

तो बस अपने खुद के unsetenv का उपयोग कर, **environ पर अभिनय जगह में पर्यावरण को संशोधित:

for (i=0;environ[i];i++) 
{ 
    if (strstr(environ[i],"LD_PRELOAD=")) 
    { 
     printf("hacking out LD_PRELOAD from environ[%d]\n",i); 
     environ[i][0] = 'D'; 
    } 
} 

strace में काम करने के लिए देखा जा सकता है जो:

execve("/bin/sh", ["sh", "-c", "ls"], [... "DD_PRELOAD=mylib.so" ...]) = 0 

QED

+0

एलडी_PRELOAD बच्चे की प्रक्रिया के माहौल में नहीं है (न तो माता-पिता में unsetenv के बाद)। पावेल का उत्तर इस कारण के बारे में संकेत देता है: लोडर द्वारा एलडी_PRELOAD का उपयोग किया जाता है, न कि मेरे कार्यक्रम द्वारा। mylib.so पहले ही लोड हो चुका है और लोडर शायद एलडी_PRELOAD को फिर से पढ़े बिना अपना व्यवहार रखता है। कांटा और execle एक विकल्प है; हालांकि, पॉपन बहुत पसंद किया गया है क्योंकि यह मुझे सरल तरीके से बाल प्रक्रिया के आउटपुट को पढ़ने देता है। स्ट्रेस ग्रेस बहुत सारी जानकारी है जो मेरी मदद नहीं करती है। मैंने उपयोग किया: निर्यात LD_DEBUG = symnols और स्पष्ट रूप से देखा कि सभी प्रतीकों को किसी अन्य lib से पहले mylib.so में खोजा जाता है। – avner

+0

यह और अधिक दिलचस्प हो रहा है, बस कुछ और सुझावों के साथ मेरा उत्तर अपडेट किया गया – mvds

+0

@avner: उत्तर में और भी सुझाव, अपना स्वयं का '.so' बनाया और अभी भी आप जो देख रहे हैं उसे नहीं देख सकते हैं ... – mvds

2

(जवाब एक शुद्ध अटकलें है, और हो सकता है सही नहीं है)।

शायद, जब आप अपनी प्रक्रिया को फोर्क करते हैं, तो लोड किए गए पुस्तकालयों का संदर्भ बनी रहती है। तो, mylib.so लोड किया गया था जब आपने मुख्य कार्यक्रम को LD_PRELOAD के माध्यम से बुलाया था। जब आप चर और फोर्कड को अनसेट करते हैं, तो इसे फिर से लोड नहीं किया गया था; हालांकि यह पहले से ही को पैरेंट प्रक्रिया द्वारा लोड किया गया है। शायद, आपको फोर्किंग के बाद इसे स्पष्ट रूप से अनलोड करना चाहिए।

आप mylib.so में प्रतीकों को "demote" करने का भी प्रयास कर सकते हैं। ऐसा करने के लिए, dlopen के माध्यम से इसे फिर से खोलना झंडे है कि यह प्रतीक संकल्प कतार के अंत करने के लिए जगह के साथ:

dlopen("mylib.so", RTLD_NOLOAD | RTLD_LOCAL); 

+0

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

+0

क्या आप कह रहे हैं कि 'निष्पादित' पुस्तकालयों के बाद 'निष्पादन' के बाद? मैं एक स्ट्रेस में देखता हूं कि 'निष्पादन' के बाद भी libc फिर से खोला जाता है, कल्पना नहीं कर सकता कि कुछ अन्य पुस्तकालय प्रबल होंगे। – mvds

+0

@ एमवीडीएस, फिर से खोला जा रहा है और अनलोड/लोड किया जा रहा है अलग-अलग चीजें हैं। आप पहले से लोड की गई लाइब्रेरी को फिर से खोल सकते हैं। –

0

एमवीडीएस का उत्तर गलत है!

popen() बाल प्रक्रिया को जन्म देगा जो preloaded का वारिस होगा। इसलिए पैरेंट प्रक्रिया में झूठ बोला। इस बच्चे की प्रक्रिया एलडी_PRELOAD पर्यावरण परवाह नहीं है।

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