
ExportHider: DLL फ़ाइल से एक्सपोर्ट किए गए फ़ंक्शन्स को छिपाने के लिए रनटाइम के दौरान एक्सपोर्ट टेबल जनरेट करना।
ExportHider एक C++ DLL टेम्पलेट उत्पन्न करता है जिसमें एक कोड स्टब होता है जो आपको फ़ाइलसिस्टम पर DLL के निर्यात निर्देशिका (Export Directory) से निर्यात किए गए फ़ंक्शन (Exported Functions) को छिपाने की अनुमति देता है। फ़ंक्शन परिभाषाओं को डालने और फ़ाइल को संकलित करने के बाद, आप PE फ़ाइल व्यूअर्स (जैसे CFF Explorer) के माध्यम से छिपे हुए निर्यात फ़ंक्शन को नहीं देख पाएंगे। हालांकि, चूंकि टेम्पलेट में कोड स्टब रनटाइम के दौरान निर्यात निर्देशिका को पुनः बनाता है, वैध GetProcAddress कॉल सफलतापूर्वक निष्पादित होंगे। यह विधि केवल डायनामिक DLL लोडिंग या कस्टम DLL लोडर मामलों के लिए काम करती है।
सामान्यतः, जब आप DLL फ़ाइलों (C या C++ में) में एक निर्यातित फ़ंक्शन परिभाषित करना चाहते हैं, तो आप फ़ंक्शन नाम से पहले __declspec(dllexport) कीवर्ड डालते हैं, या एक .def फ़ाइल बनाते हैं। संकलन के बाद, कंपाइलर एक विशिष्ट तालिका बनाता है जिसे Export Directory कहा जाता है, जो निर्यातित फ़ंक्शन से संबंधित जानकारी रखती है। Export Directory की संरचना नीचे देखी जा सकती है:
जब कोई प्रक्रिया DLL फ़ाइल से किसी फ़ंक्शन का उपयोग करना चाहती है, तो Windows Loader इस संरचना को पार्स करता है और AddressOfFunctions, AddressOfNames, AddressOfNameOrdinals ऐरे का उपयोग करके अनुरोधित फ़ंक्शन को आयात करता है।
फ़ंक्शन आयात करने की विशिष्ट प्रक्रिया को ferreirasc's blog post में विस्तार से समझाया गया है, लेकिन संक्षेप में, नाम द्वारा आयातित फ़ंक्शन के लिए, Loader AddressOfNames ऐरे (इस ऐरे के मान केवल RVA मान हैं) को पुनरावृत्त करता है और दिए गए नाम की खोज करता है। एक बार जब Loader को "i" स्थिति में मिलान मिल जाता है, तो यह AddressOfNameOrdinals ऐरे के ith इंडेक्स को संदर्भित करता है और इस फ़ंक्शन से जुड़ा ऑर्डिनल प्राप्त करता है। ऑर्डिनल प्राप्त करने के बाद, Loader आयातित फ़ंक्शन से जुड़ा RVA प्राप्त करने के लिए ऑर्डिनल मान स्थिति पर AddressOfFunctions को संदर्भित करेगा।
यहाँ महत्वपूर्ण बिंदु यह है कि Windows Loader द्वारा ये सभी खोज और पहुँच संचालन, जब LoadLibrary कॉल किया जाता है, DLL को प्रक्रिया एड्रेस स्पेस में मैप करने के बाद किए जाते हैं। DLL मैपिंग के दौरान, संपूर्ण DLL फ़ाइल, इसके PE हेडर सहित, मेमोरी में लिखी जाती है, और Loader Export Directory तक पहुँचने के लिए मेमोरी में हेडर को पार्स करता है। इसका मतलब है कि यदि DLL स्वयं प्रक्रिया से जुड़ने के बाद Export Directory Address (सीधे DataDirectory के 0वें इंडेक्स) को बदलने के लिए मेमोरी में अपने PE हेडर को ओवरराइट करने में सक्षम है, तो Loader कंपाइलर द्वारा जोड़ी गई Export Directory के बजाय एक मनमानी Export Directory में फ़ंक्शन आयात करने के लिए देख सकता है।
__ _ _ _
/__\_ ___ __ ___ _ __| |_ /\ /(_) __| | ___ _ __
/_\ \ \/ / '_ \ / _ \| '__| __|/ /_/ / |/ _` |/ _ \ '__|
//__ > <| |_) | (_) | | | |_/ __ /| | (_| | __/ |
\__/ /_/\_\ .__/ \___/|_| \__\/ /_/ |_|\__,_|\___|_|
|_|
by @R0h1rr1m
Usage of C:\Users\Public\DLLDemo\ExportHider.exe:
-h | --help Show the help message.
-i | --input <Input Path> Input path for the list of function names to be hidden. (Mandatory)
-o | --output <Output Path> Output path for the DLL template. (Mandatory)
-n | --name <DLL Name> Name of the DLL for the Export Directory. (Mandatory)
-c | --count <Number of Other Functions> Number of other exported functions that won't be hidden.
-i | --input <Input Path> पैरामीटर के संबंध में, आपको इनपुट फ़ाइल का पथ निर्दिष्ट करना होगा जो छिपाए जाने वाले फ़ंक्शन नामों को लाइन दर लाइन संग्रहीत करता है। एक उदाहरण इनपुट फ़ाइल की सामग्री होगी:
TestFunction1
TestFunction2
TestFunction3
-c | --count पैरामीटर के संबंध में, यदि आप अपने सभी निर्यातित फ़ंक्शन को छिपाना नहीं चाहते हैं (अर्थात कुछ फ़ंक्शन __declspec(dllexport) या .def फ़ाइल के साथ निर्यात किए गए हैं, और वे PE फ़ाइल व्यूअर के माध्यम से फ़ाइलसिस्टम पर DLL फ़ाइल की Export Directory में दिखाई देते हैं), तो बस इस पैरामीटर का उपयोग करके निर्दिष्ट करें कि उनमें से कितने हैं क्योंकि उपकरण को मेमोरी गणना करते समय इस जानकारी की आवश्यकता होती है।
यदि आप तकनीक के साथ खेलना चाहते हैं, तो परियोजना के विकास के दौरान मुझे दो दिलचस्प बिंदु मिले। प्रोजेक्ट को बदलने से पहले आपको उन्हें जानने की आवश्यकता हो सकती है:
GetProcAddress फ़ंक्शन को नाम के साथ कॉल करते हैं तो Windows Loader एक Binary Search-जैसे एल्गोरिदम का उपयोग करता है। इस वजह से, सभी निर्यातित फ़ंक्शन के नाम, जिनमें छिपे हुए भी शामिल हैं, को AddressOfNames ऐरे में क्रमबद्ध करने की आवश्यकता है। अन्यथा, GetProcAddress फ़ंक्शन NULL लौटाता है। इसलिए, मैंने इस ऐरे के सदस्यों को सॉर्ट करने के लिए Bubble Sort एल्गोरिदम का उपयोग किया।AddressOfNames ऐरे, AddressOfFunctions ऐरे, DataDirectory ऐरे और कुछ अन्य फ़ील्ड को relative Virtual Addresses (RVAs) में मानों की आवश्यकता होती है। इसके अलावा, वे इन RVA मानों को DWORD-आकार के फ़ील्ड में रखते हैं। जब आप डायनामिक मेमोरी आवंटन फ़ंक्शन जैसे VirtualAlloc या HeapAlloc का उपयोग करके एक नई मनमानी निर्यात निर्देशिका के लिए मेमोरी क्षेत्र आवंटित करते हैं, तो दिए गए पते DLL मैप किए गए क्षेत्र से दूर होंगे, और RVA मान DWORD-आकार के फ़ील्ड में फ़िट नहीं होते हैं, और आपको पूर्णांक ओवरफ्लो का सामना करना पड़ता है। इसलिए मैंने मेमोरी आवश्यकताओं के लिए DLL टेम्पलेट में बाइट ऐरे प्रकार के साथ ग्लोबल वेरिएबल का उपयोग किया।इस प्रोजेक्ट को बनाने का मेरा पहला लक्ष्य एक DLL उत्पन्न करना था जिसमें लापता निर्यात (या निर्यात तालिका की पूर्ण कमी) हो, लेकिन फिर भी नव निर्मित प्रक्रिया द्वारा सफलतापूर्वक लोड किया जाता है। मैंने सोचा कि यह व्यवहार DLL साइडलोडिंग पेलोड के लिए एक नया खेल का मैदान ला सकता है। हालांकि, मैं कोई फ़ंक्शन या कोई ऐसा तरीका खोज नहीं सका जिससे कि निर्यात निर्देशिका फिक्सर स्टब Windows Loader द्वारा निर्यातित DLL फ़ंक्शन की जाँच करने से पहले चले।
अधिक तकनीकी रूप से, मैंने NTDLL में Windows Loader से संबंधित कोड अनुभागों में प्रत्येक DLL के लिए निम्नलिखित प्रवाह और फ़ंक्शन कॉल देखा (मेरे पुराने नोट्स से एकत्रित; इसमें कुछ गलतियाँ हो सकती हैं क्योंकि मैं एक विशेषज्ञ रिवर्स इंजीनियर नहीं हूँ):
1. LdrpMapDll - यह वह जगह है जहाँ DLL को प्रक्रिया एड्रेस स्पेस में मैप किया जाता है। सभी DLL जो DLL नाम की शर्त को पूरा करते हैं,
सीधे मेमोरी में डाल दिए जाते हैं, फ़ाइलसिस्टम संस्करण के लिए कोई प्रीचेक नियंत्रण नहीं।
2. LdrpSnapModule - यह वह जगह है जहाँ Windows Loader आयात हल करना शुरू करता है। प्रत्येक आयात विवरणक के लिए, यह PE
संरचना को पार्स करता है, निर्यात तालिका की जाँच करता है, आयातित फ़ंक्शन की बाइनरी खोज करता है, उसके RVA की गणना करता है,
और इस फ़ंक्शन के दौरान अपना पता संबंधित कॉलर की प्रक्रिया के आयात पता तालिका प्रविष्टि पर लिखता है।
3. LdrpDoPostSnapWork - यदि प्रत्येक आयातित फ़ंक्शन के लिए चरण 2 सफल होता है, तो इस फ़ंक्शन में मेमोरी सुरक्षा, TLS आरंभीकरण, CFG सक्षमीकरण
किए जाते हैं।
4. LdrpInitializeNode - यदि चरण 3 सफल होता है, तो इस चरण में मॉड्यूल लिंकिंग फ़ंक्शन हैं।
5. LdrpCallTlsInitializers - यह वह जगह है जहाँ DllMain फ़ंक्शन से पहले TLS कॉलबैक कॉल किए जाते हैं।
6. LdrpCallInitRoutine - यह वह जगह है जहाँ आयातित DLL के लिए पहली बार DLLMain को कॉल किया जाता है। मूल
समाधान में, यह फ़ंक्शन निर्यात तालिका को ठीक करने के लिए बहुत देर हो चुकी है।
जब आप एक निष्पादन योग्य चलाते हैं जो एक DLL आयात करता है, यदि Windows Loader DLL की निर्यात तालिका में आवश्यक फ़ंक्शन नाम नहीं ढूँढ पाता है, तो यह निष्पादन को रोक देता है, और यह LdrpSnapModule के बाद निष्पादित होने वाले फ़ंक्शन को निष्पादित नहीं करता है।
DLL साइडलोडिंग मामले के लिए, हम कॉलर प्रक्रिया को संशोधित नहीं कर सकते; इस प्रकार, गतिशील रूप से निर्यात तालिका को ठीक करने का एकमात्र मौका LdrpMapDll और LdrpSnapModule फ़ंक्शन के बीच कोड निष्पादन का अवसर खोजना है क्योंकि Loader LdrpSnapModule फ़ंक्शन जाँच के दौरान तुरंत रुक जाता है। मैंने TLS कॉलबैक, दूसरे DLL लोड, फ़ॉरवर्ड किए गए निर्यात और कुछ अन्य वर्कअराउंड का प्रयास किया, लेकिन उनमें से किसी ने भी मुझे ऐसी जगह खोजने में मदद नहीं की, इसलिए दुर्भाग्य से, यह विधि DLL साइडलोडिंग या स्टैटिकली आयातित DLL के लिए सीधे काम नहीं करती है। यदि आप इस समस्या का कोई समाधान या व्यवहार्य वर्कअराउंड खोजते हैं, तो मुझे इसका और अन्वेषण करने में बहुत खुशी होगी — चाहे वह विचार पर चर्चा करना, एक साथ दृष्टिकोण के बारे में सोचना, या इसे लागू करना हो। इस दिशा में किसी भी योगदान की बहुत सराहना की जाएगी।
DLL साइडलोडिंग मामलों के लिए इस विधि का उपयोग करने का एक संभावित तरीका पूरे काम को दो DLL में विभाजित करना है, अर्थात प्रॉक्सी DLL और पेलोड DLL। प्रॉक्सी DLL वह नाम लेता है जिसकी EXE को उम्मीद है, और इसमें दृश्यमान निर्यात होते हैं जो लोडर की स्थिर आयात जाँच को संतुष्ट करते हैं, जबकि पेलोड DLL में वास्तविक छिपी कार्यक्षमता होती है, इसमें लापता निर्यात होते हैं, और DllMain में अपनी निर्यात तालिका का पुनर्निर्माण करता है। मुझे नहीं लगता कि यह इस समस्या का एक अच्छा वर्कअराउंड है, इसलिए मैंने इसे लागू नहीं किया।
केवल अधिकृत सुरक्षा परीक्षण के लिए। स्पष्ट अनुमति के बिना सिस्टम के विरुद्ध इस उपकरण का दुरुपयोग अवैध है।