2010-02-16 19 views
7

निर्यातित डीएलएल फ़ंक्शन का क्रमिक प्राप्त करने के लिए सी ++ में सबसे आसान तरीका क्या है, इसका नाम दिया गया है? (एक तरह से जो अपने आप को पार्स IATs invlove नहीं करता है के लिए खोज रहे ...)प्रोग्राम नाम से क्रमशः

धन्यवाद, दान

+0

तालिका को पार्स करने में क्या गलत है? यह मुश्किल नहीं है और आपको केवल एक बार फ़ंक्शन लिखना होगा। – MSN

उत्तर

12

मैं तुम क्या चाहते करने के लिए कोई बहुत आसान तरीका नहीं सोच सकते हैं:

यहाँ त्रुटि जाँच के बिना मूल विचार है। आपके पास कम से कम कुछ विकल्प हैं जो मैं देख सकता हूं:

  1. मार्क द्वारा दिए गए मार्ग को लें, हालांकि यह थोड़ा सा लगता है और कुछ कमियां हो सकती हैं।
  2. निर्यात सूचकांक खोजने के लिए नाम सूचक तालिका (एनपीटी) और निर्यात सामान्य तालिका (ईओटी) का उपयोग करें।

मुख्य समस्या यह है कि मैं पहले विकल्प के साथ देख आप कितने ऑर्डिनल्स कोशिश करने के लिए (वहाँ क्रमसूचक संख्या में अंतराल हो सकता है, ऐसा नहीं अंत का संकेत करने के लिए शून्य लौटने GetProcAddress पर भरोसा होगा पता नहीं है कि है काम)। यह कुछ हद तक अक्षम है क्योंकि इसे Win32 कॉल के लॉट को बार-बार कॉल करने की आवश्यकता होती है और यह मूल रूप से निर्यात पता तालिका की रैखिक खोज के लिए होती है। वास्तव में सुंदर, सुरुचिपूर्ण।

एक विकल्प के रूप में, आप एनपीटी खोज सकते हैं और परिणामस्वरूप इंडेक्स को ईओटी में एक ordinal प्राप्त करने के लिए उपयोग कर सकते हैं। यह एक और अधिक सुरुचिपूर्ण दृष्टिकोण है क्योंकि यह संभवतः सबसे प्रत्यक्ष तरीके से क्रमशः आता है (वास्तव में यह वही विधि है जो डायनामिक लिंकर अपने पते पर निर्यात नामों को हल करने के लिए उपयोग करता है)। इसके अलावा, क्योंकि एनपीटी को क्रमबद्ध रूप से क्रमबद्ध किया गया है, इसलिए बाइनरी खोज करना संभव है जो अन्य विधि की रैखिक खोज के लिए स्पष्ट रूप से बेहतर है। वास्तव में, इस विधि के साथ लागू GetProcOrdinal पर एक कॉल केवल एक से थोड़ा तेज़ होना चाहिए GetProcAddress पर कॉल करें। शायद अधिक महत्वपूर्ण बात यह है कि यह विधि किसी भी अज्ञात (यानी ordinals की संख्या) पर निर्भर नहीं है। इस विधि का नुकसान यह है कि यह अन्य विधि के जितना आसान नहीं है।

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

#include <stdio.h> 

#include "windows.h" 

/// Efficiently searches a module's name pointer table (NPT) for the named 
/// procedure. 
/// 
/// @param[in] npt  Address of the NPT to search. 
/// 
/// @param[in] size Number of entries in the NPT. 
/// 
/// @param[in] base Base address of the module containing the NPT. This is 
///     used to resolve addresses in the NPT (which are relative 
///     to the module's base address). 
/// 
/// @param[in] proc String containing the name of the procedure to search 
///     for. 
/// 
/// @return Returns the index into the NPT of the entry matching the named 
///   procedure. If no such matching entry exists, the function returns 
///   -1. 
/// 
DWORD FindNptProc (PDWORD npt, DWORD size, PBYTE base, LPCSTR proc) 
{ 
    INT cmp; 
    DWORD max; 
    DWORD mid; 
    DWORD min; 

    min = 0; 
    max = size - 1; 

    while (min <= max) { 
     mid = (min + max) >> 1; 
     cmp = strcmp((LPCSTR)(npt[mid] + base), proc); 
     if (cmp < 0) { 
      min = mid + 1; 
     } else if (cmp > 0) { 
      max = mid - 1; 
     } else { 
      return mid; 
     } 
    } 

    return -1; 
} 

/// Gets a pointer to a module's export directory table (EDT). 
/// 
/// @param[in] module Handle to the module (as returned by GetModuleHandle). 
/// 
/// @return Returns a pointer to the module's EDT. If there is an error (e.g. 
///   if the module handle is invalid or the module has no EDT) the 
///   function will return NULL. 
/// 
PIMAGE_EXPORT_DIRECTORY GetExportDirectoryTable (HMODULE module) 
{ 
    PBYTE     base; // base address of module 
    PIMAGE_FILE_HEADER  cfh; // COFF file header 
    PIMAGE_EXPORT_DIRECTORY edt; // export directory table (EDT) 
    DWORD     rva; // relative virtual address of EDT 
    PIMAGE_DOS_HEADER  mds; // MS-DOS stub 
    PIMAGE_OPTIONAL_HEADER oh; // so-called "optional" header 
    PDWORD     sig; // PE signature 

    // Start at the base of the module. The MS-DOS stub begins there. 
    base = (PBYTE)module; 
    mds = (PIMAGE_DOS_HEADER)module; 

    // Get the PE signature and verify it. 
    sig = (DWORD *)(base + mds->e_lfanew); 
    if (IMAGE_NT_SIGNATURE != *sig) { 
     // Bad signature -- invalid image or module handle 
     return NULL; 
    } 

    // Get the COFF file header. 
    cfh = (PIMAGE_FILE_HEADER)(sig + 1); 

    // Get the "optional" header (it's not actually optional for executables). 
    oh = (PIMAGE_OPTIONAL_HEADER)(cfh + 1); 

    // Finally, get the export directory table. 
    if (IMAGE_DIRECTORY_ENTRY_EXPORT >= oh->NumberOfRvaAndSizes) { 
     // This image doesn't have an export directory table. 
     return NULL; 
    } 
    rva = oh->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress; 
    edt = (PIMAGE_EXPORT_DIRECTORY)(base + rva); 

    return edt; 
} 

/// Gets the ordinal of an exported procedure. 
/// 
/// @param[in] module Handle (as returned by GetModuleHandle) of the module 
///      that exports the procedure. 
/// 
/// @param[in] proc  String containing the name of the procedure. 
/// 
/// @return Returns the procedure's ordinal. If an ordinal for the procedure 
///   could not be located (e.g. if the named procedure is not exported 
///   by the specified module) then the function will return -1. 
/// 
DWORD GetProcOrdinal (HMODULE module, LPCSTR proc) 
{ 
    PBYTE     base; // module base address 
    PIMAGE_EXPORT_DIRECTORY edt; // export directory table (EDT) 
    PWORD     eot; // export ordinal table (EOT) 
    DWORD     i; // index into NPT and/or EOT 
    PDWORD     npt; // name pointer table (NPT) 

    base = (PBYTE)module; 

    // Get the export directory table, from which we can find the name pointer 
    // table and export ordinal table. 
    edt = GetExportDirectoryTable(module); 

    // Get the name pointer table and search it for the named procedure. 
    npt = (DWORD *)(base + edt->AddressOfNames); 
    i = FindNptProc(npt, edt->NumberOfNames, base, proc); 
    if (-1 == i) { 
     // The procedure was not found in the module's name pointer table. 
     return -1; 
    } 

    // Get the export ordinal table. 
    eot = (WORD *)(base + edt->AddressOfNameOrdinals); 

    // Actual ordinal is ordinal from EOT plus "ordinal base" from EDT. 
    return eot[i] + edt->Base; 
} 

int main (int argc, char *argv []) 
{ 
    LPCSTR procName; 
    HMODULE module = NULL; 
    LPCSTR moduleName; 
    DWORD ordinal; 

    if (argc != 3) { 
     printf("A DLL name and procedure name must be specified\n"); 
     return EXIT_FAILURE; 
    } 

    moduleName = argv[1]; 
    procName = argv[2]; 

    if (NULL == LoadLibrary(moduleName)) { 
     printf("Could not load library %s\n", moduleName); 
     return EXIT_FAILURE; 
    } 

    module = GetModuleHandle(moduleName); 
    if (NULL == module) { 
     printf("Couldn't get a handle to %s\n", moduleName); 
     return EXIT_FAILURE; 
    } 

    ordinal = GetProcOrdinal(module, procName); 
    if (-1 == ordinal) { 
     printf("Could not find ordinal for %s in %s\n", procName, moduleName); 
    } else { 
     printf("Found %s at ordinal %d\n", procName, ordinal); 
    } 

    return EXIT_SUCCESS; 
} 

GetProcOrdinal है:

व्यापार करने के लिए नीचे हो रही है, यहाँ है सी में एक उदाहरण दिया गया।कोड उम्मीद है कि काफी आत्म-व्याख्यात्मक है; हालांकि, पूरी तरह से समझें कि इसे पीई फ़ाइल प्रारूप के बारे में थोड़ी सी जानकारी की आवश्यकता हो सकती है, जिसे मैं यहां आने वाला नहीं हूं (इसके बारे में वेब पर बहुत सारी जानकारी है)। FindNptProc बस एक सुविधा समारोह है जो एनपीटी की बाइनरी खोज करता है। GetExportDirectoryTable एक अन्य सुविधा फ़ंक्शन है जो निर्यात निर्देशिका तालिका का पता लगाने के लिए पीई हेडर को पार करता है।

उपरोक्त कोड विजुअल स्टूडियो 2008 और विंडोज एक्सपी (एसपी 3), लेकिन वाईएमएमवी के तहत मेरे लिए स्पष्ट रूप से संकलित करता है। मैं वास्तव में एक विंडोज लड़का नहीं हूं *, इसलिए यह सबसे स्वच्छ कोड पोर्टेबिलिटी-वार (विंडोज के विभिन्न संस्करणों के संदर्भ में) नहीं हो सकता है। हमेशा की तरह, इस कोड को प्रदान की जाती है किसी भी प्रकार की कोई वारंटी के साथ "जैसा है";)

* हाँ, मामले में आप सोच रहे हैं, मैं करना अभी भी तरह का गंदा लग रहा है कि सभी Microsoft शैली लेखन विंडोज के बाद कोड।

+0

एक महान उत्तर के लिए धन्यवाद! – Danra

+0

उत्कृष्ट !!!!!! – eerok512

+0

यह इतना अद्भुत और स्पष्ट उत्तर है। दान, तुम चट्टान। –

4

एक बदसूरत तरह से एक DUMPBIN कमांड के साथ एक सिस्टम कॉल चलाने के लिए और उत्पादन पार्स करने के लिए किया जाएगा। लेकिन यह कहानियों की चीन की दुकान में एक बैल के समान ही लालित्य है।

डंपबिन/निर्यात सी: \ windows \ system32 \ user32.dll | grep FunctionOfInterest

अन्यथा, आप एक साधारण लूप को GetProcAddress को ऑर्डिनल के साथ कॉल कर सकते हैं (नाम पैरामीटर के निम्न दो बाइट्स में पारित)। जब वास्तविक सूचक पारित होने पर फ़ंक्शन पॉइंटर पॉइंटर से मेल खाता है, तो आप कर चुके हैं।

HANDLE hMod; 
    HANDLE byname, byord; 
    int ord; 

    hMod = LoadLibrary("user32.dll"); 
    byname = GetProcAddress(hMod, "GetWindow"); 
    byord = 0; 
    ord = 1; 
    while (1) { 
    byord = GetProcAddress(hMod, (LPCSTR)ord); 
    if (byord == byname) { 
     printf("ord = %d\n", ord); 
     break; 
     } 
    ord++; 
    } 
+0

धन्यवाद। अभी भी बहुत ही सुरुचिपूर्ण IMHO नहीं है। – Danra

+0

@ दानारा: मैं मानता हूं कि यह बहुत ही सुरुचिपूर्ण नहीं है। –

+6

यदि आप लालित्य चाहते हैं, तो अमूर्तता का स्तर बढ़ाएं। उस सामान को एक फ़ंक्शन में रखें और इसे GetProcOrdinal पर कॉल करें। कॉल के बिंदु पर, यह वास्तव में बहुत ही सुरुचिपूर्ण लगेगा। – markh44