अनुप्रयोग सत्यापनकर्ता के साथ मेमोरी एक्सेस त्रुटियों को डीबग करना

हेब्रूसर बर्डकोवड ने क्यू एंड ए में सी ++, वेक्टर और किसी और की याद में लिखने के बारे में एक कार्य निर्धारित किया। अन्य बातों के अलावा, यह काम अच्छा है कि आप आसानी से आवेदन सत्यापनकर्ता उपकरण का उपयोग कर सकते हैं और यह पता लगा सकते हैं कि कौन स्मृति को खराब करता है।

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

उपकरण


एप्लिकेशन वेरिफायर के अलावा, हमें WinDBG की आवश्यकता है, जिसमें विंडोज के लिए माइक्रोसॉफ्ट डिबगिंग टूल्स के साथ एक फ्री डिबगर शामिल है। पहले, डिबगिंग टूल को अलग से डाउनलोड किया जा सकता था, लेकिन अब किसी कारण से, केवल विंडोज एसडीके या विंडोज ड्राइवर किट के हिस्से के रूप में। लेकिन आप अभी भी पिछले संस्करण को अलग से डाउनलोड कर सकते हैं, जो हमारे कार्यों के लिए एकदम सही है। ठीक है, या यहां मैंने नवीनतम संस्करण (6.12.2.633) पोस्ट किए, ताकि पूरे एसडीके को डाउनलोड न करें: dbg_x86.msi , dbg_amd64.msi

आपको विज़ुअल सी ++ (किसी भी संस्करण, नए, शायद, वीएस 200, आप एक्सप्रेस कर सकते हैं) या विंडोज एसडीके से सी ++ कंपाइलर की आवश्यकता होगी। हमें Microsoft से एक कंपाइलर की आवश्यकता है, न कि MinGW की, क्योंकि हमें PDB डिबगिंग जानकारी की आवश्यकता है जो WinDBG समझती है।

एक उदाहरण रखना


हम ऊपर बताई गई समस्या में स्रोत को लेते हैं ( पेस्टी पर कॉपी )। हम इसे डिबगिंग जानकारी (कंपाइलर के लिए कुंजी / ज़ी या / ZI और लिंकर के लिए DEBUG) और विकलांग ऑप्टिमाइज़ेशन के साथ इकट्ठा करते हैं। कंसोल से बनाने के लिए कमांड लाइन कुछ इस तरह दिखाई देगी:
cl /D_DEBUG /Zi /Od /EHsc /DEBUG /MDd vector_misuse.cpp

अनुप्रयोग सत्यापन कॉन्फ़िगर करें

  1. हम प्रशासक विशेषाधिकारों के साथ AppVerifier शुरू करते हैं।
  2. फ़ाइल चुनें-> एप्लिकेशन जोड़ें (या Ctrl + A), हमारे दुरुपयोग_वेक्टर को खोजें, खोलें पर क्लिक करें।
  3. बेसिक नोड से सभी चेकमार्क निकालें।
  4. नोड बेसिक पर चेकमार्क सेट करें-> हीप्स। बस मामले में, आइए इस नोड के गुणों में जाएं (इस पर राइट-क्लिक करें- गुण) और सुनिश्चित करें कि पूर्ण (बहुत ऊपर) और विपरीत निशान (लगभग डायलॉग के बीच में) के विपरीत चेकबॉक्स चालू हैं। यदि यह चालू नहीं है, तो इसे चालू करें और ठीक पर क्लिक करें।
  5. सेव बटन पर क्लिक करें।

डिबगर को कॉन्फ़िगर करें

  1. File-> Symbol File Path पर जाएं ... और वहां srv*c:\mysymbols*http://msdl.microsoft.com/download/symbols । इसका अर्थ है कि डीबगर पहले c: \ mysymbols निर्देशिका में वर्णों की खोज करेगा, और यदि ऐसा नहीं होता है, तो यह इंटरनेट से Microsoft प्रतीक स्टोर से डाउनलोड करेगा। सुंदर कॉल स्टैक्स देखने के लिए सार्वजनिक प्रतीकों की आवश्यकता होती है। आप .symfix+ c:\mysymbols उपयोग कर सकते हैं, लेकिन एप्लिकेशन के डिबगर में लोड होने के बाद।
  2. फ़ाइल में-> ओपन एक्ज़ीक्यूटेबल ... (Ctrl + E) हमारे दुरुपयोग_करने वाले। Exe का चयन करें। हम कार्यक्षेत्र रखने के प्रस्ताव से सहमत हैं। डीबगर छवि में मेमोरी लोड करेगा, लेकिन निष्पादन शुरू नहीं करेगा।
  3. हम निष्पादन पर एक उदाहरण लॉन्च करते हैं - डिबग-> गो (या F5, या डी डिबगर प्रॉम्प्ट पर)।
यदि आपने पहले WinDBG के साथ काम नहीं किया है, तो यह व्यू-> फ़ॉन्ट मेनू में देखने और फ़ॉन्ट को कॉन्फ़िगर करने के लिए समझ में आता है। जो डिफ़ॉल्ट रूप से स्थापित है वह आपको पूरी तरह से पागल लग सकता है (या ऐसा नहीं लग सकता है)।

पतन का कारण ज्ञात कीजिए


जब हम प्रोग्राम चलाते हैं, तो यह एक्सेस वॉयलेशन के साथ क्रैश हो जाएगा।

हम स्टैक को देखते हैं - देखें कॉल स्टैक (या Alt + 6 या निमंत्रण पर kp) और देखें कि फ़ंक्शन च में क्या गिर गया, नेस्टिंग के दूसरे स्तर पर। कॉल स्टैक विंडो में दिखाई देने वाले फ़ंक्शन तर्कों के लिए, स्रोत args बटन पर क्लिक करें। कोड की लाइनों के लिंक देखने के लिए, सोर्स बटन पर क्लिक करें। Kp कमांड डिबगर की कमांड विंडो में इस जानकारी को प्रदर्शित करेगा। स्रोत पाठ के साथ एक विंडो भी खुलनी चाहिए और इसमें वर्तमान लाइन को हाइलाइट किया जाएगा।

ठीक है, हम देखते हैं कि समस्या लाइन में है
 v[i] += f(x / 2); 
लेकिन वास्तव में उसके साथ क्या गलत है? डिबगर इस प्रश्न का उत्तर देगा यदि आप उसे सही तरीके से पूछते हैं। हम प्रॉम्प्ट पर लिखते हैं !analyze -v और एंटर दबाएं।

डिबगर हमें पाठ की एक शीट डंप करेगा जिसमें से हम निम्नलिखित चीजों में रुचि रखते हैं:
DEFAULT_BUCKET_ID: INVALID_POINTER_READ - अमान्य सूचक द्वारा पढ़ने का प्रयास
READ_ADDRESS: 060a0ff4 - स्वयं पता, जिस पर हमने पढ़ने की कोशिश की।

कॉल स्टैक जिसे हमने पहले ही देखा था और यहां तक ​​कि स्रोत का एक टुकड़ा भी चिह्नित रेखा के साथ जहां अपवाद हुआ था, भी मुद्रित किया जाएगा।

यह निश्चित रूप से बहुत दिलचस्प है, लेकिन मैं यह जानना चाहूंगा कि इस स्मृति को क्यों नहीं पढ़ा जा सकता है? उन सेटिंग्स के लिए धन्यवाद, जो हमने AppVerifier में किए थे, सिस्टम ने कॉल स्टैक एकत्र किए और उन्हें प्रत्येक मेमोरी आवंटन और डीलक्लोकेशन के साथ सावधानी से संग्रहीत किया, ताकि हम उन्हें हमारे अनुरोध पर प्रदान कर सकें।

हम डिबगर आमंत्रण !heap -p -a 060a0ff4 (यहां आपको उस पते को स्थानापन्न करने की आवश्यकता होगी जो आपके पास READ_ADDRESS में होगा, यह सबसे अधिक भिन्न होगा। इसके लिए डीबगर यह बताएगा कि यह पता ऐसे हीप का है। एक कॉल स्टैक के साथ इस तरह से मुक्त किए गए एक आकार (फ्री-एड आवंटन में):
     5da190b2 सत्यापनकर्ता! AVrfDebugPageHeapFree + 0x000000c2
     77cd1464 ntdll! RtlDebugFreeHeap + 0x0000002f
     77c8ab3a ntdll! RtlpFreeHeap + 0x0000005d
     77c33472 ntdll! RtlFreeHeap + 0x00000142
     75cc14dd कर्नेल 32! HeapFree + 0x00000014
     5c677f59 MSVCR100D! _Free_base + 0x00000029
     5c687a4e MSVCR100D! _Free_dbg_nolock + 0x000004ae
     5c687560 MSVCR100D! _Free_dbg + 0x00000050
     5c686629 MSVCR100D! ऑपरेटर हटाएं + 0x000000b9
     00f71af0 वेक्टर_misuse! एसटीडी :: आवंटनकर्ता <int> :: deallocate + 0x00000010
     00f7193b वेक्टर_मिस्यू! Std :: वेक्टर <int, std :: एलोकेटर <int> :: रिजर्व + 0x0000010b
     00f716db वेक्टर_स्मिस्यू! Std :: वेक्टर <int, std :: एलोकेटर <int> :: _ रिजर्व + 0x0000005b
     00f714c4 वेक्टर_स्मिस्यू! Std :: वेक्टर <int, std :: एलोकेटर <int> :: push_back + 0x000000c4
     00f712dc वेक्टर_misuse! F + 0x0000002c
     00f7130b वेक्टर_misuse! F + 0x0000005b
     00f7130b वेक्टर_misuse! F + 0x0000005b
     00f7134b वेक्टर_स्मिस्यू! मुख्य + 0x0000000b
     00f7323f वेक्टर_misuse! __ tmainCRTStartup + 0x000001bf
     00f7306f वेक्टर_मिस्यू! मेनक्रर्टार्टअप + 0x0000000f
     75cc33ca कर्नेल32! BaseThreadInitThunk + 0x0000000e
     77c39ed2 ntdll! __ RtlUserThreadStart + 0x00000070
     77c39ea5 ntdll! _RtlUserThreadStart + 0x0000001b 

इस प्रकार, हमने सीखा कि अगले वेक्टर पर पुनरावृत्ति के तीसरे स्तर पर :: push_back, वेक्टर ने अपने आकार (वेक्टर :: रिजर्व) को बदलने का फैसला किया, जिसके कारण इस वेक्टर का वास्तविक रूप से पता चला (std :: आवंटनकर्ता - स्टैकलोकेट और आगे स्टैक नीचे) और बाद में दूसरे स्तर पर लौटने पर मुक्त मेमोरी तक पहुंच।

कुल मिलाकर


मुझे हमेशा सुंदर निष्कर्ष और सारांश लिखने में समस्या होती है, इसलिए कोई समस्या नहीं होगी। स्मार्ट लोग, वे स्वयं आवश्यक निष्कर्ष निकालेंगे :)

आपका ध्यान देने के लिए धन्यवाद। :)

Source: https://habr.com/ru/post/In117810/


All Articles