如何确定编译可执行文件的平台?

时间:2008-10-13 15:16:55

标签: c# powershell cpu-architecture

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

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

12 个答案:

答案 0 :(得分:37)

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

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

Get-PEHeader会将DLL报告为PE32PE32+

答案 1 :(得分:24)

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

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

探索PE标题(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;
}

答案 2 :(得分:11)

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

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

Alternativaly您可以检查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可能会对您有所帮助。您将二进制文件读作文件:检查前两个字节是否为“MZ”,然后跳过接下来的58个字节,并将60字节的魔术32位值读入图像(对于PE可执行文件,它等于0x00004550)。以下字节为this header,其前2个字节告诉您二进制设计的机器(0x8664 = x86_64,0x0200 = IA64,0x014c = i386)。

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

答案 3 :(得分:8)

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

目标机器应该在机器中。

但这只适用于.NET程序集。

答案 4 :(得分:2)

根据这个post,您可以通过 NotePad 打开它来检查DLL或EXE是32还是64,并在开头查找“PE”,如果下一个字母是“L”平台是32位,它的字母是“D”平台是64位。

我在我的dll上尝试过,看起来很准确。

答案 5 :(得分:1)

我可以提供link to some C# code来访问IMAGE_FILE_HEADER,我认为可以(轻松)编译成PowerShell cmdlet。我有理由相信你不能直接在PowerShell脚本中使用该方法,因为它缺少指针和PInvoke功能。

但是,你应该能够使用你现在广泛的PE头格式知识;-)直接“直接”到正确的字节并弄清楚。此 将在PowerShell脚本中运行,您应该只需将this C# code from Tasos' blog转换为脚本即可。我不打算在这里重复代码,因为它不是我的。

答案 6 :(得分:1)

这是一个写出文件头信息的 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;
}

答案 7 :(得分:0)

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

答案 8 :(得分:0)

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

// 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'是跨平台(所有平台),因此它是获取此信息的良好基础。这些信息可能还有其他来源,但它们似乎在谷歌ca-ca中嵌套,需要在Google-fu中找到第10条黑带。

答案 9 :(得分:0)

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

答案 10 :(得分:0)

dumpbin.exe在Visual Studio的bin目录下可用于.lib.dll

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

答案 11 :(得分:0)

这是另一种使用 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;
}