C# 如何确定为哪个平台编译了可执行文件?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/197951/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 17:40:02  来源:igfitidea点击:

How can I determine for which platform an executable is compiled?

c#powershellcpu-architecture

提问by halr9000

I have a need to work with Windows executables which are made for x86, x64, and IA64. I'd like to programmatically figure out the platform by examining the files themselves.

我需要使用为 x86、x64 和 IA64 制作的 Windows 可执行文件。我想通过检查文件本身以编程方式找出平台。

My target language is PowerShell but a C# example will do. Failing either of those, if you know the logic required that would be great.

我的目标语言是 PowerShell,但 C# 示例也可以。如果您知道所需的逻辑,那其中任何一个都失败了,那就太好了。

采纳答案by Andrew Backer

(from another Q, since removed)

(来自另一个 Q,自删除以来)

Machine type: This is a quick little bit of code I based on some that gets the linker timestamp. This is in the same header, and it seems to work - it returns I386 when compiled -any cpu-, and x64 when compiled with that as the target platform.

机器类型:这是我基于一些获取链接器时间戳的快速代码。这是在同一个头文件中,它似乎可以工作 - 编译时返回 I386 -any cpu- 和 x64 编译时将其作为目标平台。

The Exploring PE Headers (K. Stanton,MSDN) blog entry that showed me the offset, as another response noted.

正如另一个回复指出的那样,Exploring PE Headers (K. Stanton, MSDN) 博客条目向我展示了偏移量。

public enum MachineType {
    Native = 0, I386 = 0x014c, Itanium = 0x0200, x64 = 0x8664
}

public static MachineType GetMachineType(string fileName)
{
    const int PE_POINTER_OFFSET = 60;            
    const int MACHINE_OFFSET = 4;
    byte[] data = new byte[4096];
    using (Stream s = new FileStream(fileName, FileMode.Open, FileAccess.Read)) {
        s.Read(data, 0, 4096);
    }
    // dos header is 64 bytes, last element, long (4 bytes) is the address of the PE header
    int PE_HEADER_ADDR = BitConverter.ToInt32(data, PE_POINTER_OFFSET);
    int machineUint = BitConverter.ToUInt16(data, PE_HEADER_ADDR + MACHINE_OFFSET);
    return (MachineType)machineUint;
}

回答by Sec

Unix OS have a utility called "file" which identifies files. The rules for identifying are kept in a description file called "magic". You could try file to see if it is able to identify your files correctly and grab the appropriate rules out of the magic file.

Unix OS 有一个名为“file”的实用程序,用于标识文件。识别规则保存在一个名为“magic”的描述文件中。您可以尝试 file 以查看它是否能够正确识别您的文件并从魔术文件中获取适当的规则。

回答by ICR

Assembly assembly = Assembly.LoadFile(Path.GetFullPath("ConsoleApplication1.exe"));
Module manifestModule = assembly.ManifestModule;
PortableExecutableKinds peKind;
ImageFileMachine machine;
manifestModule.GetPEKind(out peKind, out machine);

The target machine should then be in machine.

然后目标机器应该在机器中。

That'll only work with .NET assemblies though.

不过,这只适用于 .NET 程序集。

回答by gbjbaanb

You need the GetBinaryType win32 function. This will return the relevant parts of the PE-format executable.

您需要 GetBinaryType win32 函数。这将返回 PE 格式可执行文件的相关部分。

Typically, you'll get either SCS_32BIT_BINARY or SCS_64BIT_BINARY in the BinaryType field,

通常,您将在 BinaryType 字段中获得 SCS_32BIT_BINARY 或 SCS_64BIT_BINARY,

Alternativaly you can check the PE format itself to see what architecture the executable is compiled for.

或者,您可以检查 PE 格式本身以查看可执行文件的编译架构。

The IMAGE_FILE_HEADER.Machine field will have "IMAGE_FILE_MACHINE_IA64" set for IA64 binaries, IMAGE_FILE_MACHINE_I386 for 32-bit and IMAGE_FILE_MACHINE_AMD64 for 64-bit (ie x86_64).

IMAGE_FILE_HEADER.Machine 字段将为 IA64 二进制文件设置“IMAGE_FILE_MACHINE_IA64”,为 32 位设置 IMAGE_FILE_MACHINE_I386,为 64 位(即 x86_64)设置 IMAGE_FILE_MACHINE_AMD64。

There's a MSDN magazine articleto help you get going.

有一篇MSDN 杂志文章可帮助您开始工作。

Addendum: Thismay help you a little more. You read the binary as a file: check the first 2 bytes say "MZ", then skip the next 58 bytes and read the magic 32-bit value at 60 bytes into the image (which equals 0x00004550 for PE executables). The following bytes are this header, the first 2 bytes of which tell you which machine the binary is designed for (0x8664 = x86_64, 0x0200 = IA64, 0x014c = i386).

附录:可能会对您有所帮助。您将二进制文件作为文件读取:检查前 2 个字节说“MZ”,然后跳过接下来的 58 个字节并将 60 个字节处的神奇 32 位值读入映像(对于 PE 可执行文件等于 0x00004550)。下面的字节是这个 header,其中的前 2 个字节告诉你二进制文件是为哪台机器设计的 (0x8664 = x86_64, 0x0200 = IA64, 0x014c = i386)。

(executive summary: read bytes 65 and 66 of the file to get the image type)

(执行摘要:读取文件的第 65 和 66 字节以获取图像类型)

回答by Jaykul

I can offer a link to some C# codefor accessing the IMAGE_FILE_HEADER, which I think could be (easily) compiled into a PowerShell cmdlet. I'm reasonably sure you can't use that method in PowerShell script directly, since it lacks pointers and PInvoke capability.

我可以提供一些 C# 代码链接以访问 IMAGE_FILE_HEADER,我认为可以(轻松)将其编译为 PowerShell cmdlet。我有理由确定您不能直接在 PowerShell 脚本中使用该方法,因为它缺少指针和 PInvoke 功能。

However, you should be able to use your by now extensive knowledge of the PE header format ;-) to just go "straight" to the right bytes and figure it out. This willwork in PowerShell script, and you should be able to just convert this C# code from Tasos' blogto script. I won't bother repeating the code here since it's not mine.

但是,您应该能够使用您现在对 PE 标头格式的广泛了解 ;-) 来“直接”找到正确的字节并弄清楚。这在 PowerShell 脚本中工作,您应该能够将Tasos 博客中的此 C# 代码转换为脚本。我不会在这里重复代码,因为它不是我的。

回答by Keith Hill

If you have Visual Studio installed you can use dumpbin.exe. There's also the Get-PEHeadercmdlet in the PowerShell Community Extensionsthat can be used to test for executable images.

如果您安装了 Visual Studio,则可以使用dumpbin.exe. PowerShell Community Extensions中还有一个Get-PEHeadercmdlet ,可用于测试可执行映像。

Dumpbin will report DLLs as machine (x86)or machine (x64)

Dumpbin 会将 DLL 报告为machine (x86)machine (x64)

Get-PEHeader will report DLLs as either PE32or PE32+

Get-PEHeader 会将 DLL 报告为PE32PE32+

回答by Kraang Prime

Here is my own implementation of this which has several more checks in place and always returns a result.

这是我自己的实现,它有更多的检查并且总是返回一个结果。

// the enum of known pe file types
public enum FilePEType : ushort
{
    IMAGE_FILE_MACHINE_UNKNOWN = 0x0,
    IMAGE_FILE_MACHINE_AM33 = 0x1d3,
    IMAGE_FILE_MACHINE_AMD64 = 0x8664,
    IMAGE_FILE_MACHINE_ARM = 0x1c0,
    IMAGE_FILE_MACHINE_EBC = 0xebc,
    IMAGE_FILE_MACHINE_I386 = 0x14c,
    IMAGE_FILE_MACHINE_IA64 = 0x200,
    IMAGE_FILE_MACHINE_M32R = 0x9041,
    IMAGE_FILE_MACHINE_MIPS16 = 0x266,
    IMAGE_FILE_MACHINE_MIPSFPU = 0x366,
    IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466,
    IMAGE_FILE_MACHINE_POWERPC = 0x1f0,
    IMAGE_FILE_MACHINE_POWERPCFP = 0x1f1,
    IMAGE_FILE_MACHINE_R4000 = 0x166,
    IMAGE_FILE_MACHINE_SH3 = 0x1a2,
    IMAGE_FILE_MACHINE_SH3DSP = 0x1a3,
    IMAGE_FILE_MACHINE_SH4 = 0x1a6,
    IMAGE_FILE_MACHINE_SH5 = 0x1a8,
    IMAGE_FILE_MACHINE_THUMB = 0x1c2,
    IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169,
}

// pass the path to the file and check the return
public static FilePEType GetFilePE(string path)
{
    FilePEType pe = new FilePEType();
    pe = FilePEType.IMAGE_FILE_MACHINE_UNKNOWN;
    if(File.Exists(path))
    {
        using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
            byte[] data = new byte[4096];
            fs.Read(data, 0, 4096);
            ushort result = BitConverter.ToUInt16(data, BitConverter.ToInt32(data, 60) + 4);
            try
            {
                pe = (FilePEType)result;
            } catch (Exception)
            {
                pe = FilePEType.IMAGE_FILE_MACHINE_UNKNOWN;
            }
        }
    }
    return pe;
}

How to use :

如何使用 :

string myfile = @"c:\windows\explorer.exe"; // the file
FilePEType pe = GetFilePE( myfile );

System.Diagnostics.WriteLine( pe.ToString() );

For the enum values used here, they were obtained from pe.go. The reason why this works is that for each binary ditribution of 'go' must have the correct flag in the assembly to let it pass the operating systems 'can you run here ?' check. Since 'go' is cross platform (all platforms), it is a good base to get this information. There are probably other sources for this information, but they seem to be nested knee-deep in google ca-ca requiring a 10th dan black-belt in Google-fu to locate.

对于此处使用的枚举值,它们是从pe.go获得的。这样做的原因是,对于“go”的每个二进制分发,程序集中必须有正确的标志,才能让它通过操作系统“你能在这里运行吗?” 查看。由于“go”是跨平台的(所有平台),因此它是获取此信息的良好基础。此信息可能还有其他来源,但它们似乎嵌套在 google ca-ca 中,需要 Google-fu 中的第 10 段黑带才能定位。

回答by mrelva

According to this post, you can check if a DLL or EXE is 32 or 64 by opening it with NotePadand looking for "PE" at the beginning, if the next letter is "L" the platform is 32-bit, it the letter is "D" the platform is 64bit.

根据这篇文章,您可以通过用记事本打开它并在开头查找“PE”来检查 DLL 或 EXE 是 32 位还是 64 位,如果下一个字母是“L”,则平台为 32 位,则为该字母是“D”平台是64位。

I tried it on my dlls and it seems to be accurate.

我在我的 dll 上试​​过它,它似乎是准确的。

回答by Jiminion

Here is an implementation in C.

这是 C 中的实现。

// Determines if DLL is 32-bit or 64-bit.
#include <stdio.h>

int sGetDllType(const char *dll_name);

int main()
{
  int ret;
  const char *fname = "sample_32.dll";
  //const char *fname = "sample_64.dll";
  ret = sGetDllType(fname);
}

static int sGetDllType(const char *dll_name) {
  const int PE_POINTER_OFFSET = 60;
  const int MACHINE_TYPE_OFFSET = 4;
  FILE *fp;
  unsigned int ret = 0;
  int peoffset;
  unsigned short machine;

  fp = fopen(dll_name, "rb");
  unsigned char data[4096];
  ret = fread(data, sizeof(char), 4096, fp);
  fclose(fp);
  if (ret == 0)
    return -1;

  if ( (data[0] == 'M') && (data[1] == 'Z') ) {
    // Initial magic header is good
    peoffset = data[PE_POINTER_OFFSET + 3];
    peoffset = (peoffset << 8) + data[PE_POINTER_OFFSET + 2];
    peoffset = (peoffset << 8) + data[PE_POINTER_OFFSET + 1];
    peoffset = (peoffset << 8) + data[PE_POINTER_OFFSET];

    // Check second header
    if ((data[peoffset] == 'P') && (data[peoffset + 1] == 'E')) {
      machine = data[peoffset + MACHINE_TYPE_OFFSET];
      machine = (machine)+(data[peoffset + MACHINE_TYPE_OFFSET + 1] << 8);

      if (machine == 0x014c)
        return 32;
      if (machine == 0x8664)
        return 64;

      return -1;
    }
    return -1;
  }
  else
    return -1;
}

回答by Saad Saadi

dumpbin.exeavailable under bindirectory of Visual Studio works for both .liband .dll

dumpbin.exe可在binVisual Studio中的目录同时适用于.lib.dll

 dumpbin.exe /headers *.dll |findstr machine
 dumpbin.exe /headers *.lib |findstr machine