2011-12-21 6 views
6

Estoy trabajando para migrar una aplicación web de 32 bits a 64 bits y tengo algunos problemas con nuestro código de cargador de complementos.Determinar si un dll es un dll CLR .valido leyendo directamente el PE (problema de 64 bits)

En la versión de 32 bits, escaneamos el directorio bin de webapps para todos los .net dlls, luego los cargamos con Assembly.Load para verificar la presencia de nuestros atributos de plugin.

Hicimos esto de una manera bastante ingeniosa utilizando el código de dominio público:

/// <summary> 
/// Returns true if the file specified is a real CLR type, 
/// otherwise false is returned. 
/// False is also returned in the case of an exception being caught 
/// </summary> 
/// <param name="file">A string representing the file to check for 
/// CLR validity</param> 
/// <returns>True if the file specified is a real CLR type, 
/// otherwise false is returned. 
/// False is also returned in the case of an exception being 
/// caught</returns> 
public static bool IsDotNetAssembly(String file) 
{ 
    Stream fs = new FileStream(@file, FileMode.Open, FileAccess.Read); 

    try 
    { 
     BinaryReader reader = new BinaryReader(fs); 
     //PE Header starts @ 0x3C (60). Its a 4 byte header. 
     fs.Position = 0x3C; 
     uint peHeader = reader.ReadUInt32(); 
     //Moving to PE Header start location... 
     fs.Position = peHeader; 
     uint peHeaderSignature = reader.ReadUInt32(); 
     ushort machine = reader.ReadUInt16(); 
     ushort sections = reader.ReadUInt16(); 
     uint timestamp = reader.ReadUInt32(); 
     uint pSymbolTable = reader.ReadUInt32(); 
     uint noOfSymbol = reader.ReadUInt32(); 
     ushort optionalHeaderSize = reader.ReadUInt16(); 
     ushort characteristics = reader.ReadUInt16(); 

     // PE Optional Headers 
     // To go directly to the datadictionary, we'll increase the stream's current position to with 96 (0x60). 
     // 28 bytes for Standard fields 
     // 68 bytes for NT-specific fields 
     // 128 bytes DataDictionary 
     // DataDictionay has 16 directories 
     // 8 bytes per directory (4 bytes RVA and 4 bytes of Size.) 
     // 15th directory consist of CLR header! (if its 0, it is not a CLR file) 

     uint[] dataDictionaryRVA = new uint[16]; 
     uint[] dataDictionarySize = new uint[16];    
     ushort dataDictionaryStart = Convert.ToUInt16(Convert.ToUInt16(fs.Position) + 0x60); 

     fs.Position = dataDictionaryStart; 
     for (int i = 0; i < 15; i++) 
     { 
      dataDictionaryRVA[i] = reader.ReadUInt32(); 
      dataDictionarySize[i] = reader.ReadUInt32(); 
     } 
     if (dataDictionaryRVA[14] == 0) 
     { 
      fs.Close(); 
      return false; 
     } 
     else 
     { 
      fs.Close(); 
      return true; 
     } 
    } 
    catch (Exception) 
    { 
     return false; 
    } 
    finally 
    { 
     fs.Close(); 
    } 
} 

Ahora el problema es que ahora tenemos que manejar 64 bits o plataforma DLL independientes y el desplazamiento parece haber cambiado y no logra este código . ¿Alguien sabe las modificaciones correctas en el código anterior para que sea verdadero solo para 64 bits válidos O dlls independientes de plataforma?

+1

¿De verdad tienes que usar el código de arriba? – Hans

+1

Nifty? Más como implementación específica, creación de suposiciones y obligado a meterse en problemas ... oh, sí, ya lo hizo. :) – bzlm

+0

la aplicación web es enorme, con más de 100 dlls en el directorio bin. Muchos de los dlls no son complementos para nuestra aplicación y muchos no son clr dlls, por lo que usar Assembly.Load no es una buena opción. – Vaevictus

Respuesta

7

La razón por la que su código no está funcionando para DLL de 64 bits se porque el tamaño del encabezado opcional de imagen de una DLL de 64 bits y una DLL de 86-bits es diferente. Debe tener en cuenta los diferentes tamaños de encabezado opcionales de la imagen para determinar si una DLL determinada es una DLL .Net.

El PE file format specification describes en la sección 3.4 (opcional Header) las diferentes compensaciones para saltar a los directorios de datos:

  1. Para PE32 (x86) imágenes el desplazamiento es 0x60 (como lo es en su código) y
  2. para imágenes PE32 + (x64) el desplazamiento es 0x70.

el fin de determinar si es o no un determinado DLL es una DLL de 64 bits usted tiene que leer los bytes mágicos de la cabecera opcional:

  1. Un valor de 0x20b significa PE32 +,
  2. un valor de 0x10b PE32.

He ampliado su ejemplo:

Stream fs = new FileStream(@file, FileMode.Open, FileAccess.Read); 

try 
{ 
    BinaryReader reader = new BinaryReader(fs); 
    //PE Header starts @ 0x3C (60). Its a 4 byte header. 
    fs.Position = 0x3C; 
    uint peHeader = reader.ReadUInt32(); 
    //Moving to PE Header start location... 
    fs.Position = peHeader; 
    uint peHeaderSignature = reader.ReadUInt32(); 
    ushort machine = reader.ReadUInt16(); 
    ushort sections = reader.ReadUInt16(); 
    uint timestamp = reader.ReadUInt32(); 
    uint pSymbolTable = reader.ReadUInt32(); 
    uint noOfSymbol = reader.ReadUInt32(); 
    ushort optionalHeaderSize = reader.ReadUInt16(); 
    ushort characteristics = reader.ReadUInt16(); 

    long posEndOfHeader = fs.Position; 
    ushort magic = reader.ReadUInt16(); 

    int off = 0x60; // Offset to data directories for 32Bit PE images 
        // See section 3.4 of the PE format specification. 
    if (magic == 0x20b) //0x20b == PE32+ (64Bit), 0x10b == PE32 (32Bit) 
    { 
    off = 0x70; // Offset to data directories for 64Bit PE images 
    } 
    fs.Position = posEndOfHeader;  

    uint[] dataDictionaryRVA = new uint[16]; 
    uint[] dataDictionarySize = new uint[16]; 
    ushort dataDictionaryStart = Convert.ToUInt16(Convert.ToUInt16(fs.Position) + off); 

    fs.Position = dataDictionaryStart; 

    for (int i = 0; i < 15; i++) 
    { 
    dataDictionaryRVA[i] = reader.ReadUInt32(); 
    dataDictionarySize[i] = reader.ReadUInt32(); 
    } 
    if (dataDictionaryRVA[14] == 0) 
    { 
    fs.Close(); 
    return false; 
    } 
    else 
    { 
    fs.Close(); 
    return true; 
    } 
} 
catch (Exception) 
{ 
    return false; 
} 
finally 
{ 
    fs.Close(); 
} 

En el SDK de Windows también hay estructuras definidas para la encabezados opcionales PE32/PE32 +. Puede encontrar una descripción de esas estructuras aquí MSDN.

Espero, esto ayuda.

+0

+1 para hacer el trabajo de piernas. –

+0

esto parece estar funcionando perfectamente, muchas gracias. – Vaevictus

2

¿Hay algún motivo por el que no pueda usar métodos en el marco? código de ejemplo siguiente:

 var assembly = Assembly.Load("path to assembly"); 
     ImageFileMachine machine; 
     PortableExecutableKinds peKind; 
     assembly.ManifestModule.GetPEKind(out peKind, out machine); 

GetPEKind method on MSDN y PortableExecutableKinds debería empezar. Este último es básicamente corflags

+4

No es una solución, el método Load() bombardeará si no es un ensamblado .NET o si se dirige a la versión o bitness de .NET incorrecto. –

3

Para una alternativa que no utiliza reflexión y no carga ensamblajes directamente, pruebe Common Compiler Infrastructure Metadata API. Parece que puede fácilmente load a PE assembly y determinar si tiene un módulo CLR.

MetadataReaderHost host = new PeReader.DefaultHost(); 
var module = host.LoadUnitFrom(args[0]) as IModule; 
if (module == null) 
{ 
    Console.WriteLine(args[0]+" is not a PE file containing a CLR module or assembly."); 
    return; 
} 
Cuestiones relacionadas