【问题标题】:How can I determine for which platform an executable is compiled?如何确定可执行文件是为哪个平台编译的?
【发布时间】:2010-09-16 21:52:38
【问题描述】:

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

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

【问题讨论】:

标签: c# powershell cpu-architecture


【解决方案1】:

这是另一种使用 C/C++ 作为独立工具的解决方案,可随时根据您的需要进行调整:

// Fri May 28, 2021 -two

#include <stdio.h>
#include <io.h>
#include <stdint.h>
#include <iostream.h>
using namespace std;

bool queryExeMachineType( const char *filename )
{
    FILE *fp = fopen( filename, "rb" );

    if (fp == NULL)
        return false;

    // DOS header is 64 bytes
    const uint32_t fsize = filelength( fileno( fp ) );
    char magic[ 2 ] = { 0 };
    uint32_t offset = 0;
    uint16_t machine = 0;

    if (fread( magic, 1, 2, fp ) != 2 || magic[ 0 ] != 'M' || magic[ 1 ] != 'Z')
    {
        cerr << "not an executable file" << endl;
        fclose( fp );
        return false;
    }
    fseek( fp, 60, SEEK_SET );
    fread( &offset, 1, 4, fp );

    if (offset >= fsize)
    {
        cerr << "invalid pe offset" << endl;
        fclose( fp );
        return false;
    }
    fseek( fp, offset, SEEK_SET );

    if (fread( magic, 1, 2, fp ) != 2 || magic[ 0 ] != 'P' || magic[ 1 ] != 'E')
    {
        cerr << "not a pe executable" << endl;
        fclose( fp );
        return false;
    }
    fread( magic, 1, 2, fp );
    fread( &machine, 1, 2, fp );

    switch (machine)
    {
        case 0x014c:
            cout << "i386" << endl;  // x86
            break;

        case 0x8664:
            cout << "amd64" << endl; // x86_64
            break;

       case 0x0200:
            cout << "ia64" << endl;  // itanium
            break;

        default:
            cerr << "unknown machine 0x" << hex << machine << endl;
            break;
    }
    fclose( fp );
    return true;
}

int main( int argc, char *argv[] )
{
    const char *fn = (argc > 1) ? argv[ 1 ] : "test.dll";

    if (queryExeMachineType( fn ))
        cerr << "succeeded" << endl;
    else
        cerr << "failed" << endl;

    return 0;
}

【讨论】:

    【解决方案2】:

    这是一个写出文件头信息的 C++ MFC 控制台应用程序。您可以检查机器类型(IMAGE_FILE_HEADER 机器成员)或特性中的 IMAGE_FILE_32BIT_MACHINE 标志,以查看文件是为哪个平台构建的。有关结构的更多信息,请参见 WinNT.h。

    #include "stdafx.h"
    
    int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
    {
      int nRetCode = 0;
      int nrd;
    
      IMAGE_DOS_HEADER idh;
      IMAGE_NT_HEADERS inth;
      IMAGE_FILE_HEADER ifh;
    
      // initialize MFC and print and error on failure
      if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
      {
        _tprintf(_T("Fatal Error: MFC initialization failed\n"));
        nRetCode = 1;
        return 1;
      }
      if (argc != 2) {
        _ftprintf(stderr, _T("Usage: %s filename\n"), argv[0]);
        return 1;
      }
      // Try to open the file
      CFile ckf;
      CFileException ex;
      DWORD flags = CFile::modeRead | CFile::shareDenyNone;
    
      if (!ckf.Open(argv[1], flags, &ex)) {
        TCHAR szError[1024];
        ex.GetErrorMessage(szError, 1024);
        _tprintf_s(_T("Couldn't open file: %1024s"), szError);
        return 2;
      }
    
      // The following is adapted from:
      // https://stackoverflow.com/questions/495244/how-can-i-test-a-windows-dll-file-to-determine-if-it-is-32-bit-or-64-bit
      // https://stackoverflow.com/questions/46024914/how-to-parse-exe-file-and-get-data-from-image-dos-header-structure-using-c-and
      // Seek to beginning of file
      ckf.Seek(0, CFile::begin);
    
      // Read DOS header
      int nbytes = sizeof(IMAGE_DOS_HEADER);
      nrd = ckf.Read(&idh, nbytes);
    
      // The idh.e_lfanew member is the offset to the NT_HEADERS structure
      ckf.Seek(idh.e_lfanew, CFile::begin);
    
      // Read NT headers
      nbytes = sizeof(IMAGE_NT_HEADERS);
      nrd = ckf.Read(&inth, nbytes);
    
      ifh = inth.FileHeader;
    
      _ftprintf(stdout, _T("File machine type: "));
      switch (ifh.Machine) {
         case IMAGE_FILE_MACHINE_I386:
           _ftprintf(stdout, _T("I386\n"));
           break;
         case IMAGE_FILE_MACHINE_IA64:
           _ftprintf(stdout, _T("IA64\n"));
           break;
         case IMAGE_FILE_MACHINE_AMD64:
           _ftprintf(stdout, _T("AMD64\n"));
           break;
         default:
           _ftprintf(stdout, _T("Unknown (%d = %X)\n"), ifh.Machine, ifh.Machine);
           break;
      }
    
      // Write characteristics (see WinNT.h)
      _ftprintf(stdout, _T("Characteristics:\n"));
      _ftprintf(stdout, _T("RELOCS_STRIPPED Relocation info stripped from file: %c\n"),
        (ifh.Characteristics & IMAGE_FILE_RELOCS_STRIPPED ? _T('Y') : _T('N')));
      _ftprintf(stdout, _T("EXECUTABLE_IMAGE File is executable  (i.e. no unresolved externel references): %c\n"),
        (ifh.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE ? _T('Y') : _T('N')));
      _ftprintf(stdout, _T("LINE_NUMS_STRIPPED Line nunbers stripped from file: %c\n"),
        (ifh.Characteristics & IMAGE_FILE_LINE_NUMS_STRIPPED ? _T('Y') : _T('N')));
      _ftprintf(stdout, _T("LOCAL_SYMS_STRIPPED Local symbols stripped from file: %c\n"),
        (ifh.Characteristics & IMAGE_FILE_LOCAL_SYMS_STRIPPED ? _T('Y') : _T('N')));
      _ftprintf(stdout, _T("AGGRESIVE_WS_TRIM Agressively trim working set: %c\n"),
        (ifh.Characteristics & IMAGE_FILE_AGGRESIVE_WS_TRIM ? _T('Y') : _T('N')));
      _ftprintf(stdout, _T("LARGE_ADDRESS_AWARE App can handle >2gb addresses: %c\n"),
        (ifh.Characteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE ? _T('Y') : _T('N')));
      _ftprintf(stdout, _T("BYTES_REVERSED_LO Bytes of machine word are reversed: %c\n"),
        (ifh.Characteristics & IMAGE_FILE_BYTES_REVERSED_LO ? _T('Y') : _T('N')));
      _ftprintf(stdout, _T("32BIT_MACHINE 32 bit word machine: %c\n"),
        (ifh.Characteristics & IMAGE_FILE_32BIT_MACHINE ? _T('Y') : _T('N')));
      _ftprintf(stdout, _T("DEBUG_STRIPPED Debugging info stripped from file in .DBG file: %c\n"),
        (ifh.Characteristics & IMAGE_FILE_DEBUG_STRIPPED ? _T('Y') : _T('N')));
      _ftprintf(stdout, _T("REMOVABLE_RUN_FROM_SWAP If Image is on removable media, copy and run from the swap file: %c\n"),
        (ifh.Characteristics & IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP ? _T('Y') : _T('N')));
      _ftprintf(stdout, _T("NET_RUN_FROM_SWAP If Image is on Net, copy and run from the swap file: %c\n"),
        (ifh.Characteristics & IMAGE_FILE_NET_RUN_FROM_SWAP ? _T('Y') : _T('N')));
      _ftprintf(stdout, _T("SYSTEM System File: %c\n"),
        (ifh.Characteristics & IMAGE_FILE_SYSTEM ? _T('Y') : _T('N')));
      _ftprintf(stdout, _T("DLL File is a DLL: %c\n"),
        (ifh.Characteristics & IMAGE_FILE_DLL ? _T('Y') : _T('N')));
      _ftprintf(stdout, _T("UP_SYSTEM_ONLY File should only be run on a UP machine: %c\n"),
        (ifh.Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY ? _T('Y') : _T('N')));
      _ftprintf(stdout, _T("BYTES_REVERSED_HI Bytes of machine word are reversed: %c\n"),
        (ifh.Characteristics & IMAGE_FILE_BYTES_REVERSED_HI ? _T('Y') : _T('N')));
    
    
      ckf.Close();
    
      return nRetCode;
    }
    

    【讨论】:

      【解决方案3】:

      dumpbin.exe 位于 Visual Studio 的 bin 目录下,适用于 .lib.dll

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

      【讨论】:

        【解决方案4】:

        这是一个 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;
        }
        

        【讨论】:

          【解决方案5】:

          根据这个post,你可以检查一个DLL或EXE是32还是64,用记事本打开它并在开头寻找“PE”,如果下一个字母是“ L"平台为32位,字母为"D"平台为64位。

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

          【讨论】:

            【解决方案6】:

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

            // 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;
            }
            

            如何使用:

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

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

            【讨论】:

              【解决方案7】:

              如果您安装了 Visual Studio,则可以使用 dumpbin.exePowerShell Community Extensions 中还有 Get-PEHeader cmdlet,可用于测试可执行映像。

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

              Get-PEHeader 会将 DLL 报告为 PE32PE32+

              【讨论】:

              • 太棒了。 Get-PEHeader 为您做了(目前)评价最高的两个答案,只是为您提供了编写自己的信息。 PSCX FTW。
              • stackoverflow.com/a/16181743/64257 在 Stack Overflow 上也有类似(可能功能减少)cmdlet 的代码。
              • 对于那些(像我一样)懒得看手册的人:dumpbin /headers &lt;executable&gt; | findstr machine
              • 您也可以使用corflags.exe &lt;dll path&gt;。更多详情:stackoverflow.com/questions/18608785/…
              【解决方案8】:

              (来自另一个Q,已被删除)

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

              正如另一个回复所指出的,向我展示了偏移量的 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;
              }
              

              【讨论】:

                【解决方案9】:

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

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

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

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

                有一个MSDN magazine article 可以帮助您开始。

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

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

                【讨论】:

                • 在我的情况下,这比有用的信息更多。我的错,不是你的。 :) 我需要一些能让我更接近的东西。
                • 对不起老伙计,我不熟悉 powershell,但我希望我能让你走上正确的道路。查看我的编辑。
                • 我将在下周尝试一下,最终可能会将您的答案标记为“答案”。
                • 这似乎有点不准确。 EXE 文件中偏移量 60 处的 4 个字节是 PE 头相对于文件开头的偏移量。因此,正确答案是:读取 2 个字节,检查它们是否等于 MZ,跳转到偏移量 60,读取 4 个字节,解释为偏移量并跳转到那里,读取四个字节并与 PE\0\0(即 0x00004550)进行比较.接下来的 2 个字节是如上所述的机器 ID。
                【解决方案10】:

                我可以提供一个link to some C# code 来访问 IMAGE_FILE_HEADER,我认为它可以(很容易)编译成一个 PowerShell cmdlet。我有理由确定您不能直接在 PowerShell 脚本中使用该方法,因为它缺少指针和 PInvoke 功能。

                但是,您现在应该能够使用您对 PE 标头格式的广泛了解 ;-) 来“直接”找到正确的字节并找出答案。这在 PowerShell 脚本中工作,您应该能够将 this C# code from Tasos' blog 转换为脚本。代码不是我的,我就不在这里重复了。

                【讨论】:

                【解决方案11】:
                Assembly assembly = Assembly.LoadFile(Path.GetFullPath("ConsoleApplication1.exe"));
                Module manifestModule = assembly.ManifestModule;
                PortableExecutableKinds peKind;
                ImageFileMachine machine;
                manifestModule.GetPEKind(out peKind, out machine);
                

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

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

                【讨论】:

                • 如果可以加载目标二进制文件,这会很好。在我的情况下,有一个需要 VCRedist 的 .NET dll,我试图找出对应于 .NET dll 的哪个(x86 或 x64)。但是,从逻辑上和具有讽刺意味的是,如果没有安装 VCRedist,我无法加载此 .NET dll,因此无法检测到它需要哪个(使用此方法)。
                【解决方案12】:

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

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2016-06-27
                  • 2016-05-08
                  • 2023-03-10
                  • 2015-03-26
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-08-17
                  相关资源
                  最近更新 更多