通过直接读取PE(64位问题)确定dll是否为.valid CLR dll
本文关键字:dll 确定 是否 CLR valid 问题 64位 读取 PE | 更新日期: 2023-09-27 18:18:42
我正在将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();
}
}
现在的问题是,我们现在必须处理64位或独立于平台的dll,并且偏移量似乎已经改变,这段代码失败了。有没有人知道对上述代码的正确修改以仅为有效的64位或独立于平台的dll返回true ?
您的代码不能用于x64位dll的原因是因为图像可选的头大小的x64位DLL和而x86位DLL则不同。你必须选择不同的图像可选的标题大小要考虑进去,以便确定是否给定的DLL是。net DLL。
第3.4节(可选头)中描述的PE文件格式规范跳转到数据目录的不同偏移量:
- 对于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();
}
在Windows SDK中也有为PE32/PE32+可选头定义的结构。这些结构的描述可以在MSDN中找到。
希望有帮助。
对于不使用反射且不直接加载程序集的替代方法,请尝试使用通用编译器基础结构元数据API。似乎您可以相当容易地加载PE程序集并确定它是否具有CLR模块。
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;
}
是否有不能在框架中使用方法的原因?示例代码如下:
var assembly = Assembly.Load("path to assembly");
ImageFileMachine machine;
PortableExecutableKinds peKind;
assembly.ManifestModule.GetPEKind(out peKind, out machine);
GetPEKind方法在MSDN和portableexecutabletypes应该让你开始。后者基本上是corflags