क्या आप साइबर सुरक्षा कंपनी में काम करते हैं? क्या आप अपनी कंपनी का हैकट्रिक्स में विज्ञापन देखना चाहते हैं? या क्या आप PEASS के नवीनतम संस्करण या हैकट्रिक्स को पीडीएफ में डाउनलोड करना चाहते हैं? सब्सक्रिप्शन प्लान्स की जाँच करें!
पिछले प्रोग्राम में 9 प्रोग्राम हेडर्स हैं, फिर, सेगमेंट मैपिंग इसका इंडिकेट करता है कि किस प्रोग्राम हेडर (00 से 08 तक) में हर सेक्शन स्थित है।
PHDR - प्रोग्राम हेडर
प्रोग्राम हेडर तालिकाएं और मेटाडेटा स्वयं को शामिल करता है।
INTERP
बाइनरी को मेमोरी में लोड करने के लिए उपयोग करने वाले लोडर का पथ इंडिकेट करता है।
LOAD
ये हेडर्स इंडिकेट करने के लिए उपयोग किए जाते हैं कि मेमोरी में बाइनरी को कैसे लोड करें।
प्रत्येक LOAD हेडर एक मेमोरी क्षेत्र (आकार, अनुमतियाँ और संरेखण) इंडिकेट करता है और ELF बाइनरी के बाइट्स की कॉपी करने के लिए इंडिकेट करता है।
उदाहरण के लिए, दूसरा हेडर 0x1190 का आकार है, 0x1fc48 पर स्थित होना चाहिए जिसमें पढ़ने और लिखने की अनुमति है और 0xfc48 से 0x528 के साथ भरा जाएगा (यह सभी आरक्षित स्थान को भरता नहीं है)। यह मेमोरी सेक्शन .init_array .fini_array .dynamic .got .data .bss को शामिल करेगी।
DYNAMIC
यह हेडर प्रोग्रामों को उनके लाइब्रेरी आवश्यकताओं से लिंक करने और स्थानांतरण लागू करने में मदद करता है। .dynamic सेक्शन की जाँच करें।
NOTE
बाइनरी के बारे में विक्रेता मेटाडेटा जानकारी को संग्रहित करता है।
GNU_EH_FRAME
स्टैक अनवाइंड तालिकाओं का स्थान निर्धारित करता है, जिसे डीबगर और सी++ असाधारण हैंडलिंग-रनटाइम फ़ंक्शनों द्वारा उपयोग किया जाता है।
GNU_STACK
स्टैक निषेध रक्षा के कॉन्फ़िगरेशन को शामिल करता है। यदि सक्षम है, तो बाइनरी स्टैक से कोड नहीं चला सकेगा।
GNU_RELRO
बाइनरी की RELRO (स्थानांतरण केवल पढ़ने योग्य) कॉन्फ़िगरेशन को इंडिकेट करता है। यह सुरक्षा विशेष रूप से मेमोरी के कुछ सेक्शन को केवल पढ़ने योग्य चिह्नित करेगा (जैसे GOT या init और fini तालिकाएं) जब प्रोग्राम लोड हो जाता है और शुरू होने से पहले।
पिछले उदाहरण में यह 0x3b8 बाइट की कॉपी 0x1fc48 पर पढ़ने योग्य रूप से .init_array .fini_array .dynamic .got .data .bss सेक्शनों को प्रभावित करता है।
ध्यान दें कि RELRO आंशिक या पूर्ण हो सकता है, आंशिक संस्करण .plt.got सेक्शन को संरेखित करता है नहीं, जिसे लेजी बाइंडिंग के लिए उपयोग किया जाता है और इस स्मृति स्थान को लिखने की अनुमति होनी चाहिए जिसमें पहली बार उनकी स्थान खोजी जाती है।
TLS
TLS एंट्रीज की एक तालिका को परिभाषित करता है, जो सूत्र-स्थानीय चरणियों के बारे में जानकारी संग्रहित करती है।
सेक्शन हेडर्स
सेक्शन हेडर्स एलएफ बाइनरी का एक और विस्तृत दृश्य प्रदान करते हैं।
स्ट्रिंग टेबल: यह सभी स्ट्रिंग्स को संग्रहीत करता है जो ELF फ़ाइल द्वारा आवश्यक हैं (लेकिन वास्तव में कार्यक्रम द्वारा उपयोग की जाने वाली नहीं।) उदाहरण के लिए, यह खंड धाराएँ नामों को शामिल करता है जैसे .text या .data। और यदि .text स्ट्रिंग टेबल में 45 ऑफसेट पर है तो यह नाम फ़ील्ड में नंबर 45 का उपयोग करेगा।
स्ट्रिंग टेबल का पता लगाने के लिए, ELF में स्ट्रिंग टेबल के लिए एक पॉइंटर होता है।
सिम्बल टेबल: इसमें सिम्बल के बारे में जानकारी होती है जैसे नाम (स्ट्रिंग टेबल में ऑफसेट), पता, आकार और सिम्बल के बारे में अधिक मेटाडेटा।
मुख्य खंड
.text: कार्यक्रम के निर्देशों को चलाने के लिए।
.data: कार्यक्रम में परिभाषित मान वाले ग्लोबल वेरिएबल्स।
.bss: अपरिभाषित ग्लोबल वेरिएबल्स (या जीरो से आरंभित)। यहाँ वेरिएबल्स स्वचालित रूप से शून्य पर आरंभित होते हैं इसलिए बाइनरी में अनावश्यक शून्यों को जोड़ने से बचाते हैं।
.rodata: स्थिर ग्लोबल वेरिएबल्स (केवल पढ़ने योग्य खंड)।
.tdata और .tbss: जब स्थानीय धागा चर उपयोग किया जाता है (C++ में __thread_local या __thread में) तो जैसे .data और .bss।
.dynamic: नीचे देखें।
सिम्बल्स
सिम्बल्स कार्यक्रम में एक नामित स्थान है जो एक फ़ंक्शन, एक ग्लोबल डेटा ऑब्जेक्ट, स्थानीय धागा चर वेरिएबल्स हो सकता है...
readelf -s lnstat
Symbol table '.dynsym' contains 49 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000001088 0 SECTION LOCAL DEFAULT 12 .init
2: 0000000000020000 0 SECTION LOCAL DEFAULT 23 .data
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strtok@GLIBC_2.17 (2)
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND s[...]@GLIBC_2.17 (2)
5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strlen@GLIBC_2.17 (2)
6: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fputs@GLIBC_2.17 (2)
7: 0000000000000000 0 FUNC GLOBAL DEFAULT UND exit@GLIBC_2.17 (2)
8: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _[...]@GLIBC_2.34 (3)
9: 0000000000000000 0 FUNC GLOBAL DEFAULT UND perror@GLIBC_2.17 (2)
10: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterT[...]
11: 0000000000000000 0 FUNC WEAK DEFAULT UND _[...]@GLIBC_2.17 (2)
12: 0000000000000000 0 FUNC GLOBAL DEFAULT UND putc@GLIBC_2.17 (2)
[...]
द्यनामिक खंड
प्रत्येक प्रतीक प्रविष्टि में निम्नलिखित शामिल है:
नाम
बाइंडिंग गुण (कमजोर, स्थानीय या वैश्विक): स्थानीय प्रतीक केवल कार्यक्रम द्वारा ही पहुंचा जा सकता है जबकि वैश्विक प्रतीक कार्यक्रम के बाहर साझा होते हैं। एक कमजोर वस्तु उदाहरण के लिए एक ऐसा कार्य है जिसे एक भिन्न कार्य द्वारा ओवरराइड किया जा सकता है।
प्रकार: NOTYPE (कोई प्रकार निर्दिष्ट नहीं), OBJECT (ग्लोबल डेटा वेरिएबल), FUNC (कार्य), SECTION (खंड), FILE (डीबगर्स के लिए स्रोत-कोड फ़ाइल), TLS (सूत्र-स्थलीय चर), GNU_IFUNC (पुनर्स्थापन के लिए अप्रत्यक्ष कार्य)
आवश्यकता निर्देशिका इसका सूचित करती है कि कार्यक्रम जारी रखने के लिए उक्त पुस्तकालय को लोड करना आवश्यक है। जब साझा पुस्तकालय पूरी तरह से संचालनयोग्य और उपयुक्त हो जाता है, तो आवश्यकता निर्देशिका पूरी हो जाती है।
पुनर्थान
लोडर को इन्हें भी उन्हें लोड करने के बाद आवश्यकता होती है कि वह आवश्यकताओं को पुनर्थान करे। ये पुनर्थान पुस्तकालय में पुनर्थान सारणी में REL या RELA प्रारूप में दिखाए जाते हैं और पुनर्थानों की संख्या गतिशील खंडों RELSZ या RELASZ में दी जाती है।
यदि प्रोग्राम पसंदीदा पते से भिन्न स्थान पर लोड किया गया है (सामान्यत: 0x400000) क्योंकि पता पहले से ही उपयोग में है या ASLR या किसी अन्य कारण से, एक स्थैतिक पुनर्स्थापना सही करती है जिन पॉइंटर्स की मानें बाइनरी को पसंदीदा पते पर लोड होने की उम्मीद थीं।
उदाहरण के लिए किसी भी खंड का प्रकार R_AARCH64_RELATIV को पुनर्स्थापना बायस प्लस एडेंड मान के साथ संशोधित होना चाहिए।
गतिशील पुनर्स्थापना और GOT
पुनर्स्थापना एक बाह्य प्रतीक (जैसे एक निर्भरता से फ़ंक्शन) को भी संदर्भित कर सकती है। जैसे libC से malloc फ़ंक्शन। तब, जब लोडर libC को एक पते में लोड करता है जहां malloc फ़ंक्शन लोड हो रहा है, तो यह लोडर गॉट (ग्लोबल ऑफ़सेट टेबल) टेबल में इस पते को लिखेगा (पुनर्स्थापना तालिका में सूचित) जहां malloc का पता निर्दिष्ट होना चाहिए।
प्रक्रिया संबंधन सारणी
पीएलटी खंड को आलसी बाइंडिंग करने की अनुमति देता है, जिसका मतलब है कि फ़ंक्शन के स्थान का संक्षेपण केवल उस समय किया जाएगा जब यह उपयोग किया जाएगा।
तो जब एक प्रोग्राम malloc को कॉल करता है, वास्तव में यह malloc के संबंधित स्थान को पीएलटी में कॉल करता है (malloc@plt)। जब यह पहली बार कॉल होता है, तो यह malloc का पता स्थापित करता है और इसे संभालता है ताकि अगली बार malloc को कॉल किया जाए, उस पते का उपयोग किया जाए बजाय पीएलटी कोड का।
प्रोग्राम प्रारंभीकरण
प्रोग्राम लोड होने के बाद इसे चलाने का समय है। हालांकि, पहला कोड जो चलाया जाता है हमेशा main नहीं होता है। यह इसलिए है क्योंकि उदाहरण के लिए C++ में यदि एक ग्लोबल चर एक कक्षा का ऑब्जेक्ट है, तो यह ऑब्जेक्ट प्रारंभहोना चाहिएमुख्य चलता है, जैसे:
ध्यान दें कि ये ग्लोबल वेरिएबल्स .data या .bss में स्थित होते हैं लेकिन सूचियों __CTOR_LIST__ और __DTOR_LIST__ में ऑब्जेक्ट्स को इनिशियलाइज और डीस्ट्रक्ट करने के लिए क्रम में स्टोर किया जाता है ताकि उनका ट्रैक रखा जा सके।
सी कोड से GNU एक्सटेंशन का उपयोग करके एक ही परिणाम प्राप्त किया जा सकता है:
__attributte__((constructor)) //Add a constructor to execute before__attributte__((destructor)) //Add to the destructor list
कॉंपाइलर की दृष्टि से, main फ़ंक्शन के पहले और बाद में इन क्रियाओं को निष्पादित करने के लिए, एक init फ़ंक्शन और एक fini फ़ंक्शन बनाना संभव है जिन्हें डायनामिक सेक्शन में INIT और FIN के रूप में संदर्भित किया जा सकता है और ये ELF के init और fini सेक्शन में रखे जाते हैं।
दूसरा विकल्प, जैसा कि उल्लिखित है, डायनामिक सेक्शन में INIT_ARRAY और FINI_ARRAY प्रविष्टियों में सूची __CTOR_LIST__ और __DTOR_LIST__ को संदर्भित करना है और इनकी लंबाई को INIT_ARRAYSZ और FINI_ARRAYSZ द्वारा दर्शाया जाता है। प्रत्येक प्रविष्टि एक फ़ंक्शन पॉइंटर है जो बिना तर्क के बुलाया जाएगा।
इसके अतिरिक्त, INIT_ARRAY पॉइंटरों से पहले निष्पादित होने वाले पॉइंटर के साथ एक PREINIT_ARRAY भी संभव है।