如何确定 DLL 是托管程序集还是本机程序集(防止加载本机 dll)? [英] How to determine whether a DLL is a managed assembly or native (prevent loading a native dll)?

查看:19
本文介绍了如何确定 DLL 是托管程序集还是本机程序集(防止加载本机 dll)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景:

我的 C# 应用程序包括一个插件框架和通用插件加载器.

My C# application includes a plugin framework and generic plugin loader.

插件加载器枚举应用程序目录以识别插件dll(本质上它此时搜索*.dll).

The plugin loader enumerates the application directory in order to identify plugin dlls (essentially it searches for *.dll at this time).

在同一应用程序目录中是本机(Windows,非 .net)dll,间接依赖于插件 dll 之一.

Within the same application directory is a native (Windows, non-.net) dll, which, indirectly, one of the plugin dlls depends upon.

插件加载器盲目地假设 native.dll 是一个 .NET Assembly dll,仅仅因为它只检查文件扩展名.当它尝试加载本机 dll 时,抛出异常:

The plugin loader blindly assumes that the native.dll is a .NET Assembly dll, simply because it only checks the file extension. When it attempts to load the native dll, an exception is thrown:

无法加载文件或程序集‘native.dll’或其依赖项之一.该模块应包含程序集清单."

"Could not load file or assembly 'native.dll' or one of its dependencies. The module was expected to contain an assembly manifest."

如果插件加载失败,我基本上会创建一个诊断报告,所以我试图避免让这个日志充满关于无法加载本机 dll 的消息(我什至不想尝试).

I basically create a diagnostic report if plugin loading fails, so I'm trying to avoid having this log filled up with messages about not being able to load the native dll (which I don't even want to attempt).

问题:

是否有一些 .NET API 调用可用于确定二进制文件是否恰好是 .NET 程序集,以便我根本不尝试加载本机 dll?

Is there some .NET API call that I can use to determine whether a binary happens to be a .NET assembly so that I don't attempt to load the native dll at all?

也许从长远来看,我会将我的插件移动到一个子目录,但现在,我只想解决一个不涉及在我的插件加载器中硬编码native.dll"名称的方法.

Perhaps longer term I will move my plugins to a subdirectory, but for now, I just want a work around that doesn't involve hard-coding the "native.dll" name inside my plugin loader.

我想我正在寻找某种我忽略的静态 Assembly.IsManaged() API 调用......大概不存在这样的 API?

I guess I'm looking for some kind of static Assembly.IsManaged() API call that I've overlooked.... presumably no such API exists?

推荐答案

lubos hasko 引用的答案很好,但它不适用于 64 位程序集.这是一个更正的版本(灵感来自 http://apichange.codeplex.com/SourceControl/changeset/view/76c98b8c7311#ApiChange.Api/src/Introspection/CorFlagsReader.cs)

Answer quoted by lubos hasko is good but it doesn't work for 64-bit assemblies. Here's a corrected version (inspired by http://apichange.codeplex.com/SourceControl/changeset/view/76c98b8c7311#ApiChange.Api/src/Introspection/CorFlagsReader.cs)

public static bool IsManagedAssembly(string fileName)
{
    using (Stream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
    using (BinaryReader binaryReader = new BinaryReader(fileStream))
    {
        if (fileStream.Length < 64)
        {
            return false;
        }

        //PE Header starts @ 0x3C (60). Its a 4 byte header.
        fileStream.Position = 0x3C;
        uint peHeaderPointer = binaryReader.ReadUInt32();
        if (peHeaderPointer == 0)
        {
            peHeaderPointer = 0x80;
        }

        // Ensure there is at least enough room for the following structures:
        //     24 byte PE Signature & Header
        //     28 byte Standard Fields         (24 bytes for PE32+)
        //     68 byte NT Fields               (88 bytes for PE32+)
        // >= 128 byte Data Dictionary Table
        if (peHeaderPointer > fileStream.Length - 256)
        {
            return false;
        }

        // Check the PE signature.  Should equal 'PE'.
        fileStream.Position = peHeaderPointer;
        uint peHeaderSignature = binaryReader.ReadUInt32();
        if (peHeaderSignature != 0x00004550)
        {
            return false;
        }

        // skip over the PEHeader fields
        fileStream.Position += 20;

        const ushort PE32 = 0x10b;
        const ushort PE32Plus = 0x20b;

        // Read PE magic number from Standard Fields to determine format.
        var peFormat = binaryReader.ReadUInt16();
        if (peFormat != PE32 && peFormat != PE32Plus)
        {
            return false;
        }

        // Read the 15th Data Dictionary RVA field which contains the CLI header RVA.
        // When this is non-zero then the file contains CLI data otherwise not.
        ushort dataDictionaryStart = (ushort)(peHeaderPointer + (peFormat == PE32 ? 232 : 248));
        fileStream.Position = dataDictionaryStart;

        uint cliHeaderRva = binaryReader.ReadUInt32();
        if (cliHeaderRva == 0)
        {
            return false;
        }

        return true;
    }
}

缺失的部分是根据我们是 PE32 还是 PE32Plus 以不同的方式偏移到数据字典的开头:

The missing piece was to offset to the data dictionary start differently depending on whether we are PE32 or PE32Plus:

    // Read PE magic number from Standard Fields to determine format.
    var peFormat = binaryReader.ReadUInt16();
    if (peFormat != PE32 && peFormat != PE32Plus)
    {
        return false;
    }

    // Read the 15th Data Dictionary RVA field which contains the CLI header RVA.
    // When this is non-zero then the file contains CLI data otherwise not.
    ushort dataDictionaryStart = (ushort)(peHeaderPointer + (peFormat == PE32 ? 232 : 248));

这篇关于如何确定 DLL 是托管程序集还是本机程序集(防止加载本机 dll)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆