检查DLL文件是否是C#中的CLR程序集的最佳方法

时间:2009-09-02 08:42:49

标签: c# dll .net-assembly

检查DLL文件是Win32 DLL还是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;
    }

但我喜欢在加载前检查,因为我不想要那些例外(时间)。

有更好的方法吗?

8 个答案:

答案 0 :(得分:18)

检查PE头:

  

DOS标头从0x0开始,DWORD位于   0x3c包含指向PE的指针   签名(通常为0x80),即4   字节,接下来的20个字节是COFF   标题然后有PE标题   (在0x9处.PE头是224个字节   并包含数据目录(96   字节进入PE头= 0xf。该   第15个条目(0x16是CLR标题   描述符(有时称为COM   描述符,但这没有   与COM有关的任何事情。如果是这样的话   空(即0x168的8个字节中的0   到0x16f)然后该文件不是.NET   部件。如果你想检查一下   是一个COM DLL然后你应该看看   看看它是否导出了GetClassObject。

Ref.

更新:有更多“.NET”方式来实现这一目标:

使用Module.GetPEKind方法并检查PortableExecutableKinds枚举:

  

NotAPortableExecutableImage 该文件不在可移植的可执行文件中   (PE)文件格式。

     

ILOnly 可执行文件仅包含Microsoft中间语言   (MSIL),因此是中立的   尊重32位或64位平台。

     

Required32Bit 可执行文件可以在32位平台上运行,也可以在   Windows上的32位Windows(WOW)   64位平台上的环境。

     

PE32Plus 可执行文件需要64位平台。

     

Unmanaged32Bit 可执行文件包含纯粹的非托管代码。

答案 1 :(得分:6)

如果一个程序集被加载,例如Assembly.LoadFile(dotNetDllorExe)并且没有抛出任何异常,那么它就是一个有效的.NET程序集。如果不是那么它将抛出“BadImageFormatException”。

通过加载文件并检查是否抛出异常来检查文件的天气是否装配的想法;看起来不太干净。在异常使用所有异常之后。

<小时/> .NET程序集是常规的Win32 PE文件,操作系统不区分.NET程序集和Win32可执行二进制文件,它们是相同的普通PE文件。那么,如果DLL或EXE是托管程序集以加载CLR,系统如何工作?

验证文件头以检查它是否是托管程序集。在ECMA规范分区II - 随.NET SDK一起提供的元数据中,您会看到PE格式中有一个单独的CLI标头。它是PE可选标题中的第15个数据目录。所以,简单来说,如果我们在这个数据目录中有价值,那么这意味着这是一个有效的.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 RefBlog Ref

答案 2 :(得分:2)

过去遇到同样的问题,我使用了你的反射方法,因为另一种方法是手动读取PE头like this。对我的情况来说似乎有些过分,但它可能对你有用。

答案 3 :(得分:1)

我目前的声誉不足以直接评论Jeremy's answer,它对于32位DLL来说效果很好。但是我不得不针对64位DLL进行优化,这些DLL似乎具有更多Windows特定的COFF字段。

Microsoft所述,COFF Magic number 指示格式是PE32(32位)还是PE32 +(64位)。如果是后者,则到数据目录的偏移为112字节而不是96字节。

// After the COFF header, the first COFF field is a Magic Number.
UInt16 coffMagic = reader.ReadUInt16();

// Skip remaining fields to reach data directories.  See:
// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#optional-header-image-only
if (coffMagic == 0x010B)
{
    // It's a 32-bit DLL.  96 bytes of COFF fields.
    // Subtract 2 for the magic number we just read.
    fs.Position += (96 - 2);
}
else if (coffMagic == 0x020B)
{
    // It's a 64-bit DLL.  112 bytes of COFF fields.
    // Subtract 2 for the magic number we just read.
    fs.Position += (112 - 2);
}

答案 4 :(得分:0)

您没有指定是否必须在代码中执行此操作,或者您只是个人需要知道您在系统上查看的文件是否是.NET程序集(可能您认为需要您编写自己的代码这样做)。如果是后者,您可以使用Dependency Walker查看它是否依赖于MSCOREE.dll,即.Net运行时引擎。

答案 5 :(得分:0)

您可以使用以下内容:

        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 :(得分:0)

更新2020:

这应该是检测dll是否为.Net库的最简单方法:

public bool IsNetAssembly(string fileName)
{
    try
    {
        AssemblyName.GetAssemblyName(fileName);
    }
    catch (BadImageFormatException)
    {
        // not a .Net Assembly
         return false;
    }
    
    return true;
}

答案 7 :(得分:-1)

  

您可以从文件中读取前两个字节,如果字节为“MZ”,则尝试读取程序集名称以确定(microsoft slow way)程序集的有效性。

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