2010-02-16 6 views

Respuesta

12

No puedo pensar en ninguna manera terriblemente simple de hacer lo que quiere. Usted tiene al menos un par de opciones que puedo ver:

  1. Tome la ruta dada por Mark, aunque parece un poco kludgy y puede tener algunas deficiencias.
  2. Utilice la tabla de puntero de nombre (NPT) y la tabla ordinal de exportación (EOT) para buscar los ordinales de exportación.

El principal problema que veo con la primera opción es que usted no sabe cuántos números ordinales a tratar (no puede haber lagunas en los números ordinales, por lo que contar con GetProcAddress devuelva NULL para indicar el final será no trabajo). También es algo ineficiente porque requiere hacer un lote de llamadas Win32 repetidamente y básicamente equivale a una búsqueda lineal de la tabla de direcciones de exportación. Muy poco elegante, de hecho.

Como alternativa, puede buscar el NPT y usar el índice resultante en el EOT para obtener un ordinal. Este es un enfoque más elegante porque llega al ordinal de la manera más directa posible (en realidad es el mismo método que utiliza el vinculador dinámico para resolver los nombres de exportación en sus direcciones). Además, como el NPT está ordenado léxicamente, es posible hacer una búsqueda binaria que es obviamente preferible a la búsqueda lineal del otro método. De hecho, una sola llamada a GetProcOrdinal implementada con este método debe ser ligeramente más rápida que solo una llamada al GetProcAddress. Quizás lo más importante es que este método no depende de ninguna incógnita (es decir, número de ordinales). La desventaja de este método es que no es tan simple como el otro método.

Puede usar la biblioteca de ayuda de depuración para ayudar a evitar parte del análisis de la imagen de archivo PE (esto es lo que hice inicialmente), pero resulta que analizar las partes requeridas de la imagen PE no es tan difícil . Creo que evitar la dependencia de la Biblioteca de Ayuda de Debug vale el mínimo esfuerzo adicional requerido para analizar los encabezados de imagen PE.

ponerse a trabajar, aquí está un ejemplo de implementación en 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 es donde suceden las cosas interesantes.El código es, con suerte, bastante fácil de entender; sin embargo, para comprender completamente, puede requerir un poco de conocimiento sobre el formato de archivo PE, que no voy a entrar aquí (hay mucha información en la web al respecto). FindNptProc es simplemente una función de conveniencia que realiza la búsqueda binaria del NPT. GetExportDirectoryTable es otra función de conveniencia que analiza los encabezados PE para ubicar la tabla del directorio de exportación.

El código anterior compila limpiamente para mí en Visual Studio 2008 y Windows XP (SP3), pero en YMMV. No soy realmente un chico de Windows *, por lo que esta podría no ser la más limpia en cuanto a portabilidad de código (en términos de diferentes versiones de Windows). Como de costumbre, se proporciona este código "tal cual" sin garantía de ningún tipo;)

* Si, en caso de que se esté preguntando, me hacen todavía sienten un poco sucio después de escribir todo lo que al estilo de Microsoft Windows código.

+0

¡Gracias por una gran respuesta! – Danra

+0

excelente !!!!!! – eerok512

+0

Esta es una respuesta tan sorprendente y clara. Dan, eres rockero –

4

una manera fea sería correr una llamada al sistema con un comando dumpbin y analizar la salida. Pero eso tiene la misma elegancia que un toro en la tienda de porcelana proverbial.

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

De lo contrario, podría escribir un bucle simple llamando a GetProcAddress con ordinales (pasado en los dos bytes bajos del parámetro de nombre). Cuando el puntero de función coincide con el puntero que se devuelve al pasar el nombre real, entonces está listo.

Aquí es la idea básica sin comprobación de errores:

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

Gracias. Aún no es muy elegante en mi humilde opinión. – Danra

+0

@Danra: Estoy de acuerdo en que no es terriblemente elegante. –

+6

Si quieres elegancia, eleva el nivel de abstracción. Ponga eso en una función y llámelo GetProcOrdinal. En el momento de la llamada, lucirá muy elegante. – markh44

Cuestiones relacionadas