2016-03-04 7 views
5

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

यदि यह मदद करता है, तो स्मृति क्षेत्र तय किए जाते हैं और संकलन समय पर ज्ञात होते हैं। साथ ही, मुझे पता है कि एएसएलआर को उन्नत मिटिगेशन एक्सपीरियंस टूलकिट का उपयोग करके रजिस्ट्री या प्रति-प्रक्रिया में सिस्टम-व्यापी अक्षम किया जा सकता है, लेकिन मैं अपने उपयोगकर्ताओं को ऐसा करने की आवश्यकता नहीं चाहता हूं।

+0

आप संकलन समय पर स्मृति को "आरक्षित" नहीं कर सकते हैं और फिर यह उपलब्ध होने की उम्मीद कर सकते हैं। यदि आप स्मृति चाहते हैं, तो आपको गतिशील रूप से आवंटित करना होगा। –

+0

जीसीसी का उपयोग करना संभव लगता है, शायद यह मदद कर सकता है: http://stackoverflow.com/questions/22457446/gcc-linker-description-file-force-symbol-to-be-at-specific-address – SHR

+0

तो आप चाहते हैं अपने प्रोग्राम लोड से पहले मेमोरी क्षेत्र आरक्षित करें? भले ही यह नहीं चल रहा है? ऐसे मेमोरी क्षेत्र (आईपीसी) का उपयोग क्या होगा? – dvhh

उत्तर

2

मुझे लगता है कि अंततः मुझे टिप्पणियों में सुझाए गए डीएक्सआईवी के समान तरीके से एक विधि का उपयोग कर मिला। एक डमी डीएलएल का उपयोग करने के बजाय, मैं एक मूल निष्पादन योग्य बनाता हूं जो /FIXED और /BASE कंपाइलर झंडे का उपयोग करके मेरे आरक्षित क्षेत्र की शुरुआत में लोड होता है। निष्पादन के लिए कोड एक गैर-आरंभिकृत सरणी सुनिश्चित करता है कि छवि स्मृति में की जरूरत पतों को शामिल किया गया है, लेकिन नहीं फ़ाइल में किसी भी अतिरिक्त स्थान ले करता है:

unsigned char Reserved[4194304]; // 4MB 

रनटाइम पर, एक नया करने के लिए निष्पादन योग्य प्रतियां ही मेमोरी में स्थान और प्रक्रिया पर्यावरण ब्लॉक में कुछ फ़ील्ड अपडेट करने के लिए इसे इंगित करता है। फ़ील्ड अपडेट किए बिना, FormatMessage जैसे कुछ फ़ंक्शंस को कॉल करना एक क्रैश का कारण बनता है।

#include <intrin.h> 
#include <windows.h> 
#include <winternl.h> 

#pragma intrinsic(__movsb) 

void Relocate() { 
    void *Base, *NewBase; 
    ULONG SizeOfImage; 
    PEB *Peb; 
    LIST_ENTRY *ModuleList, *NextEntry; 

    /* Get info about the PE image. */ 
    Base = GetModuleHandleW(NULL); 
    SizeOfImage = ((IMAGE_NT_HEADERS *)(((ULONG_PTR)Base) + 
     ((IMAGE_DOS_HEADER *)Base)->e_lfanew))->OptionalHeader.SizeOfImage; 

    /* Allocate memory to hold a copy of the PE image. */ 
    NewBase = VirtualAlloc(NULL, SizeOfImage, MEM_COMMIT, PAGE_READWRITE); 
    if (!NewBase) { 
     ExitProcess(GetLastError()); 
    } 

    /* Copy the PE image to the new location using __movsb since we don't have 
     a C library. */ 
    __movsb(NewBase, Base, SizeOfImage); 

    /* Locate the Process Environment Block. */ 
    Peb = (PEB *)__readfsdword(0x30); 

    /* Update the ImageBaseAddress field of the PEB. */ 
    *((PVOID *)((ULONG_PTR)Peb + 0x08)) = NewBase; 

    /* Update the base address in the PEB's loader data table. */ 
    ModuleList = &Peb->Ldr->InMemoryOrderModuleList; 
    NextEntry = ModuleList->Flink; 
    while (NextEntry != ModuleList) { 
     LDR_DATA_TABLE_ENTRY *LdrEntry = CONTAINING_RECORD(
      NextEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks); 
     if (LdrEntry->DllBase == Base) { 
      LdrEntry->DllBase = NewBase; 
      break; 
     } 
     NextEntry = NextEntry->Flink; 
    } 
} 

मैं /NODEFAULTLIB के साथ निष्पादन योग्य बनाया बस इसके आकार और रनटाइम पर लोड DLLs की संख्या है, इसलिए __movsb आंतरिक का प्रयोग कम करें। यदि आप __movsbmemcpy के साथ __movsb को प्रतिस्थापित करना चाहते हैं तो आप शायद एमएसवीसीआरटी से जुड़ने के साथ दूर हो सकते हैं। आप ntdll.dll से memcpy आयात भी कर सकते हैं या अपना खुद का लिख ​​सकते हैं।

एक बार निष्पादन योग्य तरीके से बाहर हो जाने के बाद, मैं एक डीएलएल में एक फ़ंक्शन को कॉल करता हूं जिसमें मेरा शेष कोड होता है। मूल पीई छवि से छुटकारा पाने के लिए डीएलएल UnmapViewOfFile का उपयोग करता है, जो मुझे काम करने के लिए स्मृति का एक अच्छा 4 एमबी + हिस्सा देता है, गारंटीकृत फ़ाइलों, धागे के ढेर या ढेर को शामिल करने की गारंटी नहीं देता है।

कुछ बातें इस तकनीक के साथ ध्यान में रखना:

  1. यह एक बहुत बड़ा हैक है। मुझे गंदा लेखन हुआ और यह विंडोज के भविष्य के संस्करणों में बहुत अच्छी तरह से गिर सकता है। मैंने विंडोज 7 के अलावा किसी भी अन्य चीज़ पर इसका परीक्षण नहीं किया है। यह कोड कम से कम विंडोज 7 और विंडोज 10 पर काम करता है।
  2. चूंकि निष्पादन योग्य /FIXED /BASE के साथ बनाया गया है, इसलिए इसका कोड स्थिति-स्वतंत्र नहीं है और आप केवल स्थानांतरित निष्पादन योग्य पर नहीं जा सकते हैं।
  3. यदि DL12 फ़ंक्शन जो UnmapViewOfFile रिटर्न देता है, तो प्रोग्राम क्रैश हो जाएगा क्योंकि कोड कोड जिसे हम से बुलाया गया है अब मौजूद नहीं है। यह सुनिश्चित करने के लिए कि समारोह कभी वापस नहीं आता है, मैं ExitProcess का उपयोग करता हूं।
  4. कुछ भौतिक स्मृति को मुक्त करने के लिए VirtualFree का उपयोग करके स्थानांतरित पीई छवि में कुछ अनुभागों को रिलीज़ किया जा सकता है।
  5. मेरा कोड लोडर डेटा तालिका प्रविष्टियों को फिर से क्रमबद्ध करने से परेशान नहीं करता है। ऐसा लगता है कि इस तरह से ठीक काम करता है, लेकिन अगर छवि पता द्वारा आदेशित प्रविष्टियों पर कुछ निर्भर होना है तो यह तोड़ सकता है।
  6. कुछ एंटी-वायरस प्रोग्राम इस सामान के बारे में संदिग्ध हो सकते हैं। माइक्रोसॉफ्ट सुरक्षा अनिवार्यता कम से कम शिकायत नहीं की थी।
  7. हिंडसाइट में, dxiv की डमी डीएलएल विधि आसान हो सकती है, क्योंकि मुझे पीईबी के साथ गड़बड़ करने की आवश्यकता नहीं होगी। लेकिन मैं इस तकनीक से फंस गया क्योंकि निष्पादन योग्य अपने इच्छित आधार पते पर लोड होने की अधिक संभावना है। डमी डीएलएल विधि मेरे लिए काम नहीं करती थी। डीएलएल को एनटीएल द्वारा लोड किया जाता है जब विंडोज़ पहले ही मेमोरी के क्षेत्र आरक्षित कर चुका है।
+1

प्रभावशाली, लेकिन वास्तव में नाजुक दिखता है। तकनीकी उपलब्धि के लिए +1, लेकिन नकली डीएलएल विधि अधिक भरोसेमंद दिखती है (क्योंकि यह उन सुविधाओं पर निर्भर करता है जो लोडर स्पष्ट रूप से समर्थन करते हैं)। –

+0

मैं मानता हूं, यह शायद नाजुक है। यह हमेशा विंडोज पीईबी के माध्यम से चल रहा है और छवि के मूल आधार पते को कैशिंग नहीं करता है। मुझे लगता है कि मैं नकली डीएलएल विधि को एक शॉट देने जा रहा हूं, लेकिन फिर भी यह एक मजेदार प्रयोग था। –

+0

रिकॉर्ड के लिए, निष्पादन योग्य को अनमैप करने के लिए 'UnmapViewOfFile' का उपयोग भी असमर्थित है। (वास्तव में मुझे लगता है कि यह विंडोज 10 में असफल हो सकता है? मेरे पास इस बारे में पिछले प्रश्न की अस्पष्ट स्मृति है।) –

संबंधित मुद्दे

 संबंधित मुद्दे