如何确定.NET程序集是为x86还是x64构建的?

我有一个任意的。net程序集列表。

我需要以编程方式检查每个DLL是否为x86构建(而不是x64或任何CPU)。这可能吗?

195396 次浏览

你可以使用CorFlags CLI工具(例如,C:\Program Files\Microsoft sdk \Windows\v7.0\Bin\CorFlags.exe)来确定程序集的状态,基于它的输出,并将程序集作为二进制资产打开,你应该能够确定你需要在哪里寻找,以确定32位标志是否被设置为1 (x86)或0 (任何CPUx64,取决于PE):

Option    | PE    | 32BIT
----------|-------|---------
x86       | PE32  | 1
Any CPU   | PE32  | 0
x64       | PE32+ | 0

博客文章x64 Development with .NET有一些关于corflags的信息。

更好的是,你可以使用Module.GetPEKind来确定程序集是否为PortableExecutableKindsPE32Plus(64位),Required32Bit(32位和),或ILOnly(任何CPU)以及其他属性。

看看System.Reflection.AssemblyName.GetAssemblyName(string assemblyFile)

您可以从返回的AssemblyName实例中检查程序集元数据:

使用PowerShell:

[36] C:\> [reflection.assemblyname]::GetAssemblyName("${pwd}\Microsoft.GLEE.dll") | fl


Name                  : Microsoft.GLEE
Version               : 1.0.0.0
CultureInfo           :
CodeBase              : file:///C:/projects/powershell/BuildAnalyzer/...
EscapedCodeBase       : file:///C:/projects/powershell/BuildAnalyzer/...
ProcessorArchitecture : MSIL
Flags                 : PublicKey
HashAlgorithm         : SHA1
VersionCompatibility  : SameMachine
KeyPair               :
FullName              : Microsoft.GLEE, Version=1.0.0.0, Culture=neut...

这里,ProcessorArchitecture标识目标平台。

  • Amd64:基于x64架构的64位处理器。
  • 手臂: ARM处理器。
  • IA64:仅支持64位Intel 安腾处理器。
  • MSIL:对于处理器和每字比特数来说是中性的。
  • X86:一个32位的Intel处理器,本机的或在64位平台上的Windows on Windows环境中(WoW64)。
  • 没有一个:未知或未指定的处理器和每字位数的组合。

在本例中,我使用PowerShell来调用该方法。

只是为了澄清,CorFlags.exe是.NET Framework SDK. exe的一部分。我的机器上有开发工具,对我来说,确定DLL是否仅为32位的最简单方法是:

  1. 打开Visual Studio命令提示符(Windows:菜单开始/程序/Microsoft Visual Studio/Visual Studio工具/Visual Studio 2008命令提示符)

  2. CD到包含问题DLL的目录

  3. corflags MyAssembly.dll < / p >

你会得到这样的输出:

Microsoft (R) .NET Framework CorFlags Conversion Tool.  Version  3.5.21022.8
Copyright (c) Microsoft Corporation.  All rights reserved.


Version   : v2.0.50727
CLR Header: 2.5
PE        : PE32
CorFlags  : 3
ILONLY    : 1
32BIT     : 1
Signed    : 0

根据评论,上述标志的解读如下:

  • 任意CPU: PE = PE32, 32BIT = 0
  • x86: PE = PE32, 32BIT = 1
  • 64位:PE = PE32+, 32BIT = 0

检查.NET程序集的目标平台的另一种方法是使用. net Reflector检查程序集…

@ # ~ #€~ !我刚刚意识到新版本不是免费的!因此,更正一下,如果你有一个。net reflector的免费版本,你可以用它来检查目标平台。

你自己写就行了。PE架构的核心自从在Windows 95中实现以来就没有被严重改变过。

下面是一个c#的例子:

    public static ushort GetPEArchitecture(string pFilePath)
{
ushort architecture = 0;
try
{
using (System.IO.FileStream fStream = new System.IO.FileStream(pFilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read))
{
using (System.IO.BinaryReader bReader = new System.IO.BinaryReader(fStream))
{
// Check the MZ signature
if (bReader.ReadUInt16() == 23117)
{
// Seek to e_lfanew.
fStream.Seek(0x3A, System.IO.SeekOrigin.Current);


// Seek to the start of the NT header.
fStream.Seek(bReader.ReadUInt32(), System.IO.SeekOrigin.Begin);


if (bReader.ReadUInt32() == 17744) // Check the PE\0\0 signature.
{
// Seek past the file header,
fStream.Seek(20, System.IO.SeekOrigin.Current);


// Read the magic number of the optional header.
architecture = bReader.ReadUInt16();
}
}
}
}
}
catch (Exception) { /* TODO: Any exception handling you want
to do, personally I just take 0
as a sign of failure */
}


// If architecture returns 0, there has been an error.
return architecture;
}
}

现在的常数是:

0x10B - PE32  format.
0x20B - PE32+ format.

但是用这种方法,它允许新常数的可能性。只要验证你认为合适的返回。

尝试使用CorFlagsReader 来自CodePlex的这个项目。它没有对其他程序集的引用,可以按原样使用。

cfeduke笔记调用GetPEKind的可能性。在PowerShell中进行这个操作可能会很有趣。

例如,这里是可以使用的cmdlet代码:https://stackoverflow.com/a/16181743/64257

或者,在https://stackoverflow.com/a/4719567/64257中注意到“在PowerShell社区扩展中也有Get-PEHeader cmdlet,可用于测试可执行图像。”

[TestMethod]
public void EnsureKWLLibrariesAreAll64Bit()
{
var assemblies = Assembly.GetExecutingAssembly().GetReferencedAssemblies().Where(x => x.FullName.StartsWith("YourCommonProjectName")).ToArray();
foreach (var assembly in assemblies)
{
var myAssemblyName = AssemblyName.GetAssemblyName(assembly.FullName.Split(',')[0] + ".dll");
Assert.AreEqual(ProcessorArchitecture.MSIL, myAssemblyName.ProcessorArchitecture);
}
}

下面是一个批处理文件,它将对当前工作目录和所有子目录中的所有DLL文件和EXE文件运行corflags.exe,解析结果并显示每个的目标体系结构。

根据所使用的corflags.exe的版本,输出中的行项将包括32位<强>或< / >强32 bitreq(和32 bitpref)。输出中包含这两个中的任何一个都是必须检查的关键行项,以区分任何CPUx86。如果你使用的是旧版本的corflags.exe (pre Windows SDK v8.0A),那么只有32位行项会出现在输出中,就像其他人在过去的回答中指出的那样。否则32 bitreq32 bitpref将替换它。

这假设corflags.exe%路径%中。确保这一点的最简单方法是使用开发人员命令提示符。或者你可以从它的默认位置中复制它。

如果下面的批处理文件是针对非托管DLL或EXE文件运行的,它将错误地将其显示为x86,因为Corflags.exe的实际输出将是类似于以下的错误消息:

CF008:指定的文件没有有效的托管头

@echo off


echo.
echo Target architecture for all exes and dlls:
echo.


REM For each exe and dll in this directory and all subdirectories...
for %%a in (.exe, .dll) do forfiles /s /m *%%a /c "cmd /c echo @relpath" > testfiles.txt


for /f %%b in (testfiles.txt) do (
REM Dump corflags results to a text file
corflags /nologo %%b > corflagsdeets.txt


REM Parse the corflags results to look for key markers
findstr /C:"PE32+">nul .\corflagsdeets.txt && (
REM `PE32+` indicates x64
echo %%~b = x64
) || (
REM pre-v8 Windows SDK listed only "32BIT" line item,
REM newer versions list "32BITREQ" and "32BITPREF" line items
findstr /C:"32BITREQ  : 0">nul /C:"32BIT     : 0" .\corflagsdeets.txt && (
REM `PE32` and NOT 32bit required indicates Any CPU
echo %%~b = Any CPU
) || (
REM `PE32` and 32bit required indicates x86
echo %%~b = x86
)
)


del corflagsdeets.txt
)


del testfiles.txt
echo.

一个工具是sigcheck:

sigcheck c:\Windows\winhlp32.exe

输出:

Sigcheck v2.71 - File version and signature viewer
Copyright (C) 2004-2018 Mark Russinovich
Sysinternals - www.sysinternals.com


c:\windows\winhlp32.exe:
Verified:       Signed
Signing date:   20:05 02.05.2022
Publisher:      Microsoft Windows
Company:        Microsoft Corporation
Description:    Windows Winhlp32 Stub
Product:        Microsoft® Windows® Operating System
Prod version:   10.0.19041.1
File version:   10.0.19041.1 (WinBuild.160101.0800)
MachineType:    32-bit

sigcheck -nobanner c:\Windows\HelpPane.exe

输出:

c:\windows\HelpPane.exe:
Verified:       Signed
Signing date:   00:42 23.04.2022
Publisher:      Microsoft Windows
Company:        Microsoft Corporation
Description:    Microsoft Help and Support
Product:        Microsoft® Windows® Operating System
Prod version:   10.0.19041.1151
File version:   10.0.19041.1151 (WinBuild.160101.0800)
MachineType:    64-bit

另一种方法是在DLL上使用Visual Studio工具中的dumpbin,并寻找适当的输出:

dumpbin.exe /HEADERS <your DLL file path>
FILE HEADER VALUE
14C machine (x86)
4 number of sections
5885AC36 time date stamp Mon Jan 23 12:39:42 2017
0 file pointer to symbol table
0 number of symbols
E0 size of optional header
2102 characteristics
Executable
32 bit word machine
DLL

注意:上面的输出是一个32位DLL文件

dumpbin.exe的一个更有用的选项是/EXPORTS。它将显示DLL文件公开的函数

dumpbin.exe /EXPORTS <PATH OF THE DLL FILE>

JetBrains中的DotPeek提供了一个快速简单的方法来查看msil(任何CPU), x86和x64:

DotPeek

一个更通用的方法-使用文件结构来确定位和图像类型:

public static CompilationMode GetCompilationMode(this FileInfo info)
{
if (!info.Exists)
throw new ArgumentException($"{info.FullName} does not exist");


var intPtr = IntPtr.Zero;
try
{
uint unmanagedBufferSize = 4096;
intPtr = Marshal.AllocHGlobal((int)unmanagedBufferSize);


using (var stream = File.Open(info.FullName, FileMode.Open, FileAccess.Read))
{
var bytes = new byte[unmanagedBufferSize];
stream.Read(bytes, 0, bytes.Length);
Marshal.Copy(bytes, 0, intPtr, bytes.Length);
}


// Check DOS header magic number
if (Marshal.ReadInt16(intPtr) != 0x5a4d)
return CompilationMode.Invalid;


// This will get the address for the WinNT header
var ntHeaderAddressOffset = Marshal.ReadInt32(intPtr + 60);


// Check WinNT header signature
var signature = Marshal.ReadInt32(intPtr + ntHeaderAddressOffset);
if (signature != 0x4550)
return CompilationMode.Invalid;


// Determine file bitness by reading magic from IMAGE_OPTIONAL_HEADER
var magic = Marshal.ReadInt16(intPtr + ntHeaderAddressOffset + 24);


var result = CompilationMode.Invalid;
uint clrHeaderSize;
if (magic == 0x10b)
{
clrHeaderSize = (uint)Marshal.ReadInt32(intPtr + ntHeaderAddressOffset + 24 + 208 + 4);
result |= CompilationMode.Bit32;
}
else if (magic == 0x20b)
{
clrHeaderSize = (uint)Marshal.ReadInt32(intPtr + ntHeaderAddressOffset + 24 + 224 + 4);
result |= CompilationMode.Bit64;
}
else return CompilationMode.Invalid;


result |= clrHeaderSize != 0
? CompilationMode.CLR
: CompilationMode.Native;


return result;
}
finally
{
if (intPtr != IntPtr.Zero)
Marshal.FreeHGlobal(intPtr);
}
}

编译模式枚举

[Flags]
public enum CompilationMode
{
Invalid = 0,
Native = 0x1,
CLR = Native << 1,
Bit32 = CLR << 1,
Bit64 = Bit32 << 1
}

带有解释的源代码是在GitHub

我克隆了一个超级方便的工具,它为Windows资源管理器中的程序集添加了一个上下文菜单项,以显示所有可用的信息:

release·tebjan/AssemblyInformation .c下载。

Enter image description here

前面提到的工具的另一种选择是Telerik JustDecompile(免费工具),它将显示程序集名称旁边的信息:

任何或x86或x64信息在Telerik

我喜欢ILSpy工具。它不仅显示了架构,还显示了目标框架:

// linq2db, Version=3.0.0.0, Culture=neutral, PublicKeyToken=e41013125f9e410a
// Global type: <Module>
// Architecture: AnyCPU (64-bit preferred)
// Runtime: v4.0.30319
// This assembly is signed with a strong name key.
// This assembly was compiled using the /deterministic option.
// Hash algorithm: SHA1

因此,可以确定它是net核心 2.1, .NET Framework 4.6或任何其他类型:

目标框架