web-dev-qa-db-fra.com

Comment puis-je déterminer pour quelle plate-forme un exécutable est compilé?

J'ai besoin de travailler avec des exécutables Windows conçus pour x86, x64 et IA64. Je voudrais programmer par programme la plate-forme en examinant les fichiers eux-mêmes.

Ma langue cible est PowerShell, mais un exemple en C # suffira. À défaut de l'un ou l'autre, si vous connaissez la logique requise, ce serait formidable.

47
halr9000

(d'un autre Q, depuis retiré)

Type de machine: Ceci est un petit morceau de code rapide basé sur certains qui obtient le timestamp de l'éditeur de liens. Cela se trouve dans le même en-tête et cela semble fonctionner: il renvoie I386 lorsqu'il est compilé -any cpu- et x64 lorsqu'il est compilé avec cela comme plate-forme cible.

L'entrée de blog Exploring PE Headers (K. Stanton, MSDN) qui m'a montré le décalage, comme l'a noté une autre réponse.

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;
}
23
Andrew Backer

Si Visual Studio est installé, vous pouvez utiliser dumpbin.exe. Il existe également la cmdlet Get-PEHeader dans PowerShell Community Extensions qui peut être utilisée pour tester les images exécutables.

Dumpbin signalera les DLL sous la forme machine (x86) ou machine (x64)

Get-PEHeader signalera les DLL en tant que PE32 ou PE32+

36
Keith Hill

Vous avez besoin de la fonction GetBinaryType win32. Cela renverra les parties pertinentes de l'exécutable au format PE.

En règle générale, vous obtiendrez soit SCS_32BIT_BINARY, soit SCS_64BIT_BINARY dans le champ BinaryType. 

Alternativement, vous pouvez vérifier le format PE lui-même pour voir l’architecture pour laquelle l’exécutable est compilé.

Le champ IMAGE_FILE_HEADER.Machine aura "IMAGE_FILE_MACHINE_IA64" défini pour les binaires IA64, IMAGE_FILE_MACHINE_I386 pour 32 bits et IMAGE_FILE_MACHINE_AMD64 pour 64 bits (x86_64).

Il existe un article de magazine MSDN pour vous aider à démarrer.

Addendum: Ceci peut vous aider un peu plus. Vous lisez le fichier binaire sous forme de fichier: vérifiez les 2 premiers octets avec la mention "MZ", puis ignorez les 58 octets suivants et lisez la valeur magique 32 bits à 60 octets dans l'image (ce qui correspond à 0x00004550 pour les exécutables PE). Les octets suivants sont cet en-tête , les 2 premiers octets vous indiquant pour quelle machine le binaire est conçu (0x8664 = x86_64, 0x0200 = IA64, 0x014c = i386).

(résumé: lisez les octets 65 et 66 du fichier pour obtenir le type d'image)

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

La machine cible doit alors être dans la machine.

Cela ne fonctionnera que sur les assemblys .NET.

8
ICR

Selon ce post , vous pouvez vérifier si un DLL ou EXE est 32 ou 64 en l’ouvrant avec NotePad et en cherchant "PE" au début, si la lettre suivante est "L" la plate-forme est 32 bits, la lettre est "D" la plate-forme est 64 bits.

Je l'ai essayé sur mes dll et cela semble être précis.

2
mrelva

Je peux proposer un lien vers du code C # pour accéder à IMAGE_FILE_HEADER, qui, je pense, pourrait être (facilement) compilé dans une applet de commande PowerShell. Je suis pratiquement sûr que vous ne pouvez pas utiliser directement cette méthode dans le script PowerShell, car elle manque de pointeurs et de capacité PInvoke.

Cependant, vous devriez être en mesure d'utiliser votre connaissance approfondie du format d'en-tête PE ;-) pour aller "directement" aux octets de droite et comprendre. Ceci fonctionnera dans le script PowerShell et vous devriez pouvoir simplement convertir ce code C # du blog de Tasos en script. Je ne me dérangerai pas de répéter le code ici car ce n'est pas le mien.

1
Jaykul

dumpbin.exe disponible dans le répertoire bin de Visual Studio fonctionne à la fois pour .lib et .dll 

 dumpbin.exe /headers *.dll |findstr machine
 dumpbin.exe /headers *.lib |findstr machine
0
Saad Saadi

Les systèmes d'exploitation Unix ont un utilitaire appelé "fichier" qui identifie les fichiers. Les règles d'identification sont conservées dans un fichier de description appelé "magie". Vous pouvez essayer fichier pour voir s'il est capable d'identifier correctement vos fichiers et récupérer les règles appropriées dans le fichier magique.

0
Sec

Voici une implémentation en 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;
}
0
Jiminion

Voici ma propre implémentation de ce qui a plusieurs vérifications en place et retourne toujours un résultat.

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

Comment utiliser :

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

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

Pour les valeurs enum utilisées ici, elles ont été obtenues à partir de pe.go . La raison pour laquelle cela fonctionne est que, pour chaque distribution binaire de «go», le drapeau correct doit être défini dans l’Assemblée afin de lui permettre de passer sous le système d’exploitation «pouvez-vous exécuter ici? vérifier. Puisque «aller» est multiplateforme (toutes les plateformes), c'est une bonne base pour obtenir cette information. Il existe probablement d'autres sources pour ces informations, mais elles semblent être imbriquées jusqu'aux goûts de google ca-ca, ce qui nécessite une ceinture noire 10e dan dans Google-fu à localiser. 

0
Kraang Prime