代码之家  ›  专栏  ›  技术社区  ›  schoetbi

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

  •  20
  • schoetbi  · 技术社区  · 16 年前

        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 回复  |  直到 15 年前
        1
  •  21
  •   Mitch Wheat    16 年前

    检查PE接头:

    DOS标头从0x0开始,DWORD在 签名(通常为0x80),即4 (0x9。PE报头为224字节 并包含数据目录(在96处 第15个条目(0x16处是CLR标头 描述符,但它没有 与COM有关的任何事情)。如果这是 空(即0x168后的8个字节中的0 如果是COM DLL,那么您应该查看 查看它是否导出GetClassObject。

    Ref.

    更新 :还有更多。NET的实现方式:

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

    NotAPortable可执行映像 文件不在可移植可执行文件中 (PE)文件格式。

    仅限IL 可执行文件仅包含Microsoft中间语言 (MSIL),因此对 对于32位或64位平台。

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

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

    非管理32位 可执行文件包含纯非托管代码。

        2
  •  12
  •   Jeremy Thompson    11 年前

    如果组件被加载,例如 Assembly.LoadFile(dotNetDllorExe) 并且不会抛出任何异常,这是有效的。NET程序集。如果不是,它将抛出BadImageFormatException。

    通过加载文件并检查是否抛出异常来检查文件是否是程序集的想法;看起来不太干净。毕竟,所有例外情况都应该在特殊情况下使用。


    .NET程序集是常规的Win32 PE文件,操作系统对此没有区别。NET程序集和Win32可执行二进制文件,它们是相同的普通PE文件。那么,系统如何判断DLL或EXE是否是托管程序集以加载CLR呢?

    它验证文件头以检查其是否为托管程序集。在随附的ECMA规范分区II元数据中。NET SDK中,PE格式中有一个单独的CLI头。正是 因此,简单地说,如果我们在这个数据目录中有值,那么这意味着它是有效的。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 Blog Ref

        3
  •  3
  •   Calum Heaney    5 年前

    Jeremy's answer 它适用于32位DLL。但我必须针对64位DLL对其进行优化,这些DLL似乎有更多特定于Windows的COFF字段。

    Microsoft ,COFF 幻数 指示格式是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://learn.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
  •  2
  •   Tim Cooper    14 年前

    过去,面对同样的问题,我求助于使用您的反射方法,因为另一种方法是手动读取PE标头 like this 对于我的场景来说,这似乎有些矫枉过正,但它可能对你有用。

        5
  •  0
  •   Sam Skuce    13 年前

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

        6
  •  0
  •   user5164139 user5164139    10 年前

    你可以使用类似的东西:

            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

        7
  •  0
  •   BenHero    5 年前

    2020年更新:

    这应该是检测dll是否为a的最简单方法。网络库:

    public bool IsNetAssembly(string fileName)
    {
        try
        {
            AssemblyName.GetAssemblyName(fileName);
        }
        catch (BadImageFormatException)
        {
            // not a .Net Assembly
             return false;
        }
        
        return true;
    }
    
        8
  •  -2
  •   Jacinto Gomez    12 年前

    您可以从文件中读取前两个字节,如果字节是“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;
            }
        }