Mejor manera de comprobar si un archivo DLL es un ensamblado CLR en C #
-
21-09-2019 - |
Pregunta
¿Cuál es la mejor manera de comprobar si un archivo DLL es una DLL de Win32 o si se trata de un montaje de CLR. En el momento en que utilizo este código
try
{
this.currentWorkingDirectory = Path.GetDirectoryName(assemblyPath);
//Try to load the assembly.
assembly = Assembly.LoadFile(assemblyPath);
return assembly != null;
}
catch (FileLoadException ex)
{
exception = ex;
}
catch (BadImageFormatException ex)
{
exception = ex;
}
catch (ArgumentException ex)
{
exception = ex;
}
catch (Exception ex)
{
exception = ex;
}
if (exception is BadImageFormatException)
{
return false;
}
Pero me gusta comprobar antes de la carga, porque no quiero que esas excepciones (tiempo).
¿Hay una mejor manera?
Solución
Comprobar la cabecera PE:
cabecera DOS comienza en 0x0, el valor DWORD en 0x3C contiene un puntero al PE firma (normalmente 0x80) que es 4 bytes, los próximos 20 bytes es la COFF encabezado y entonces no es la cabecera PE (En 0x9. El encabezado de PE es de 224 bytes y contiene el directorio de datos (al 96 bytes en la cabecera PE = 0xf. los entrada 15a (en 0x16 es el encabezado CLR descriptor (a veces llamado el COM descriptor, pero esto no tiene nada que ver con COM). Si esto es vacía (es decir, 0 en los 8 bytes de 0x168 a 0x16f), entonces el archivo no es un .NET montaje. Si desea comprobar si es una DLL COM entonces usted debe buscar a ver si se exporta GetClassObject.
Actualizar : hay una manera más '.NET' para lograrlo:
Module.GetPEKind
método y comprobar el PortableExecutableKinds
enumeración:
NotAPortableExecutableImage El archivo no está en portable ejecutable (PE) formato de archivo.
ILOnly El ejecutable sólo contiene lenguaje intermedio de Microsoft (MSIL), y es por lo tanto neutral con respecto a plataformas de 32 bits o de 64 bits.
Required32Bit El ejecutable se puede ejecutar en una plataforma de 32 bits, o en el Windows de 32 bits en Windows (WOW) ambiente en una plataforma de 64 bits.
PE32Plus El ejecutable requiere una plataforma de 64 bits.
Unmanaged32Bit El ejecutable contiene código no administrado puro.
Otros consejos
Si un ensamblado se carga por ejemplo Assembly.LoadFile(dotNetDllorExe)
y no lanza ninguna excepción, es un ensamblado de .NET válido. Si no es así, entonces va a lanzar una “BadImageFormatException”.
La idea de controlar el clima es un archivo de ensamblaje o no cargándolo y comprobando si excepción se emite o no; no parece ser demasiado limpio. Después se supone que todas las excepciones que se utilizará en casos excepcionales.
NET son archivos regulares Win32 PE, el sistema operativo no diferenciar entre NET y Win32 binarios ejecutables, que son los mismos archivos PE normales. Entonces, ¿cómo funciona el sistema si un DLL o EXE es un ensamblado administrado con el fin de cargar el CLR?
Se valida el encabezado del archivo para comprobar si se trata de un ensamblado administrado o no. En el ECMA Especificaciones partición II - Metadatos que se envía junto con el SDK de .NET que se ve hay una cabecera separada de la CLI en el formato PE. Es el directorio 15a de datos en el PE Opcional encabezados . Por lo tanto, en términos simples, si tenemos valor en este directorio de datos, entonces significa que este es un ensamblado de .NET válida, de lo contrario no lo es.
internal static class PortableExecutableHelper
{
internal static bool IsDotNetAssembly(string peFile)
{
uint peHeader;
uint peHeaderSignature;
ushort machine;
ushort sections;
uint timestamp;
uint pSymbolTable;
uint noOfSymbol;
ushort optionalHeaderSize;
ushort characteristics;
ushort dataDictionaryStart;
uint[] dataDictionaryRVA = new uint[16];
uint[] dataDictionarySize = new uint[16];
Stream fs = new FileStream(peFile, FileMode.Open, FileAccess.Read);
BinaryReader reader = new BinaryReader(fs);
//PE Header starts @ 0x3C (60). Its a 4 byte header.
fs.Position = 0x3C;
peHeader = reader.ReadUInt32();
//Moving to PE Header start location...
fs.Position = peHeader;
peHeaderSignature = reader.ReadUInt32();
//We can also show all these value, but we will be
//limiting to the CLI header test.
machine = reader.ReadUInt16();
sections = reader.ReadUInt16();
timestamp = reader.ReadUInt32();
pSymbolTable = reader.ReadUInt32();
noOfSymbol = reader.ReadUInt32();
optionalHeaderSize = reader.ReadUInt16();
characteristics = reader.ReadUInt16();
/*
Now we are at the end of the PE Header and from here, the
PE Optional Headers starts...
To go directly to the datadictionary, we'll increase the
stream’s current position to with 96 (0x60). 96 because,
28 for Standard fields
68 for NT-specific fields
From here DataDictionary starts...and its of total 128 bytes. DataDictionay has 16 directories in total,
doing simple maths 128/16 = 8.
So each directory is of 8 bytes.
In this 8 bytes, 4 bytes is of RVA and 4 bytes of Size.
btw, the 15th directory consist of CLR header! if its 0, its not a CLR file :)
*/
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)
{
Console.WriteLine("This is NOT a valid CLR File!!");
return false;
}
else
{
Console.WriteLine("This is a valid CLR File..");
return true;
}
fs.Close();
}
}
Ante el mismo problema en el pasado, he recurrido a la utilización de su enfoque de la reflexión, porque la alternativa es leer manualmente la cabecera PE como esto . Nos pareció que era excesivo para mi escenario, pero puede ser útil para usted.
No se especificó si se tiene que hacer esto en el código, o si lo que personalmente necesita saber si un archivo que está viendo en su sistema es un ensamblado de .NET (que tal vez usted piensa que requiere que la escritura de su el propio código para hacerlo). En este último caso, puede utilizar Dependency Walker para ver si tiene una dependencia en Mscoree.dll, que es el motor de tiempo de ejecución .Net.
Se podría utilizar algo como:
AssemblyName assemblyName = null;
try
{
assemblyName = AssemblyName.GetAssemblyName(filename);
}
catch (System.IO.FileNotFoundException ex)
{
throw new Exception("File not found!", ex);
}
catch (System.BadImageFormatException ex)
{
throw new Exception("File is not an .Net Assembly.", ex);
}
Por favor, también echa un vistazo: https://msdn.microsoft.com/en -us / biblioteca / ms173100.aspx
Puede leer los dos primeros bytes del archivo, si los bytes son "MZ" y luego tratar de leer el nombre de ensamblado para determinar ( Microsoft manera lenta) la validez del conjunto.
public static bool isValidAssembly (string sFileName)
{
try
{
using (FileStream fs = File.OpenRead(sFileName))
{
if ((fs.ReadByte() != 'M') || (fs.ReadByte() != 'Z'))
{
fs.Close();
return false;
}
fs.Close();
}
// http://msdn.microsoft.com/en-us/library/ms173100.aspx
object foo = SR.AssemblyName.GetAssemblyName(sFileName);
return true;
}
catch
{
return false;
}
}