C# 通过直接读取PE来确定dll是否为有效的CLR dll(64位问题)
我正在将一个32位的web应用程序迁移到64位,我们的插件加载程序代码有一些问题 在32位版本中,我们扫描webapps bin目录中的所有.net DLL,然后使用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
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;
}