Skip to content
KitploitKITPLOIT
उपकरणब्लॉग
जमा करें
उपकरणब्लॉग
जमा करें

हैकिंग, पेनटेस्ट और साइबर सुरक्षा उपकरण आपके सुरक्षा शस्त्रागार के लिए!

Kitploit हैकिंग, साइबर सुरक्षा और पेंटेस्टिंग टूल्स की एक निर्देशिका है। कमजोरियों को खोजने, सिस्टम का विश्लेषण करने, परीक्षण को स्वचालित करने और अपनी सुरक्षा को मजबूत करने के लिए नवीनतम प्रोजेक्ट अपडेट खोजें।

··फ़ीड·संपर्क·गोपनीयता·© 2026 Kitploit

टूल निर्देशिका

श्रेणियाँ

सभी श्रेणियाँ देखें
Loading categories
LdrShuffle — कोड निष्पादन/इंजेक्शन तकनीक DLL PEB मॉड्यूल संरचना हेरफेर का उपयोग करते हुए | Kitploit
उपकरण/GitHubGitHub/rwxstoned/ldrshuffle
कोड विश्लेषणशोषणपार्श्व आंदोलनशेलकोडपोस्ट-शोषणपेनिट्रेशन टेस्टिंगरेड टीमिंगपेलोड डेवलपमेंटबाइनरी शोषण
GitHubrwxstoned/ldrshuffle

LdrShuffle

कोड निष्पादन/इंजेक्शन तकनीक DLL PEB मॉड्यूल संरचना हेरफेर का उपयोग करते हुए

289441 साल पहलेKitploit द्वारा समीक्षित

सबसे लोकप्रिय

सभी देखें →

हमारे समुदाय द्वारा सबसे अधिक उपयोग किए जाने वाले उपकरण खोजें।

सभी उपकरण खोजें

हमारे उपकरणों का संग्रह ब्राउज़ करें

सभी उपकरण देखें →
साझा करें
रिपॉजिटरी देखें

LdrShuffle

लोड किए गए मॉड्यूल के EntryPoint को रनटाइम पर संशोधित करके गुप्त कोड निष्पादन।

सारांश

Windows प्रक्रियाओं में रनटाइम पर विभिन्न मॉड्यूल लोड होते हैं। इनमें से प्रत्येक मॉड्यूल में एक DllMain() फ़ंक्शन परिभाषित होता है, जो प्रक्रिया या थ्रेड निर्माण/विनाश (चार संभावित परिदृश्य) पर आह्वान किया जाएगा।

प्रक्रिया के जीवनकाल के दौरान उन फ़ंक्शन को उचित रूप से कॉल करने के लिए, Windows लोडर फ़ंक्शन (ntdll!Ldrp*) प्रत्येक मॉड्यूल के लिए प्रमुख पैरामीटर ( EntryPoint फ़ील्ड सहित) वाली एंट्रीज़ की एक सूची का उल्लेख करेंगे।

एक DLL के लिए इस EntryPoint को अधिलेखित करके, हम सुनिश्चित करते हैं कि कोड निष्पादन हमारी पसंद के स्थान पर पुनर्निर्देशित हो जाएगा।

उपयोग के मामले

इसका उपयोग कोड निष्पादन प्रिमिटिव के रूप में और API प्रॉक्सीइंग के लिए किया जा सकता है, यानी कुछ API को गैर-संदिग्ध कॉलस्टैक के साथ चलाने के लिए क्योंकि उन्हें वैध Windows फ़ंक्शन द्वारा आह्वान किया जाएगा।

इसका उपयोग रिमोट प्रक्रिया में निष्पादन ट्रिगर करने के लिए भी किया जा सकता है, बशर्ते हमलावर के पास इस लक्ष्य प्रक्रिया पर मेमोरी पढ़ने और लिखने की क्षमता हो। Threadless Injection के समान, यह बिना निष्पादन से संबंधित क्लासिक API (CreateRemoteThread, QueueUserAPC) को आह्वान किए प्रक्रिया में कोड निष्पादित करने की क्षमता प्रदान करता है।

चुनौतियाँ

Windows प्रक्रिया के अंदर मॉड्यूल का लोड/अनलोड करना एक जटिल विषय है जो कई चुनौतियाँ, अस्थिरता, रेस कंडीशन और क्रैश की संभावना प्रस्तुत करता है। उदाहरण के लिए, DllMain() फ़ंक्शन के भाग के रूप में कोड चलाने से संबंधित एक प्रसिद्ध बाधा इस तथ्य में निहित है कि Loader Lock सक्रिय है और हम एक ऐसे थ्रेड में चल रहे हैं जो पूरी तरह से सेटअप नहीं हुआ है, या जो समाप्त होने की प्रक्रिया में है।

इसलिए, मैंने उचित रूप से दस्तावेज़ करने का प्रयास किया है कि क्या संभव है और क्या नहीं। उदाहरण के लिए, जबकि अधिकांश सामान्य API कॉल किए जा सकते हैं, एक पूर्ण बीकन चलाने के लिए अलग प्रक्रिया में होने की कुछ आवश्यकताएँ होती हैं, ताकि wininet.dll या winhttp.dll में उपयोग किए जाने वाले फ़ंक्शन के कारण होने वाले डेडलॉक से बचा जा सके।

कार्यान्वयन

Windows में DLL लोडिंग पर पुनरावृत्ति

प्रत्येक प्रक्रिया रनटाइम पर _LDR_DATA_TABLE_ENTRY संरचनाओं की एक सूची बनाए रखती है। ये संरचनाएँ DLL से संबंधित कई विवरण जैसे इसका EntryPoint (जिसे हम अधिलेखित करेंगे), इसका नाम, कुछ हैश, टाइमस्टैम्प, विभिन्न फ्लैग आदि रखती हैं। इनमें से कुछ संरचनाएँ दस्तावेज़ीकृत हैं और कुछ नहीं।

इन्हें इस WinDbg कमांड के माध्यम से देखा जा सकता है:

dt nt!_LDR_DATA_TABLE_ENTRY 0xdeadbeef

root@kitploit:~
0:006> dt nt!_LDR_DATA_TABLE_ENTRY 0x18d1c4838c0
ntdll!_LDR_DATA_TABLE_ENTRY
   +0x000 InLoadOrderLinks : _LIST_ENTRY [ 0x0000018d`1c485de0 - 0x0000018d`1c4832b0 ]
   +0x010 InMemoryOrderLinks : _LIST_ENTRY [ 0x0000018d`1c485df0 - 0x0000018d`1c4832c0 ]
   +0x020 InInitializationOrderLinks : _LIST_ENTRY [ 0x0000018d`1c4832d0 - 0x0000018d`1c482c80 ]
   +0x030 DllBase          : 0x00007ffe`e87e0000 Void
   +0x038 EntryPoint       : 0x00007ffe`e8838d00 Void
   +0x040 SizeOfImage      : 0x2fe000
   +0x048 FullDllName      : _UNICODE_STRING "C:\WINDOWS\System32\KERNELBASE.dll"
   +0x058 BaseDllName      : _UNICODE_STRING "KERNELBASE.dll"
   +0x068 FlagGroup        : [4]  "???"
   +0x068 Flags            : 0x8a2cc
   +0x068 PackagedBinary   : 0y0
   +0x068 MarkedForRemoval : 0y0
   +0x068 ImageDll         : 0y1
(...)
   +0x0e0 MappingInfoIndexNode : _RTL_BALANCED_NODE
   +0x0f8 OriginalBase     : 0x00007ffe`e87e0000
   +0x100 LoadTime         : _LARGE_INTEGER 0x01db5f86`2fa735fc
   +0x108 BaseNameHashValue : 0x235bec4
   +0x10c LoadReason       : 0 ( LoadReasonStaticDependency )
   +0x110 ImplicitPathOptions : 0x4000
   +0x114 ReferenceCount   : 1
   +0x118 DependentLoadFlags : 0x800
   +0x11c SigningLevel     : 0 ''

संरचना का पता प्रक्रिया PEB में संदर्भित एक दोगुनी-लिंक संरचना (PEB_LDR_DATA संरचना में) को चलकर प्राप्त किया जा सकता है।

dt nt!_PEB_LDR_DATA 0xb4b4c3c3

DontCallForThreads फ्लैग पर ध्यान दें। जैसा कि नाम इंगित करता है, यदि वह फ्लैग सेट है, तो OS उस मॉड्यूल के DllMain() को थ्रेड इवेंट (जैसे DLL_THREAD_ATTACH या DLL_THREAD_DETACH) के लिए कॉल नहीं करेगा।

DLL बनाते समय, OS लोडर फ़ंक्शन के साथ हाथ से काम करने के लिए निम्नलिखित टेम्पलेट का पालन करना होगा:

root@kitploit:~
BOOL WINAPI DllMain(
    HINSTANCE hinstDLL,  // handle to DLL module
    DWORD fdwReason,     // reason for calling function
    LPVOID lpvReserved )  // reserved
{
    // Perform actions based on the reason for calling.
    switch( fdwReason ) 
    { 
        case DLL_PROCESS_ATTACH:
         // Initialize once for each new process.
         // Return FALSE to fail DLL load.
            break;

        case DLL_THREAD_ATTACH:
         // Do thread-specific initialization.
            break;

        case DLL_THREAD_DETACH:
         // Do thread-specific cleanup.
            break;

        case DLL_PROCESS_DETACH:
        
            if (lpvReserved != nullptr)
            {
                break; // do not do cleanup if process termination scenario
            }
            
         // Perform any necessary cleanup.
            break;
    }
    return TRUE;  // Successful DLL_PROCESS_ATTACH.
}

कार्यान्वयन पर तकनीकी विवरण

API कॉल सेट करना

जैसा कि ऊपर बताया गया है, यह तकनीक निष्पादन को पुनर्निर्देशित करने के लिए अस्थायी रूप से एक DLL के EntryPoint को अधिलेखित करती है। चूंकि हमारे पास निष्पादन के पुनर्निर्देशन से अधिक किसी चीज़ पर नियंत्रण नहीं है, इसलिए हमें अपनी ओर से कुछ व्यवस्थाएँ करनी होंगी कि हम क्या चलाना चाहते हैं, किस तर्क के साथ, और रिटर्न वैल्यू कैसे वापस प्राप्त करें।

यह हीप पर एक DATA_T संरचना परिभाषित करके किया जाता है, इस तरह से कि यह विभिन्न चरणों के दौरान सुलभ रहे।

वह संरचना इस प्रकार परिभाषित की गई है:

root@kitploit:~
typedef struct _DATA_T {
    // LDR structures manipulation
    ULONG_PTR   runner;             // malicious entry point to execute
    ULONG_PTR   bakOriginalBase;    // backup of overwritten OriginalBase
    ULONG_PTR   bakEntryPoint;      // backup of overwritten EntryPoint
    HANDLE      event;              // event signalling that the Runner has executed
    // function call
    ULONG_PTR   ret;                // return value
    DWORD       createThread;       // run this API call in a new thread (required for wininet/winhttp)
    ULONG_PTR   function;           // Windows API to call
    DWORD       dwArgs;             // number of args
    ULONG_PTR   args[MAX_ARGS];     // array of args
} DATA_T, * PDATA_T;

API निष्पादन सेट करने के लिए, इन फ़ील्ड को तैयार करना होगा। ret वैल्यू वह है जो निष्पादन के बाद रिटर्न वैल्यू एकत्र करेगी। event का उपयोग सिंक्रोनाइज़ेशन के लिए किया जाता है, यह संकेत देने के लिए कि निष्पादन पूरा हो गया है। अन्य सभी फ़ील्ड इनपुट हैं जो यह परिभाषित करते हैं कि किस API को कॉल करना है (function), किस तर्क के साथ (dwArgs और args[]), Runner() फ़ंक्शन का पता जहाँ निष्पादन पुनर्निर्देशित होता है, और अधिलेखित मूल DLL प्रविष्टियों का बैकअप (bakOriginalBase और bakEntryPoint)।

createThread फ़ील्ड को उन जटिल API फ़ंक्शन के लिए 1 पर सेट करने की आवश्यकता है जो DllMain() सेटअप में अच्छी तरह से नहीं चलेंगे (इसमें कई wininet और winhttp लाइब्रेरी शामिल हैं)।

यहाँ PoC में दिखाई देने वाले MessageBoxA() के लिए कॉल सेट करने का एक उदाहरण है:

root@kitploit:~
 pDataT->dwArgs = 4;
 pDataT->runner = (ULONG_PTR)Runner;
 pDataT->function = (ULONG_PTR)MessageBoxA;
 pDataT->args[0] = (ULONG_PTR)0;
 pDataT->args[1] = (ULONG_PTR)"Hello";
 pDataT->args[2] = (ULONG_PTR)"LDRSHUFFLE";
 pDataT->args[3] = (ULONG_PTR)MB_OKCANCEL;
 pDataT->event = CreateEventA(NULL, FALSE, FALSE, "ExecEvt");

_LDR_DATA_TABLE_ENTRY को संशोधित करना

UpdateLdr() फ़ंक्शन लक्ष्य मॉड्यूल के _LDR_DATA_TABLE_ENTRY में सही संशोधन करने के लिए जिम्मेदार है।

RestoreLdr() बाद के चरण में उन परिवर्तनों को पुनर्स्थापित करेगा (Runner() द्वारा आह्वान)।

ये फ़ंक्शन मूल रूप से PEB का पता लगाते हैं और सही DLL और उसके फ़ील्ड की पहचान करने के लिए मॉड्यूल संरचनाओं को चलते हैं। हेडर फ़ाइलों में, मैं Batsec द्वारा अपने DarkLoadLibrary में उपयोग की गई परिभाषाओं का पुन: उपयोग कर रहा हूँ और मैं पाठकों को इस प्रोजेक्ट और संबंधित MDSec ब्लॉगपोस्ट को देखने के लिए प्रोत्साहित करूँगा ताकि Windows में मॉड्यूल लोडिंग के आंतरिक भागों पर उनके द्वारा किए गए उत्कृष्ट कार्य से लाभ उठाया जा सके।

नोट: यह PoC एक बलि DLL (SACRIFICIAL_DLL_NAME) लोड करता है और इस DLL पर उन परिवर्तनों को करता है। हालाँकि, पहले से लोड किए गए DLL को संशोधित करना पूरी तरह से संभव है। वास्तव में, यह क्रॉस-प्रोसेस इंजेक्शन के लिए अपनाया गया दृष्टिकोण है। स्थिरता उद्देश्यों के लिए, मैं ntdll या kernel32 जैसे महत्वपूर्ण DLL को छूने से बचने की सलाह दूँगा, जो सुरक्षा समाधानों द्वारा अधिक जाँच-पड़ताल किए जाते हैं।

निष्पादन

थ्रेड निर्माण या विनाश पर, निष्पादन Runner() को पुनर्निर्देशित किया जाता है, जो मॉड्यूल के लिए नकली DllMain() के रूप में कार्य करता है। यह फ़ंक्शन तब:

  • हीप पर वह स्थान ढूँढता है जहाँ कॉल चलाने/रिटर्न वैल्यू प्राप्त करने के लिए उपयोग की जाने वाली डेटा संरचना (PDATA_T संरचना) स्थित है
  • PEB को RestoreLdr() के माध्यम से उसकी मूल स्थिति में पुनर्स्थापित करता है
  • सामान्य DllMain() कॉल करता है (मूलतः सामान्य DLL कॉल को प्रॉक्सी करना)।

इस बिंदु पर, "सामान्य" OS निष्पादन किया जा चुका है। फिर यह हमारे पेलोड के साथ जारी रहता है:

  • DATA_T संरचना पर संग्रहीत मानों और तर्कों के अनुसार हमारी दुर्भावनापूर्ण API कॉल निष्पादित करता है। यदि इस API को एक नए थ्रेड में चलाने के लिए चिह्नित किया गया है (createThread = 1), तो यह कॉल एक नए थ्रेड में किया जाएगा।
  • अंत में, एक इवेंट (pDataT->event) को संकेत देता है ताकि हमारा मुख्य कोड जान सके कि कॉल किया जा चुका है।

जब Windows हमारे नकली EntryPoint (जो Runner() फ़ंक्शन पता है) को आह्वान करता है, तो कॉलस्टैक इस प्रकार दिखता है:

Callstack on MessageBoxA()

API प्रॉक्सीइंग उदाहरण

प्रदान किया गया PoC MessageBoxA() को आह्वान करने वाला एक उदाहरण शामिल करता है।

इसमें wininet का उपयोग करके HTTP डाउनलोड का एक प्रदर्शन भी शामिल है। उस कोड को सक्षम करने के लिए HTTP चर परिभाषित करें।

Callstack on MessageBoxA()

क्रॉस-प्रोसेस इंजेक्शन उदाहरण

ऊपर उल्लिखित सिद्धांत प्रक्रिया मेमोरी स्पेस में पढ़ने और लिखने तक सीमित हो जाते हैं, ताकि भविष्य में किसी मनमाने समय पर कोड निष्पादन हो सके।

कुछ ट्वीक्स के साथ, इन रीड और राइट ऑपरेशंस को एक रिमोट प्रक्रिया पर लागू किया जा सकता है ताकि उसके एक DLL के EntryPoint को अधिलेखित किया जा सके।

एक पूर्व-आवश्यकता प्रक्रिया मेमोरी स्पेस में पढ़ने और लिखने की क्षमता है, अर्थात:

OpenProcess(PROCESS_VM_READ, FALSE, dwPid) and OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, PID)

PoC में एक अतिरिक्त प्रोजेक्ट मौजूद है, जिसे LdrInject कहा जाता है, जो इन चरणों को करने का तरीका प्रदर्शित करता है। संक्षेप में, यह निम्नलिखित करता है:

  • ReadPEB() में, यह लक्ष्य प्रक्रिया में _LDR_DATA_TABLE_ENTRY सूची को चलता है ताकि अधिलेखित करने के लिए उपयुक्त DLL की पहचान की जा सके। ध्यान दें कि इस DLL का DontCallForThreads == 0 होना चाहिए क्योंकि हम चाहते हैं कि Windows थ्रेड निर्माण पर उस DLL के EntryPoint को आह्वान करे। हम सूची के पहले DLL भी नहीं चुन रहे हैं क्योंकि वे सुरक्षा उत्पादों द्वारा अधिक जाँच-पड़ताल किए जाते हैं (ntdll.dll, kernel32.dll...).

  • उस DLL के विवरण एक PEBINJ_DATA डेटा संरचना में संग्रहीत किए जाते हैं।

  • शेलकोड (इस मामले में एक बीकन) को InjectShellcodeToRemoteProcess() के साथ रिमोट प्रोसेसस्पेस में लिखा जाता है।

  • दो WriteProcessMemory() कॉल DLL के EntryPoint को अधिलेखित करते हैं और इसे OriginalBase में बैकअप करते हैं ताकि बाद में इसे पुनर्स्थापित किया जा सके।

उस बिंदु पर, अगला DLL_THREAD_ATTACH या DLL_THREAD_DETACH इवेंट शेलकोड के आह्वान का परिणाम देगा। बीकन चलाने के संदर्भ में यह कुछ सीमाओं और चेतावनियों के साथ आता है, जिनका विवरण अगले भाग में दिया गया है।

Cobalt Strike बीकन

यह तकनीक एक बहुत ही विशिष्ट स्थिति में शेलकोड के निष्पादन का परिणाम देती है। Loader Lock सक्रिय है (क्योंकि OS मानता है कि वह DLL लोड/अनलोड करने की प्रक्रिया में है); एक थ्रेड या तो बनाया जा रहा है या नष्ट किया जा रहा है; और सामान्यतया, थ्रेड सिंक्रोनाइज़ेशन समस्याएँ, डेडलॉक आदि की संभावना है।

परीक्षण के दौरान, दो चुनौतियाँ देखी गई हैं:

  1. एक सामान्य Cobalt Strike बीकन चलाने पर wininet.dll या winhttp.dll में एपीआई का उपयोग करते समय डेडलॉक होगा।

  2. थ्रेड विनाश पर चलने से स्थिरता संबंधी समस्याएँ होती हैं क्योंकि हम एक ऐसे थ्रेड में चल रहे हैं जो नष्ट होने की प्रक्रिया में है।

स्थिरता बढ़ाने के लिए, हमें:

  • सुनिश्चित करें कि बीकन एक नए थ्रेड में चलेगा। इसलिए, UDRL सामान्य Cobalt Strike रिफ्लेक्टिव DLL एंट्री पॉइंट को आह्वान करने से पहले CreateThread करेगा।

  • केवल बनाए जा रहे थ्रेड में चलाएँ, न कि मर रहे थ्रेड में। ऐसा करने के लिए, हम यह सुनिश्चित करते हैं कि जब OS द्वारा EntryPoint कॉल किया जाता है, तो आह्वान कारण fdwReason == DLL_THREAD_ATTACH हो:

winApi.CreateThread(NULL, 4096, (LPTHREAD_START_ROUTINE)&runner, (LPVOID)&ct_data, 0, &dwThId);

इसके बजाय सामान्य

((DLLMAIN)entryPoint)((HINSTANCE)loaderStart, 4, NULL);

root@kitploit:~
    ULONG_PTR __cdecl ReflectiveLoader(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
        // only run for a Thread creation event
        
        if (fdwReason != DLL_THREAD_ATTACH) {
            return TRUE;
        }

        ...
    }

ये दो अतिरिक्त कदम UDRL के डेमो में शामिल किए गए हैं।

परीक्षण

LdrShuffle के लिए परीक्षण किए गए API की सूची

  • VirtualAlloc
  • VirtualProtect
  • CreateThread
  • Sleep
  • MessageBoxA
  • InternetOpenW (createThread = 1 के साथ चलाने की आवश्यकता है)
  • InternetOpenUrlA (createThread = 1 के साथ चलाने की आवश्यकता है)

कार्य सूची

  • LdrShuffle के लिए और अधिक API का परीक्षण जारी रखें
टूल डाउनलोड करें