2011-12-21 9 views
6

Sto lavorando alla migrazione di un'applicazione Web a 32 bit in 64 bit e sto avendo qualche problema con il nostro codice caricatore di plugin.Determinare se una dll è una dll CLR valida leggendo il PE direttamente (problema a 64 bit)

Nella versione a 32 bit, eseguiamo la scansione della directory bin webapps per tutte le DLL .net, quindi li carica con Assembly.Load per verificare la presenza dei nostri attributi del plugin.

L'abbiamo fatto in un modo piuttosto ingegnoso utilizzando il codice di dominio pubblico:

/// <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(); 
    } 
} 

Ora il problema è che ora abbiamo di gestire 64 bit o piattaforme DLL indipendenti e l'offset sembra essere cambiato e questo codice non riesce . Qualcuno conosce le corrette modifiche al codice sopra riportato per restituire true per le DLL indipendenti solo a 64 bit o indipendenti dalla piattaforma?

+1

Davvero devi usare il codice sopra? – Hans

+1

Nifty? Più simile all'implementazione, alla creazione di presupposti e al vincolo-a-finire-nei guai ... oh aspetta, lo ha già fatto. :) – bzlm

+0

l'applicazione Web è enorme - con oltre 100 DLL nella directory bin. Molte delle DLL non sono plug-in per la nostra app e molte non sono DLL dll, quindi usare Assembly.Load non è una buona opzione. – Vaevictus

risposta

7

Il motivo per cui il codice non funziona per le DLL x64-bit è perché la dimensione dell'intestazione facoltativa dell'immagine di una DLL x64-Bit e una DLL x86-Bit è diversa. Per determinare , è necessario prendere in considerazione le diverse dimensioni di intestazione opzionale dell'immagine indipendentemente dal fatto che una determinata DLL sia o meno una DLL .Net.

La PE file format specification describes nella sezione 3.4 (intestazione opzionale) i diversi offset per passare alla directory dei dati:

  1. Per PE32 (x86) immagini l'offset è 0x60 (come è nel vostro codice) e
  2. per immagini PE32 + (x64) l'offset è 0x70.

Al fine di determinare se un dato DLL è una DLL a 64 bit devi leggere i byte magici dell'intestazione optional:

  1. Un valore di 0x20b significa PE32 +,
  2. un valore di 0x10b PE32.

ho esteso il tuo esempio:

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(); 
} 

In Windows SDK ci sono anche strutture definite per l'intestazioni facoltative PE32/PE32 +. Una descrizione di queste strutture può essere trovata qui MSDN.

Spero che questo aiuti.

+0

+1 per fare le gambe. –

+0

sembra funzionare perfettamente, grazie mille. – Vaevictus

2

C'è un motivo per cui non è possibile utilizzare i metodi nel framework? Il codice di esempio qui di seguito:

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

GetPEKind method on MSDN e PortableExecutableKinds dovrebbe iniziare. Quest'ultimo è fondamentalmente corflags

+4

Non è una soluzione, il metodo Load() bombarderà se non è un assembly .NET o se si rivolge alla versione .NET o bitness errata. –

3

Per un'alternativa che non utilizza la riflessione e non carica direttamente gli assiemi, provare lo Common Compiler Infrastructure Metadata API. Sembra che tu possa abbastanza facilmente load a PE assembly e determinare se ha un modulo 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; 
} 
Problemi correlati