C# · 12月 26, 2021

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.

希望这可以帮助.