C# 通过直接读取PE来确定dll是否为有效的CLR dll(64位问题)

C# 通过直接读取PE来确定dll是否为有效的CLR dll(64位问题),c#,parsing,dll,64-bit,portable-executable,C#,Parsing,Dll,64 Bit,Portable Executable,我正在将一个32位的web应用程序迁移到64位,我们的插件加载程序代码有一些问题 在32位版本中,我们扫描webapps bin目录中的所有.net DLL,然后使用Assembly.load加载它们,以检查是否存在插件属性 我们使用公共域代码以一种相当巧妙的方式做到了这一点: /// <summary> /// Returns true if the file specified is a real CLR type, /// otherwise false is returne

我正在将一个32位的web应用程序迁移到64位,我们的插件加载程序代码有一些问题

在32位版本中,我们扫描webapps bin目录中的所有.net DLL,然后使用
Assembly.load加载它们,以检查是否存在插件属性

我们使用公共域代码以一种相当巧妙的方式做到了这一点:

/// <summary>
/// Returns true if the file specified is a real CLR type, 
/// otherwise false is returned.
/// False is also returned in the case of an exception being caught
/// </summary>
/// <param name="file">A string representing the file to check for 
/// CLR validity</param>
/// <returns>True if the file specified is a real CLR type, 
/// otherwise false is returned.
/// False is also returned in the case of an exception being 
/// caught</returns>
public static bool IsDotNetAssembly(String file)
{   
    Stream fs = new FileStream(@file, FileMode.Open, FileAccess.Read);

    try
    {
        BinaryReader reader = new BinaryReader(fs);
        //PE Header starts @ 0x3C (60). Its a 4 byte header.
        fs.Position = 0x3C;
        uint peHeader = reader.ReadUInt32();
        //Moving to PE Header start location...
        fs.Position = peHeader;
        uint peHeaderSignature = reader.ReadUInt32();
        ushort machine = reader.ReadUInt16();
        ushort sections = reader.ReadUInt16();
        uint timestamp = reader.ReadUInt32();
        uint pSymbolTable = reader.ReadUInt32();
        uint noOfSymbol = reader.ReadUInt32();
        ushort optionalHeaderSize = reader.ReadUInt16();
        ushort characteristics = reader.ReadUInt16();

        // PE Optional Headers 
        // To go directly to the datadictionary, we'll increase the stream's current position to with 96 (0x60). 
        // 28 bytes for Standard fields
        // 68 bytes for NT-specific fields 
        // 128 bytes DataDictionary 
        //  DataDictionay has 16 directories
        //  8 bytes per directory (4 bytes RVA and 4 bytes of Size.) 
        // 15th directory consist of CLR header! (if its 0, it is not a CLR file )

        uint[] dataDictionaryRVA = new uint[16];
        uint[] dataDictionarySize = new uint[16];            
        ushort 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)
        {
            fs.Close();
            return false;
        }
        else
        {
            fs.Close();
            return true;
        }
    }
    catch (Exception)
    {
        return false;
    }
    finally
    {
        fs.Close();
    }
}
//
///如果指定的文件是实CLR类型,则返回true,
///否则返回false。
///如果捕获到异常,也会返回False
/// 
///表示要检查的文件的字符串
///CLR有效性
///如果指定的文件是真正的CLR类型,则为True,
///否则返回false。
///如果发生异常,也会返回False
///抓住
公共静态bool IsDotNetAssembly(字符串文件)
{   
Stream fs=新文件流(@file,FileMode.Open,FileAccess.Read);
尝试
{
BinaryReader=新的BinaryReader(fs);
//PE头从0x3C(60)开始。它是一个4字节的头。
fs.位置=0x3C;
uint peHeader=reader.ReadUInt32();
//正在移动到PE标头开始位置。。。
fs.Position=PEF头;
uint peHeaderSignature=reader.ReadUInt32();
ushort machine=reader.ReadUInt16();
ushort sections=reader.ReadUInt16();
uint timestamp=reader.ReadUInt32();
uint pSymbolTable=reader.ReadUInt32();
uint noOfSymbol=reader.ReadUInt32();
ushort optionalHeaderSize=reader.ReadUInt16();
ushort characteristics=reader.ReadUInt16();
//PE可选标题
//要直接转到datadictionary,我们将流的当前位置增加到96(0x60)。
//标准字段为28字节
//68字节用于NT特定字段
//128字节数据字典
//DataDictionary有16个目录
//每个目录8字节(4字节RVA和4字节大小。)
//第15个目录包含CLR头!(如果为0,则不是CLR文件)
uint[]数据字典YRVA=新uint[16];
uint[]dataDictionarySize=新uint[16];
ushort-dataDictionaryStart=Convert.ToUInt16(Convert.ToUInt16(fs.Position)+0x60);
fs.Position=dataDictionaryStart;
对于(int i=0;i<15;i++)
{
dataDictionaryRVA[i]=reader.ReadUInt32();
dataDictionarySize[i]=reader.ReadUInt32();
}
如果(dataDictionaryRVA[14]==0)
{
fs.Close();
返回false;
}
其他的
{
fs.Close();
返回true;
}
}
捕获(例外)
{
返回false;
}
最后
{
fs.Close();
}
}

现在的问题是,我们现在必须处理64位或独立于平台的DLL,偏移量似乎发生了变化,这段代码失败了。是否有人知道对上述代码的正确修改,以便对有效的64位纯DLL或独立于平台的DLL返回true?

是否有理由不能在框架中使用方法?示例代码如下:

        var assembly = Assembly.Load("path to assembly");
        ImageFileMachine machine;
        PortableExecutableKinds peKind;
        assembly.ManifestModule.GetPEKind(out peKind, out machine);

应该让你开始。后者基本上是corflags

您的代码不适用于x64位DLL的原因是 因为映像的可选头大小为x64位DLL和 x86位DLL则不同。你必须采取不同的措施 图像可选页眉大小,以便确定 给定的DLL是否为.Net DLL

第3.4节中的标题(可选标题) 要跳转到数据目录的不同偏移量:

  • 对于PE32(x86)图像,偏移量为
    0x60
    (与代码中的偏移量相同),并且
  • 对于PE32+(x64)图像,偏移量为
    0x70
  • 以确定给定DLL是否为x64位DLL 您必须读取可选标题的魔法字节:

  • 0x20b的值表示PE32+
  • 0x10b
    PE32
  • 我扩展了您的示例:

    Stream fs = new FileStream(@file, FileMode.Open, FileAccess.Read);
    
    try
    {
      BinaryReader reader = new BinaryReader(fs);
      //PE Header starts @ 0x3C (60). Its a 4 byte header.
      fs.Position = 0x3C;
      uint peHeader = reader.ReadUInt32();
      //Moving to PE Header start location...
      fs.Position = peHeader;
      uint peHeaderSignature = reader.ReadUInt32();
      ushort machine = reader.ReadUInt16();
      ushort sections = reader.ReadUInt16();
      uint timestamp = reader.ReadUInt32();
      uint pSymbolTable = reader.ReadUInt32();
      uint noOfSymbol = reader.ReadUInt32();
      ushort optionalHeaderSize = reader.ReadUInt16();
      ushort characteristics = reader.ReadUInt16();
    
      long posEndOfHeader = fs.Position;
      ushort magic = reader.ReadUInt16();
    
      int off = 0x60; // Offset to data directories for 32Bit PE images
                      // See section 3.4 of the PE format specification.
      if (magic == 0x20b) //0x20b == PE32+ (64Bit), 0x10b == PE32 (32Bit)
      {
        off = 0x70;  // Offset to data directories for 64Bit PE images
      }
      fs.Position = posEndOfHeader;       
    
      uint[] dataDictionaryRVA = new uint[16];
      uint[] dataDictionarySize = new uint[16];
      ushort dataDictionaryStart = Convert.ToUInt16(Convert.ToUInt16(fs.Position) + off);
    
      fs.Position = dataDictionaryStart;
    
      for (int i = 0; i < 15; i++)
      {
        dataDictionaryRVA[i] = reader.ReadUInt32();
        dataDictionarySize[i] = reader.ReadUInt32();
      }
      if (dataDictionaryRVA[14] == 0)
      {
        fs.Close();
        return false;
      }
      else
      {
        fs.Close();
        return true;
      }
     }
     catch (Exception)
     {
       return false;
     }
     finally
     {
       fs.Close();
     }
    
    Stream fs=new FileStream(@file,FileMode.Open,FileAccess.Read);
    尝试
    {
    BinaryReader=新的BinaryReader(fs);
    //PE头从0x3C(60)开始。它是一个4字节的头。
    fs.位置=0x3C;
    uint peHeader=reader.ReadUInt32();
    //正在移动到PE标头开始位置。。。
    fs.Position=PEF头;
    uint peHeaderSignature=reader.ReadUInt32();
    ushort machine=reader.ReadUInt16();
    ushort sections=reader.ReadUInt16();
    uint timestamp=reader.ReadUInt32();
    uint pSymbolTable=reader.ReadUInt32();
    uint noOfSymbol=reader.ReadUInt32();
    ushort optionalHeaderSize=reader.ReadUInt16();
    ushort characteristics=reader.ReadUInt16();
    长posEndOfHeader=fs.位置;
    ushort magic=reader.ReadUInt16();
    int off=0x60;//32位PE映像到数据目录的偏移量
    //参见PE格式规范第3.4节。
    if(magic==0x20b)//0x20b==PE32+(64位),0x10b==PE32(32位)
    {
    off=0x70;//偏移到64位PE映像的数据目录
    }
    fs.位置=posEndOfHeader;
    uint[]数据字典YRVA=新uint[16];
    uint[]dataDictionarySize=新uint[16];
    ushort-dataDictionaryStart=Convert.ToUInt16(Convert.ToUInt16(fs.Position)+off;
    fs.Position=dataDictionaryStart;
    对于(int i=0;i<15;i++)
    {
    dataDictionaryRVA[i]=reader.ReadUInt32();
    dataDictionarySize[i]=reader.ReadUInt32();
    }
    如果(dataDictionaryRVA[14]==0)
    {
    fs.Close();
    返回false;
    }
    其他的
    {
    fs.Close();
    返回true;
    }
    }
    捕获(例外)
    {
    返回false;
    }
    最后
    {
    fs.Close();
    }
    
    在Windows SDK中,还为PE32/PE32+可选标头定义了结构。 这些结构的描述可以在这里找到


    希望如此,这会有所帮助。

    对于不使用反射且不直接加载程序集的替代方案,请尝试。看来你是公平的
    MetadataReaderHost host = new PeReader.DefaultHost();
    var module = host.LoadUnitFrom(args[0]) as IModule;
    if (module == null)
    {
         Console.WriteLine(args[0]+" is not a PE file containing a CLR module or assembly.");
         return;
    }