
एक PoC परियोजना जो शेलकोड को Hint/Name Table में एम्बेड करने के लिए है।
HintInject एक शेलकोड एम्बेडर और लोडर है जिसे मैंने PE फ़ाइल और Import Directory Table संरचनाओं के साथ खेलते हुए विकसित किया है। यह एक रॉ शेलकोड फ़ाइल लेता है और शेलकोड को टुकड़ों में Hint/Name Table प्रविष्टियों में डालता है जो लोडर निष्पादन योग्य पर एक नकली आयातित DLL प्रविष्टि की Import Lookup Table के माध्यम से पहुंच योग्य हैं। लोडर फिर शेलकोड को निष्पादित करने के लिए इन टुकड़ों को मर्ज करता है। मैं नहीं जानता कि यह एक नई तकनीक है या नहीं लेकिन मैं इसे मज़े के लिए साझा करना चाहता था।
HintInject कैसे काम करता है यह समझाने से पहले, मैं Stack Overflow पर एक पोस्ट का संदर्भ देते हुए DLL लोडिंग प्रक्रिया का संक्षेप में उल्लेख करना चाहता हूं।
आवश्यक DLL और फ़ंक्शन/इम्पोर्ट की जानकारी प्राप्त करने के लिए, पहले Import Directory Table को देखना चाहिए। Import Directory Table प्रविष्टियों की एक तालिका है, प्रत्येक आयातित DLL के लिए एक प्रविष्टि। ये प्रविष्टियाँ आयातित DLL नाम का एक पॉइंटर, Import Lookup Table का एक पॉइंटर, Import Address Table का एक पॉइंटर और विभिन्न जानकारी के लिए अन्य फ़ील्ड रखती हैं।
सीधे शब्दों में, Import Lookup Table इम्पोर्ट पर जानकारी की ओर ले जाती है, और Import Address Table इम्पोर्ट के पतों की ओर ले जाती है। हालांकि, जब निष्पादन योग्य डिस्क पर होता है, या DLL लोडिंग प्रक्रिया से ठीक पहले, Import Address Table Import Lookup Table के समान होती है। DLL लोडिंग प्रक्रिया के दौरान Import Address Table की सामग्री को इम्पोर्ट के पतों से अधिलेखित कर दिया जाता है।
कुछ विस्तार में जाए बिना, हम इन तीन तालिकाओं के बीच संबंध देखने के लिए नीचे दिए गए आरेख को देख सकते हैं।
यदि हम थोड़ा और विस्तार में जाते हैं, तो Import Lookup Table सीधे इम्पोर्ट के नाम नहीं रखती है। नाम से आयातित फ़ंक्शन के लिए, यह Hint/Name Table प्रविष्टियों के RVA रखती है। ये प्रविष्टियाँ फ़ंक्शन के नामों को Null-टर्मिनेटेड ASCII स्ट्रिंग के रूप में संग्रहीत करती हैं। इसलिए, Hint/Name Table प्रविष्टि की संरचना परिभाषा इस प्रकार है:
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint;
CHAR Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
यहां Hint फ़ील्ड वास्तव में DLL की export name pointer तालिका में एक इंडेक्स है। इसका उपयोग उस आयात की स्थिति खोजने में तेजी लाने के लिए किया जाता है। इसलिए, हम Import Lookup Table और Hint/Name तालिका के बीच संबंध को नीचे दिए गए आरेख के साथ सारांशित कर सकते हैं।
निष्कर्ष में, एक DLL और उसके आयात को खोजने के लिए, इन चरणों का पालन करना चाहिए:
हाल ही में, x86matthew द्वारा साझा की गई ImportDLLInjection तकनीक की जांच करते समय, मैंने देखा कि मेमोरी में लोड किए गए DLL के Import Directory में एक नकली प्रविष्टि कैसे जोड़ी जाए। उसके बाद, मैं एक छोटा प्रोजेक्ट विकसित करना चाहता था जो सीधे डिस्क पर एक बाइनरी की import तालिका में एक नकली प्रविष्टि जोड़ता है, मज़े के लिए और अपने ज्ञान को ताज़ा करने के लिए। इस प्रोजेक्ट को विकसित करने के लिए DLL लोडिंग प्रक्रिया की समीक्षा करते समय, इस प्रक्रिया में उपयोग की जाने वाली Hint/Name Table प्रविष्टियों में Hint फ़ील्ड ने मेरा ध्यान आकर्षित किया।
MSDN दस्तावेज़ के अनुसार, मैंने सीखा कि यह फ़ील्ड विंडोज लोडर द्वारा उस आयात का पता खोजने के लिए उपयोग किया जाता है, जो नाम से आयात किया गया है, सीधे उस DLL की export name तालिका से जिसमें वह है। हालांकि, उसी दस्तावेज़ में, यह कहा गया था कि यदि इस फ़ील्ड का उपयोग करके फ़ंक्शन नहीं पाया जा सकता है, तो उस फ़ंक्शन को DLL की export name तालिका में बाइनरी सर्च ऑपरेशन के माध्यम से खोजा जाएगा। इस वाक्य के आधार पर, मैंने सोचा कि इस फ़ील्ड के लिए गलत मान डालने से DLL लोडिंग प्रक्रिया बाधित नहीं होगी।
जैसा कि मैंने ऊपर उल्लेख किया है, आयात किए जाने वाले प्रत्येक फ़ंक्शन के अनुरूप Hint/Name तालिका में एक प्रविष्टि होती है। दूसरे शब्दों में, हमारे पास प्रत्येक आयात के लिए उपयोग करने के लिए 2 बाइट होते हैं। कई आयातों को मिलाकर, दुर्भावनापूर्ण शेलकोड को संग्रहीत करने के लिए पर्याप्त Hint फ़ील्ड प्राप्त किए जा सकते हैं। परिणामस्वरूप, कोई व्यक्ति लोडर बाइनरी का उपयोग करके नकली आयात DLL प्रविष्टि के Hint/Name प्रविष्टियों में शेलकोड को एम्बेड कर सकता है। वह लोडर बाइनरी रनटाइम के दौरान निष्पादित होने वाले शेलकोड को मर्ज करने के लिए इन प्रविष्टियों तक पहुंच सकता है।
HintInject का उपयोग ऐसा लोडर बनाने के लिए किया जा सकता है जो अपनी Hint/Name तालिका में शेलकोड रखता है। यह पहले .rrdata नामक एक नया सेक्शन बनाता है और वर्तमान import निर्देशिका को इस सेक्शन में कॉपी करता है। उसके बाद, यह एक नई नकली प्रविष्टि जोड़ता है जिसके आयात इनपुट शेलकोड को रखने के लिए उपयोग किए जाएंगे। सेक्शन के शेष बाइट का उपयोग Import Lookup Table, Import Address Table, DLL नाम, और नई नकली प्रविष्टि की Hint/Name तालिका को संग्रहीत करने के लिए किया जाता है। अंतिम चरण के रूप में, HintInject इनपुट शेलकोड के टुकड़े रखने के लिए आयातों के Hint फ़ील्ड का उपयोग करता है।
लोडर बाइनरी के उदाहरण आयात:
HintInject.exe चलाएं ताकि शेलकोड लोडर बनाया जा सके: HintInject.exe <Shellcode File> <Output Name>Loader.exe <PID>HintInjectLoader/Main.cpp फ़ाइल में InjectShellcode फ़ंक्शन को संशोधित करके शेलकोड इंजेक्शन विधि बदल सकते हैं।Windows 10 19044 और Visual Studio 2019 पर परीक्षण किया गया:
DllNamesForFakeImports.h फ़ाइल में dllNames सरणी में अन्य dll जोड़ सकते हैं।DllNamesForFakeImports.h फ़ाइल में dllNames सरणी को भी संपादित करना चाहिए। डिफ़ॉल्ट रूप से, HintInject सरणी क्रम के अनुसार dlss से फ़ंक्शन चुनेगा। दूसरे शब्दों में, उपकरण पहले user32.dll से निर्यातित फ़ंक्शन चुनता है, यदि शेलकोड आकार 2 * user32.dll निर्यात की संख्या से बड़ा है, तो यह advapi32.dll और अन्य के निर्यात का भी उपयोग करना शुरू कर देता है।static LPCSTR dllNames[] = {"user32.dll","advapi32.dll","gdi32.dll","wininet.dll","comctl32.dll","shell32.dll","wsock32.dll","oleaut32.dll","ws2_32.dll","urlmon.dll"};
केवल अधिकृत सुरक्षा परीक्षण के लिए। स्पष्ट अनुमति के बिना सिस्टम के खिलाफ इस उपकरण का दुरुपयोग अवैध है।