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

sch*_*tbi 17 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;
    }
Run Code Online (Sandbox Code Playgroud)

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

有没有更好的办法?

Mit*_*eat 18

检查PE头:

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

参考.

更新:有一种更".NET"的方式来完成这个:

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

NotAPortableExecutableImage该文件不是可移植可执行(PE)文件格式.

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

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

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

Unmanaged32Bit可执行文件包含纯非托管代码.

  • 我正在尝试做类似于OP的事情.这个解决方案听起来不错,但是如何在不调用Assembly.LoadFile的情况下获取Module实例(在调用GetPEKind之前会为非CLR dll引发BadImageFormatException)? (11认同)
  • @Mitch:好吧也许downvote有点仓促.我试图撤消它但超时已经过去了.抱歉.随便给我一些答案:).我确实尝试过这个工作.虽然在我看来保罗问题是有效的.Module.GetPEKind是实例.因此,要获得一个模块,您需要调用程序集Assembly.Load.这将抛出异常.由于问题明确指出"没有例外",所以这个答案似乎不完整或不正确. (3认同)

Jer*_*son 8

如果一个程序集被加载,例如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();
    }
}
Run Code Online (Sandbox Code Playgroud)

ECMA参考,博客参考