c# - studio - ¿Cómo puedo determinar para qué plataforma se compila un ejecutable?
crear.exe en visual studio 2017 (10)
(de otra Q, ya que se eliminó)
Tipo de máquina: Este es un pequeño código rápido basado en algunos que obtienen la marca de tiempo del enlazador. Esto está en el mismo encabezado, y parece funcionar: devuelve I386 cuando se compila -cualquier CPU- y x64 cuando se compila con eso como plataforma de destino.
La entrada del blog Exploring PE Headers (K. Stanton, MSDN) que me mostró el desplazamiento, como se observó en otra respuesta.
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;
}
Tengo la necesidad de trabajar con ejecutables de Windows que están hechos para x86, x64 e IA64. Me gustaría descubrir la plataforma mediante programación examinando los archivos ellos mismos.
Mi idioma de destino es PowerShell, pero un ejemplo de C # servirá. Si falla alguno de ellos, si conoce la lógica requerida, sería genial.
Aquí está mi propia implementación de esto, que tiene varios controles más en el lugar y siempre devuelve un resultado.
// 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;
}
Cómo utilizar :
string myfile = @"c:/windows/explorer.exe"; // the file
FilePEType pe = GetFilePE( myfile );
System.Diagnostics.WriteLine( pe.ToString() );
Para los valores enum utilizados aquí, se obtuvieron de pe.go La razón por la que esto funciona es que para cada distribución binaria de ''go'' debe tener el indicador correcto en el ensamblado para permitirle pasar los sistemas operativos ''¿puede ejecutar aquí?'' comprobar. Como ''ir'' es multiplataforma (todas las plataformas), es una buena base para obtener esta información. Probablemente haya otras fuentes para esta información, pero parecen estar anidadas hasta la rodilla en google ca-ca que requieren un cinturón negro de décimo dan en Google-fu para localizar.
Aquí hay una implementación 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;
}
De acuerdo con esta post , puede verificar si un archivo DLL o EXE tiene 32 o 64 al abrirlo con NotePad y buscar "PE" al principio, si la siguiente letra es "L" la plataforma es de 32 bits, es la letra es "D" la plataforma es de 64 bits.
Lo probé en mi dlls y parece ser exacto.
Necesita la función GetBinaryType win32. Esto devolverá las partes relevantes del ejecutable de formato PE.
Normalmente, obtendrá SCS_32BIT_BINARY o SCS_64BIT_BINARY en el campo BinaryType,
Alternativamente, puede verificar el formato PE en sí para ver para qué arquitectura se compila el ejecutable.
El campo IMAGE_FILE_HEADER.Machine tendrá "IMAGE_FILE_MACHINE_IA64" establecido para los binarios IA64, IMAGE_FILE_MACHINE_I386 para 32 bits y IMAGE_FILE_MACHINE_AMD64 para 64 bits (es decir, x86_64).
Hay un artículo de la revista MSDN para ayudarlo a comenzar.
Adición: This puede ayudarlo un poco más. Lees el binario como un archivo: comprueba los primeros 2 bytes, di "MZ", luego omite los siguientes 58 bytes y lee el valor mágico de 32 bits a 60 bytes en la imagen (que equivale a 0x00004550 para ejecutables PE). Los siguientes bytes son este encabezado , los primeros 2 bytes de los cuales le indican para qué máquina está diseñado el binario (0x8664 = x86_64, 0x0200 = IA64, 0x014c = i386).
(resumen ejecutivo: lea los bytes 65 y 66 del archivo para obtener el tipo de imagen)
Puedo ofrecer un enlace a algún código de C # para acceder a IMAGE_FILE_HEADER, que creo que podría (fácilmente) compilarse en un cmdlet de PowerShell. Estoy razonablemente seguro de que no puede usar ese método directamente en el script de PowerShell, ya que carece de punteros y capacidad de PInvoke.
Sin embargo, debería poder utilizar su extenso conocimiento del formato de encabezado PE ;-) para simplemente ir "directo" a los bytes correctos y resolverlo. Esto funcionará en el script de PowerShell, y debería poder convertir este código C # del blog de Tasos en script. No me molestaré en repetir el código aquí porque no es mío.
Si tiene instalado Visual Studio, puede usar dumpbin.exe
. También está el cmdlet Get-PEHeader
en las extensiones de comunidad de PowerShell que se puede usar para probar imágenes ejecutables.
Dumpbin informará DLL como machine (x86)
o machine (x64)
Get-PEHeader informará las DLL como PE32
o PE32+
Unix OS tiene una utilidad llamada "archivo" que identifica los archivos. Las reglas para identificar se guardan en un archivo de descripción llamado "magia". Podría intentar con el archivo para ver si es capaz de identificar sus archivos correctamente y tomar las reglas apropiadas del archivo mágico.
dumpbin.exe
disponible en el directorio bin
de Visual Studio funciona tanto para .lib
como para .dll
dumpbin.exe /headers *.dll |findstr machine
dumpbin.exe /headers *.lib |findstr machine
Assembly assembly = Assembly.LoadFile(Path.GetFullPath("ConsoleApplication1.exe"));
Module manifestModule = assembly.ManifestModule;
PortableExecutableKinds peKind;
ImageFileMachine machine;
manifestModule.GetPEKind(out peKind, out machine);
La máquina objetivo debería estar en la máquina.
Sin embargo, eso solo funcionará con los ensamblados de .NET.