Лучший способ проверить, является ли DLL файл сборкой CLR в С#

Каков наилучший способ проверить, является ли DLL файл DLL Win32 или сборкой CLR. В настоящий момент я использую этот код

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

Но мне нравится проверять перед загрузкой, потому что мне не нужны эти исключения (время).

Есть ли лучший способ?

Ответы

Ответ 1

Проверьте заголовок PE:

Заголовок DOS начинается с 0x0, DWORD на 0x3c содержит указатель на PE подпись (обычно 0x80), которая равна 4 байты, следующие 20 байтов - COFF заголовок, а затем заголовок PE (в 0x9. Заголовок PE составляет 224 байта и содержит каталог данных (на этапе 96 байтов в PE-заголовок = 0xf. 15-я запись (в 0x16 - заголовок CLR дескриптор (иногда называемый COM дескриптора, но это не имеет ничего общего с COM). Если это empty (т.е. 0 в 8 байтах от 0x168 до 0x16f), тогда файл не является .NET. сборка. Если вы хотите проверить, это COM-библиотека, тогда вы должны посмотреть посмотрите, экспортирует ли он GetClassObject.

Ссылка

UPDATE: существует более ".NET" способ выполнения этого:

Используйте Module.GetPEKind и проверьте PortableExecutableKinds Перечисление:

NotAPortableExecutableImage Файл не находится в переносном исполняемом файле (PE).

ILOnly Исполняемый файл содержит только промежуточный язык Microsoft (MSIL), и поэтому нейтральна с для 32-разрядных или 64-разрядных платформ.

Required32Bit Исполняемый файл может быть запущен на 32-битной платформе или в 32-разрядная Windows на Windows (WOW) среды на 64-битной платформе.

PE32Plus Для исполняемого файла требуется 64-разрядная платформа.

Unmanaged32Bit Исполняемый файл содержит чистый неуправляемый код.

Ответ 2

Если сборка загружается, например, Assembly.LoadFile(dotNetDllorExe) и не вызывает никакого исключения, ее действительная сборка .NET. Если его нет, тогда он выкинет "исключение BadImageFormatException".

Идея проверки погоды в файле собирается или нет, загружая ее и проверяя, выбрано ли исключение; похоже, не слишком чист. Ведь исключения должны использоваться исключительно.


Сборки .NET - это обычные файлы Win32 PE, операционная система не проводит различия между сборками .NET и исполняемыми двоичными файлами Win32, они являются одинаковыми нормальными PE файлами. Итак, как работает система, если DLL или EXE - это управляемая сборка для загрузки CLR?

Он проверяет заголовок файла, чтобы проверить, есть ли его управляемая сборка или нет. В спецификации ECMA Partition II - Метаданные, поставляемые вместе с .NET SDK, вы видите, что в PE-формате есть отдельный заголовок CLI. Это 15-й каталог данных в дополнительных заголовках PE. Итак, в простых терминах, если у нас есть значение в этом каталоге данных, значит, это допустимая сборка .NET, иначе это не так.

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

ECMA Ref, Ссылка в блоге

Ответ 3

Столкнувшись с той же проблемой в прошлом, я прибегнул к использованию вашего подхода к размышлению, потому что альтернативой является ручной просмотр заголовка PE как этот. Мне показалось, что это слишком тяжело для моего сценария, но это может быть полезно для вас.

Ответ 4

Вы не указали, нужно ли вам делать это в коде, или если вам просто нужно знать, есть ли файл, который вы просматриваете в вашей системе, является сборкой .NET(что, возможно, вам кажется, требует, чтобы вы писали свои собственный код для этого). Если последний, вы можете использовать Dependency Walker, чтобы узнать, имеет ли он зависимость от MSCOREE.dll, который является механизмом выполнения .Net.

Ответ 5

Вы можете использовать что-то вроде:

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

Пожалуйста, также проверьте: https://msdn.microsoft.com/en-us/library/ms173100.aspx

Ответ 6

Вы можете прочитать первые два байта из файла, если байты "MZ", затем попробуйте прочитать имя сборки, чтобы определить (медленный путь Microsoft) действительность сборки.

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