Domanda

Qual è il modo più semplice in C ++ per ottenere un numero ordinale di una funzione DLL esportata, dato il suo nome? (Alla ricerca di un modo che non invlove l'analisi del IATS me stesso ...)

È stato utile?

Soluzione

Non riesco a pensare a un modo terribilmente semplice per fare quello che vuoi. Hai almeno un paio di opzioni che posso vedere:

  1. Prendere la strada dato da Mark, anche se sembra un po 'kludgy e può avere alcune lacune.
  2. Utilizza il nome Pointer Table (TNP) e l'esportazione ordinale Tabella (EOT) per trovare ordinali di esportazione.

Il problema principale che vedo con la prima opzione è che non si sa quanti ordinali da provare (ci possono essere lacune nei numeri ordinali, così contando su GetProcAddress ritornare NULL per segnalare la fine non funzionerà). E 'anche un po' inefficiente perché richiede fare un molto di Win32 chiama ripetutamente, per un valore sostanzialmente quella di una ricerca lineare della tabella degli indirizzi di esportazione. Abbastanza poco elegante, anzi.

In alternativa, è possibile cercare il TNP e utilizzare l'indice risulta in EOT per ottenere un numero ordinale. Questo è un approccio più elegante perché arriva al numero ordinale nel modo più diretto possibile (in realtà è lo stesso metodo il linker dinamico utilizza per risolvere i nomi di esportazione per i loro indirizzi). Inoltre, poiché il TNP è lessicalmente ordinato, è possibile fare una ricerca binaria che è ovviamente preferibile alla ricerca lineare dell'altro metodo. In effetti, una singola chiamata a GetProcOrdinal implementato con questo metodo dovrebbe essere leggermente più veloce di un semplice una chiamata a GetProcAddress. Forse più importante, questo metodo non dipende da alcuna incognite (cioè numero di ordinali). Lo svantaggio di questo metodo è che non è così semplice come l'altro metodo.

È possibile utilizzare il debug Aiuto Biblioteca per evitare di fare un po 'di analisi dell'immagine del file PE (questo è quello che ho fatto inizialmente), ma si scopre che l'analisi delle parti richieste dell'immagine del PE non è così difficile. Credo che evitando la dipendenza dal debug Aiuto Biblioteca vale il minimo sforzo supplementare richiesto per analizzare le intestazioni di immagine PE.

Come al lavoro, qui è un esempio di implementazione in C:

#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 è dove avvengono i bit interessanti. Il codice si spera abbastanza auto-esplicativo; tuttavia, per completamente capire se può richiedere un po 'di conoscenza del formato di file PE, che io non sono in procinto di entrare in qui (c'è un sacco di informazioni sul web su di esso). FindNptProc è semplicemente una funzione convenienza che fa la ricerca binaria del TNP. GetExportDirectoryTable è un'altra funzione convenienza che analizza le intestazioni PE per individuare la tabella directory di esportazione.

Il codice di cui sopra viene compilato in modo pulito per me in Visual Studio 2008 e Windows XP (SP3), ma YMMV. Io non sono davvero un ragazzo * Windows, quindi questo potrebbe non essere il codice più pulito portabilità-saggio (in termini di differenti versioni di Windows). Come al solito, questo codice viene fornito "così com'è", senza garanzia di alcun tipo;)

* Sì, nel caso in cui vi state chiedendo, I do ancora si sentono tipo di sporco dopo aver scritto tutto ciò che in stile Microsoft codice di Windows.

Altri suggerimenti

Un modo brutto sarebbe quello di eseguire una chiamata di sistema con un comando DUMPBIN e analizzare l'output. Ma che ha circa la stessa eleganza come un toro in un negozio di porcellane proverbiale.

dumpbin / export c: \ windows \ system32 \ user32.dll | grep FunctionOfInterest

In caso contrario, si potrebbe scrivere un semplice ciclo chiamando GetProcAddress con ordinali (passate nelle basse due byte del parametro name). Quando il puntatore a funzione corrisponde al puntatore restituito quando si passa il nome effettivo, poi si è fatto.

Ecco l'idea di base, senza controllo degli errori:

  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++;
     }
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top