2009-12-04 8 views
14

का पता कैसे प्राप्त करें मैं x86 से x64 तक किसी एप्लिकेशन को पोर्ट करने की प्रक्रिया में हूं। मैं विजुअल स्टूडियो 200 का उपयोग कर रहा हूं; अधिकांश कोड सी ++ है और कुछ भाग सादे सी हैं। x64 की ओर संकलन करते समय __asm ​​कीवर्ड समर्थित नहीं है और हमारे एप्लिकेशन में इनलाइन असेंबलर के कुछ भाग शामिल हैं। मैं इस कोड तो मैं नहीं जानता कि वास्तव में क्या एट करने के लिए माना जाता है नहीं लिखा था:बेस स्टैक पॉइंटर

int CallStackSize() { 
    DWORD Frame; 
    PDWORD pFrame; 
    __asm 
     { 
      mov EAX, EBP 
      mov Frame, EAX 
     } 
    pFrame = (PDWORD)Frame; 
    /*... do stuff with pFrame here*/ 
} 

ईबीपी वर्तमान समारोह के ढेर को आधार सूचक है। क्या इनलाइन एएसएम का उपयोग किए बिना स्टैक पॉइंटर प्राप्त करने का कोई तरीका है? मैं उन इंट्रिनिक्स को देख रहा हूं जो माइक्रोसॉफ्ट इनलाइन एएसएम के लिए एक विकल्प के रूप में प्रदान करता है लेकिन मुझे कुछ भी नहीं मिला जो मुझे कुछ उपयोगी बना देता है। कोई विचार?

एंड्रियास ने पूछा कि पीएफआरएएम के साथ क्या सामान किया जाता है। यहां पूरा कार्य है:

int CallStackSize(DWORD frameEBP = 0) 
{ 
    DWORD pc; 
    int tmpint = 0; 
    DWORD Frame; 
    PDWORD pFrame, pPrevFrame; 

    if(!frameEBP) // No frame supplied. Use current. 
    { 
     __asm 
     { 
      mov EAX, EBP 
      mov Frame, EAX 
     } 
    } 
    else Frame = frameEBP; 

    pFrame = (PDWORD)Frame; 
    do 
    { 
     pc = pFrame[1]; 
     pPrevFrame = pFrame; 
     pFrame = (PDWORD)pFrame[0]; // precede to next higher frame on stack 

     if ((DWORD)pFrame & 3) // Frame pointer must be aligned on a DWORD boundary. Bail if not so. 
     break; 

     if (pFrame <= pPrevFrame) 
     break; 

     // Can two DWORDs be read from the supposed frame address? 
     if(IsBadWritePtr(pFrame, sizeof(PVOID)*2)) 
     break; 

     tmpint++; 
    } while (true); 
    return tmpint; 
} 

परिवर्तनीय पीसी का उपयोग नहीं किया जाता है। ऐसा लगता है कि यह फ़ंक्शन स्टैक तक चलता है जब तक कि यह विफल न हो जाए। यह मानता है कि यह अनुप्रयोगों के ढेर के बाहर नहीं पढ़ सकता है, इसलिए जब यह विफल हो जाता है तो यह कॉल स्टैक की गहराई को मापता है। इस कोड को _EVERY_SINGLE कंपाइलर पर संकलन करने की आवश्यकता नहीं है। बस वीएस 200 9। एप्लिकेशन को EVERY_SINGLE कंप्यूटर पर चलाने की आवश्यकता नहीं है। हमारे पास तैनाती का पूरा नियंत्रण है क्योंकि हम इसे स्वयं स्थापित/कॉन्फ़िगर करते हैं और पूरी चीजें अपने ग्राहकों को देते हैं।

+0

क्या सामान pFrame साथ किया जाता है:

CodeProject एक लेख जो बताते हैं कि यह कैसे उपयोग करने के लिए है? –

उत्तर

10

वास्तव में करने के लिए सही काम करना होगा कि यह फ़ंक्शन जो कुछ भी करता है, उसे वास्तविक फ्रेम पॉइंटर तक पहुंच की आवश्यकता नहीं है। यह निश्चित रूप से बुरा व्यवहार है।

लेकिन, आप क्या देख रहे तुम क्या करने में सक्षम होना चाहिए के लिए करना है:

int CallStackSize() { 
    __int64 Frame = 0; /* MUST be the very first thing in the function */ 
    PDWORD pFrame; 

    Frame++; /* make sure that Frame doesn't get optimized out */ 

    pFrame = (PDWORD)(&Frame); 
    /*... do stuff with pFrame here*/ 
} 

कारण यह काम करता है सी में आमतौर पर पहली बात एक समारोह करता है के आधार के स्थान से दूर बचाने है कि स्थानीय चर आवंटित करने से पहले सूचक (ईबीपी)। एक स्थानीय चर (फ्रेम) बनाकर और फिर पता का पता प्राप्त करके, हम वास्तव में इस फ़ंक्शन के ढेर फ्रेम की शुरुआत का पता प्राप्त कर रहे हैं।

नोट: कुछ अनुकूलन "फ़्रेम" चर को हटा सकते हैं। शायद नहीं, लेकिन सावधान रहें।

दूसरा नोट: आपका मूल कोड और यह कोड "पीएफआरएएम" द्वारा इंगित डेटा को मैनिपुलेट करता है जब "पीएफआरएम" स्वयं ढेर पर होता है। यहां दुर्घटना से पीएफआरएएम को ओवरराइट करना संभव है और फिर आपके पास एक खराब सूचक होगा, और कुछ अजीब व्यवहार प्राप्त हो सकता है। X86 से x64 तक जाने पर विशेष रूप से ध्यान रखें, क्योंकि पीएफआरएम अब 4 की बजाय 8 बाइट्स है, इसलिए यदि आपका पुराना "पीएफआरएएम के साथ सामान करता है" कोड स्मृति के साथ गड़बड़ करने से पहले फ्रेम और पीएफआरएम के आकार के लिए लेखांकन कर रहा था, तो आप नए, बड़े आकार के लिए खाते की आवश्यकता है।

+6

मुझे नहीं लगता कि यदि आप अपना पता लेते हैं तो चर हटा दिया जा सकता है। हालांकि, कंपाइलर वैरिएबल को पुनर्व्यवस्थित करने के लिए स्वतंत्र है हालांकि इसे पसंद है। तकनीकी रूप से कोई भाषा-स्तर गारंटी फ्रेम ढेर पर भी स्थित नहीं है (लेकिन व्यवहार में मैं उम्मीद करता हूं कि यह ठीक काम करे)। –

+0

मेरा विचार बिल्कुल, मैं एक समान उत्तर सबमिट करने के अपने रास्ते पर था लेकिन मैंने खुद को रोक दिया क्योंकि यह बहुत नाजुक लगता है। –

+3

क्या होगा यदि आप इसे 'अस्थिर' बनाते हैं? –

0

यदि आपको सटीक "बेस पॉइंटर" की आवश्यकता है तो इनलाइन असेंबली जाने का एकमात्र तरीका है।

यह आश्चर्यजनक रूप से संभव है कि कोड लिखना संभव है जो अपेक्षाकृत छोटे प्लेटफॉर्म-विशिष्ट कोड के साथ स्टैक को घुमाता है, लेकिन असेंबली से बचना मुश्किल है (आप जो कर रहे हैं उसके आधार पर)।

यदि आप जो कुछ करने की कोशिश कर रहे हैं वह ढेर से बहने से बचने के लिए है, तो आप बस किसी भी स्थानीय चर का पता ले सकते हैं।

+0

ऐसा प्रतीत होता है कि आपको स्टैक पॉइंटर का निरीक्षण करने वाले दिनचर्या को बनाने के लिए एक समर्पित असेंबलर (एमएल 64) का उपयोग करना होगा, पिछले स्टैक फ्रेम के पते को पता चलता है (स्टैक पॉइंटर में आंदोलन के लिए खाते को अपने असेंबलर रूटीन को कॉल करते समय) और इसे वापस कर देता है (मुझे नहीं पता कि आप यह कैसे करेंगे)। फिर इसे अपने सी प्रोग्राम में लिंक करें। –

4

वर्तमान फ्रेम सूचक में कोई स्थान निर्धारित करने के लिए आप _AddressOfReturnAddress() आंतरिक का उपयोग कर सकते हैं, मानते हैं कि इसे पूरी तरह से अनुकूलित नहीं किया गया है। मुझे लगता है कि संकलक उस फ़ंक्शन को फ़्रेम पॉइंटर को ऑप्टिमाइज़ करने से रोक देगा यदि आप स्पष्ट रूप से इसका उल्लेख करते हैं।या, यदि आप केवल एक थ्रेड का उपयोग करते हैं, तो आप मुख्य थ्रेड के स्टैक आकार को निर्धारित करने के लिए IMAGE_NT_HEADER.OptionalHeader.SizeOfStackReserve और IMAGE_NT_HEADER.OptionalHeader.SizeOfStackCommit का उपयोग कर सकते हैं। वर्तमान छवि के लिए IMAGE_NT_HEADER तक पहुंचने के लिए this देखें।

मैं भी IsBadWritePtr का उपयोग करके स्टैक के अंत का निर्धारण करने के खिलाफ की सिफारिश करेंगे। कम से कम आप रिजर्व को तब तक बढ़ने का कारण बनेंगे जब तक कि आप रिजर्व हिट नहीं करते, क्योंकि आप एक गार्ड पेज पर जाते हैं। यदि आप वास्तव में स्टैक के वर्तमान आकार को ढूंढना चाहते हैं, तो आप जिस पते को चेक कर रहे हैं उसके साथ VirtualQuery का उपयोग करें।

और अगर मूल उपयोग ढेर चलने के लिए है, तो आप उस के लिए StackWalk64 उपयोग कर सकते हैं।

+0

यह नहीं देख रहा है कि कितनी जगह शेष है, यह पिछले स्टैक फ्रेम की श्रृंखला का बैक अप ले रहा है। मूल रूप से एक बैकट्रैक कर रहे हैं। – caf

+0

यदि ऐसा है तो उसके लिए एक एपीआई है: StackWalk64। – MSN

+0

मेरा सुझाव है कि आप इसे एक नए उत्तर के रूप में जोड़ें, ऐसा लगता है जैसे ओपी की जरूरत है। – caf

0
.code 

PUBLIC getStackFrameADDR _getStackFrameADDR 
getStackFrameADDR: 
    mov RAX, RBP 
    ret 0 

END 

ऐसा कुछ आपके लिए काम कर सकता है।

इसे ml64 या jwasm के साथ संकलित करें और इसे अपने कोड बाहरी "सी" शून्य getstackFrameADDR (शून्य) में इसका उपयोग करके कॉल करें;

+1

x86-64 कोड में आमतौर पर डिफ़ॉल्ट रूप से फ्रेम पॉइंटर नहीं होता है। जीसीसी और एमएसवीसी दोनों एक के बिना संकलित। –

0

कोई गारंटी नहीं कि RBP (ईबीपी के 64 के बराबर) वास्तव में callstack में वर्तमान फ्रेम करने के लिए एक सूचक है नहीं है। मुझे लगता है कि माइक्रोसॉफ्ट ने फैसला किया कि कई नए सामान्य प्रयोजन रजिस्टर के बावजूद, उन्हें किसी और को मुक्त करने की आवश्यकता है, इसलिए आरबीपी केवल उन कार्यों में फ्रेमपोइंटर के रूप में उपयोग किया जाता है जो एलोका(), और कुछ अन्य मामलों में कॉल करते हैं। तो अगर इनलाइन असेंबली का समर्थन किया गया था, तो यह जाने का रास्ता नहीं होगा।

तुम सिर्फ पश्व-अनुरेखन करना चाहते हैं, तो आप dbghelp.dll में StackWalk64 उपयोग करने के लिए की जरूरत है। यह dbghelp.dll में है जिसे XP के साथ भेज दिया गया है, और प्री-एक्सपी में 64-बिट समर्थन नहीं था, इसलिए आपको अपने एप्लिकेशन के साथ डीएलएल शिप करने की आवश्यकता नहीं है।

अपने 32-बिट संस्करण के लिए, बस अपने वर्तमान विधि का उपयोग करें। डीबीएचएचएलपी के लिए आयात लाइब्रेरी की तुलना में आपकी खुद की विधियां छोटी होंगी, जो कि स्मृति में वास्तविक डीएल बहुत कम है, इसलिए यह एक निश्चित अनुकूलन है (व्यक्तिगत अनुभव: मैंने एक ग्लिब-स्टाइल बैकट्रैस और बैकट्रैक_सिम्बोल्स को x86 के लिए एक से कम में लागू किया है। dbghelp आयात पुस्तकालय का आकार दसवां)।

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

शायद कुछ दिन मैं x64 को गंभीरता से लक्षित करने का फैसला करूंगा, और स्टैकवॉक 64 का उपयोग करके एक सस्ता तरीका समझ सकता हूं जिसे मैं साझा कर सकता हूं, लेकिन चूंकि मैं अभी भी अपनी सभी परियोजनाओं के लिए x86 को लक्षित कर रहा हूं, इसलिए मुझे परेशान नहीं है।

1

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

http://www.codeproject.com/KB/threads/StackWalker.aspx

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