
No execution के दौरान पेज प्रोटेक्शन बदलावों को लागू करते हुए, वर्तमान थ्रेड को समाप्त करने और निष्पादन फिर से शुरू करने से पहले इसे पुनर्स्थापित करने की एक एवेज़न तकनीक के लिए PoC कार्यान्वयन।
██████╗ ███████╗ █████╗ ████████╗██╗ ██╗███████╗██╗ ███████╗███████╗██████╗
██╔══██╗██╔════╝██╔══██╗╚══██╔══╝██║ ██║██╔════╝██║ ██╔════╝██╔════╝██╔══██╗
██║ ██║█████╗ ███████║ ██║ ███████║███████╗██║ █████╗ █████╗ ██████╔╝
██║ ██║██╔══╝ ██╔══██║ ██║ ██╔══██║╚════██║██║ ██╔══╝ ██╔══╝ ██╔═══╝
██████╔╝███████╗██║ ██║ ██║ ██║ ██║███████║███████╗███████╗███████╗██║
╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝╚══════╝╚═╝
वर्तमान थ्रेड को समाप्त करने और निष्पादन फिर से शुरू करने से पहले इसे पुनर्स्थापित करने के लिए एक चोरी तकनीक का PoC कार्यान्वयन, जबकि नो एक्ज़ीक्यूशन के दौरान पेज सुरक्षा परिवर्तन लागू किया जाता है।

स्लीप और अस्पष्टीकरण विधियाँ maldev समुदाय में प्रसिद्ध हैं, विभिन्न कार्यान्वयनों के साथ, उनका उद्देश्य सोते समय मेमोरी स्कैनर्स से छिपना है, आमतौर पर पेज सुरक्षाएँ बदलना और शेलकोड को एन्क्रिप्ट करने जैसी शानदार सुविधाएँ जोड़ना, लेकिन हमारे शेलकोड को छिपाने का एक और महत्वपूर्ण बिंदु है, और वह है वर्तमान निष्पादन थ्रेड को छिपाना। स्टैक को स्पूफ करना अच्छा है, लेकिन इसके बारे में थोड़ा सोचने के बाद मैंने सोचा कि स्टैक को स्पूफ करने की कोई आवश्यकता नहीं है... अगर कोई स्टैक ही नहीं है :)
इस तकनीक की उपयोगिता का आकलन पाठक पर छोड़ दिया गया है, लेकिन किसी भी मामले में, मुझे लगता है कि यह कुछ विषयों की समीक्षा करने और उन लोगों के लिए कुछ maldev सीखने का एक अच्छा तरीका है जो, मेरी तरह, इस दुनिया में शुरुआत कर रहे हैं।
यहाँ दिखाया गया मुख्य कार्यान्वयन वह सब कुछ रखता है जिसे हमें स्टैक से बाहर निकालने की आवश्यकता है डेटा अनुभाग में, वैश्विक चर के रूप में, लेकिन सब कुछ हीप में ले जाने वाला एक कार्यान्वयन जल्द ही प्रकाशित किया जाएगा। इसका उद्देश्य कुछ प्रमुख संशोधन दिखाना है जिन्हें इस कोड को pic और इंजेक्टेबल बनाने के लिए करने की आवश्यकता है।
यह रिपॉजिटरी GitHub और GitLab के बीच मिरर की गई है।
यहाँ बताई गई सभी बातें विभिन्न विषयों की मेरी समझ से आती हैं, या तो पढ़ने से या विकास के दौरान अनुभव से। मैं जानता हूँ कि मैं कोई विशेषज्ञ नहीं हूँ और सबसे आखिरी चीज जो मैं करना चाहता हूँ वह है गलत सूचना फैलाना, इसलिए यदि आपको लगता है कि कुछ सही नहीं है, तो मुझे खुशी होगी अगर आप मुझे इसके बारे में बताएँ, आप मुझसे ट्विटर पर संपर्क कर सकते हैं, या इस रेपो में इश्यू खोल सकते हैं। आपकी समझ के लिए बहुत-बहुत धन्यवाद। :)
इस तकनीक का मुख्य उद्देश्य स्पष्ट है, वर्तमान थ्रेड को समाप्त करना और निष्पादन फिर से शुरू करने से पहले इसे पुनर्स्थापित करना, लेकिन इसका वास्तव में क्या मतलब है, और यह कौन सी नई बाधाएँ लाता है?
निष्पादन को पुनर्स्थापित करने में सक्षम होने के लिए, हमें थ्रेड को समाप्त करने से पहले दो चीजों को सहेजना होगा, पहला, CPU स्थिति, और दूसरा स्टैक, और नया थ्रेड लॉन्च होने के बाद उन्हें प्रभावी ढंग से फिर से सेट करना होगा।
मैंने इस तकनीक में दिखाई देने वाली नई बाधाओं के बारे में बात की, और दो बड़ी हैं: पहला, हमें उस क्षण से जब थ्रेड समाप्त होता है जब तक स्टैक पुनर्स्थापित नहीं हो जाता, स्टैक के बाहर किसी भी चीज़ को संग्रहीत करने की आवश्यकता है, और जैसा कि आप देखेंगे, यह कुछ नई चुनौतियाँ पैदा करता है।
दूसरा, हमें हमेशा अपनी प्रक्रिया में कम से कम एक और थ्रेड चलाने की आवश्यकता होती है, क्योंकि हम अपने थ्रेड को समाप्त कर रहे हैं, यदि कोई अन्य थ्रेड नहीं है तो प्रक्रिया समाप्त हो जाएगी। मुझे नहीं लगता कि यह एक बड़ी समस्या है, क्योंकि अधिकांश एजेंट अन्य प्रक्रियाओं में इंजेक्ट किए जाते हैं, हम मान सकते हैं कि यह प्रक्रिया कम से कम एक थ्रेड चलाती रहेगी।
हम इस POC में 4 मुख्य कार्य देख सकते हैं:
जब हम स्टैक को सहेजने वाले होते हैं, तो एक प्रश्न उठता है: स्टैक का कितना भाग सहेजने की आवश्यकता है?
आइए पहले समीक्षा करें कि DeathSleep फंक्शन को कॉल करने के बाद स्टैक में क्या है (यह वह फंक्शन है जो संदर्भ, स्टैक को सहेजता है और अस्पष्टीकरण और पुनर्स्थापना के लिए सब कुछ तैयार करता है)

जैसा कि हम देख सकते हैं, प्रत्येक फंक्शन के तीन भाग होते हैं:
स्टैक का न्यूनतम भाग जिसे हमें निश्चित रूप से सहेजने की आवश्यकता है, वह हमारे मुख्य प्रोग्राम के अंदर सब कुछ है, अर्थात इसका शैडो स्पेस, इसका रिटर्न एड्रेस और DeathSleep फंक्शन तक सब कुछ। इससे पहले कुछ भी वास्तव में आवश्यक नहीं है (एंट्री फंक्शन द्वारा उपयोग किए गए स्टैक को सहेजने के अपने फायदे हैं, लेकिन हम बाद में इस पर चर्चा करेंगे), क्योंकि यह हमारे नए थ्रेड को लॉन्च करने के लिए विंडोज रूटीन द्वारा उपयोग किया जाने वाला स्टैक है। इसके अलावा, मैंने DeathSleep फंक्शन के शैडो स्पेस को भी संग्रहीत करने का निर्णय लिया (वास्तव में आवश्यक नहीं है, लेकिन यह जागने के समय Rsp की गणना को आसान बनाता है)।
तो अंत में, हम यह सहेज रहे हैं:

एक मानक संकलन पर प्रत्येक फंक्शन 3 भागों से बना होना चाहिए, प्रस्तावना, फंक्शन कोड, और उपसंहार।
Rsp (स्टैक पॉइंटर) को केवल फंक्शन प्रस्तावना और उपसंहार पर संशोधित किया जाना चाहिए। प्रस्तावना स्टैक पॉइंटर को बढ़ाती है (याद रखें स्टैक बढ़ाने का मतलब पतों को घटाना है, क्योंकि वे विपरीत दिशाओं में जाते हैं), रजिस्टरों को सहेजने, अपने सभी स्थानीय चरों को रखने और फिर शैडो स्पेस को रखने के लिए, और उपसंहार बिल्कुल विपरीत करता है।
इसका मतलब है कि फंक्शन कोड के अंदर स्टैक पॉइंटर को हमेशा शैडो स्पेस के अंत (उपरोक्त चित्र में बैंगनी) की ओर इंगित करना चाहिए, और फंक्शन के स्टैक आकार और शैडो स्पेस का योग इस गणना से पाया जा सकता है कि प्रस्तावना स्टैक पॉइंटर को कितना बढ़ाती है। यह मान अनवाइंड टेबल में मौजूद जानकारी का उपयोग करके आसानी से गणना किया जा सकता है, उनके उपयोग के बारे में एक स्पष्टीकरण यहाँ शामिल नहीं किया जाएगा, लेकिन सारांश के रूप में, ये तालिकाएँ किसी भी अन्य थ्रेड या प्रक्रिया को स्टैक के माध्यम से सही ढंग से स्थानांतरित करने, इसकी सामग्री देखने, अपवादों को संभालने या इसका विश्लेषण करने की अनुमति देने के लिए उपयोग की जाती हैं।
संदर्भ को कैप्चर करना शायद सबसे आसान कामों में से एक है, क्योंकि हम DeathSleep की पहली पंक्ति में, गैर-अस्थायी रजिस्टरों में किसी भी संशोधन से पहले, बस RtlCaptureContext() कर सकते हैं। हमें अभी भी उस संदर्भ में दो संशोधन करने की आवश्यकता है जहाँ हम निष्पादन को पुनर्स्थापित करेंगे।