c# – 通过直接读取PE确定dll是否为.valid CLR dll(64位问题)

我正在努力将32位Web应用程序迁移到64位,并且我的插件加载器代码存在一些问题.

在32位版本中,我们扫描所有.net dll的webapps bin目录,然后使用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();
    }
}

现在问题是我们现在必须处理64位或平台独立的dll,并且偏移量似乎已经改变,并且此代码失败.有没有人知道对上述代码的正确修改,对于有效的64位唯一OR平台无关的dll返回true?

解决方法

您的代码不适用于x64-Bit DLL的原因是
因为图像可选的头部大小为x64-Bit DLL和
x86-Bit DLL是不同的.你必须采取不同的方式
图像可选的标题大小考虑在内以便确定
是否给定的DLL是.Net DLL.

3.4节(可选标题)中的PE file format specification describes
跳转到数据目录的不同偏移量:

>对于PE32(x86)图像,偏移量为0x60(就像在代码中一样)和
>对于PE32(x64)图像,偏移量为0x70.

为了确定给定的DLL是否是x64位DLL
你必须阅读可选标题的魔术字节:

>值0x20b表示PE32,
>值为0x10b PE32.

我扩展了你的例子:

Stream fs = new FileStream(@file,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();
 }

在Windows SDK中,还为PE32 / PE32可选标头定义了结构.
这些结构的描述可以在这里找到MSDN.

希望这可以帮助.

相关文章

在要实现单例模式的类当中添加如下代码:实例化的时候:frmC...
1、如果制作圆角窗体,窗体先继承DOTNETBAR的:public parti...
根据网上资料,自己很粗略的实现了一个winform搜索提示,但是...
近期在做DSOFramer这个控件,打算自己弄一个自定义控件来封装...
今天玩了一把WMI,查询了一下电脑的硬件信息,感觉很多代码都...
最近在研究WinWordControl这个控件,因为上级要求在系统里,...