¿Cómo puedo determinar si un ensamblado .NET se creó para x86 o x64?

327

Tengo una lista arbitraria de ensamblados .NET.

Necesito verificar mediante programación si cada DLL se creó para x86 (a diferencia de x64 o Any CPU). es posible?

Judá Gabriel Himango
fuente
2
También es posible que desee consultar este: check-if-unmanaged-dll-is-32-bit-or-64-bit .
Matt
2
En una versión posterior de CorFlags, correspondiente a .NET 4.5, "32BIT" fue reemplazado por "32BITREQ" y "32BITPREF". .
Peter Mortensen

Respuestas:

281

Mirar System.Reflection.AssemblyName.GetAssemblyName(string assemblyFile)

Puede examinar los metadatos de ensamblaje desde la instancia de AssemblyName devuelta:

Usando PowerShell :

[36] C: \> [reflection.assemblyname] :: GetAssemblyName ("$ {pwd} \ Microsoft.GLEE.dll") | Florida

Nombre: Microsoft.GLEE
Versión: 1.0.0.0
CultureInfo:
CodeBase: archivo: /// C: / projects / powershell / BuildAnalyzer / ...
EscapedCodeBase: archivo: /// C: / projects / powershell / BuildAnalyzer / ...
Procesador Arquitectura: MSIL
Banderas: PublicKey
HashAlgorithm: SHA1
Versión Compatibilidad: SameMachine
Par de claves :
FullName: Microsoft.GLEE, Version = 1.0.0.0, Culture = neut ... 

Aquí, ProcessorArchitecture identifica la plataforma objetivo.

  • Amd64 : un procesador de 64 bits basado en la arquitectura x64.
  • Brazo : un procesador ARM.
  • IA64 : solo un procesador Intel Itanium de 64 bits.
  • MSIL : neutral con respecto al procesador y bits por palabra.
  • X86 : un procesador Intel de 32 bits, nativo o en el entorno Windows en Windows en una plataforma de 64 bits (WOW64).
  • Ninguno : una combinación desconocida o no especificada de procesador y bits por palabra.

Estoy usando PowerShell en este ejemplo para llamar al método.

x0n
fuente
6060
Perdona la estúpida pregunta, pero ¿qué te dice esto que es x86?
George Mauer
53
El campo ProcessorArchitecture es una enumeración; en el ejemplo anterior se establece en MSIL, que significa "Neutro con respecto al procesador y bits por palabra". Otros valores incluyen X86, IA64, Amd64. Ver msdn.microsoft.com/en-us/library/… para más detalles.
Brian Gillespie
44
Intente [reflection.assemblyname]::GetAssemblyName("${pwd}\name.dll")ya que a veces el directorio actual del proceso no es el mismo que el del proveedor actual (que es donde supongo que la DLL es para usted)
x0n
2
Otra advertencia a tener en cuenta es olvidarse de "desbloquear" la DLL si la descargó de Internet. Use unblock-file, o haga clic derecho / properties / unblock del explorador. Tendrá que reiniciar el shell para que reconozca el estado desbloqueado si ya ha fallado una vez en la sesión actual (culpe a Internet Explorer por eso, sí, realmente)
X0n
1
En el código ASP.NET MVC, hay un comentario // DevDiv 216459: This code originally used Assembly.GetName(), but that requires FileIOPermission, which isn't granted in medium trust. However, Assembly.FullName *is* accessible in medium trust.Lamentablemente, no hay forma de leer la Arquitectura del procesador sin usar el GetName instance method; utilizando AssemblyName constructor, el campo siempre se establece en None.
metadings
221

Puede usar la herramienta CorFlags CLI (por ejemplo, C: \ Archivos de programa \ Microsoft SDKs \ Windows \ v7.0 \ Bin \ CorFlags.exe) para determinar el estado de un ensamblaje, en función de su salida y abrir un ensamblaje como un El activo binario debe poder determinar dónde necesita buscar para determinar si el indicador de 32 bits está establecido en 1 ( x86 ) o 0 ( cualquier CPU o x64 , según PE):

Option    | PE    | 32BIT
----------|-------|---------
x86       | PE32  | 1
Any CPU   | PE32  | 0
x64       | PE32+ | 0

La publicación de blog x64 Desarrollo con .NET tiene información sobre corflags.

Aún mejor, puede usarModule.GetPEKind para determinar si un ensamblado tiene un PortableExecutableKindsvalor PE32Plus(64 bits), Required32Bit(32 bits y WOW) o ILOnly(cualquier CPU) junto con otros atributos.

cfeduke
fuente
1
Después de ver su actualización, usar GetPEKind parece ser la forma correcta de hacerlo. He marcado el tuyo como la respuesta.
Judá Gabriel Himango
99
GetPEKind falla en un proceso de 64 bits al verificar ensambles de 32 bits
jjxtra
2
Tienes que llamar a GetPEKind desde el proceso de 32 bits
Ludwo
2
Instalo VS 2008, VS 2010, VS 2012 y VS 2013. Tengo 8 archivos CorFlags.exe en subcarpetas en C: \ Archivos de programa (x86) \ Microsoft SDKs \ Windows \. ¿Cuál debería ser usado?
Kiquenet
55
Como se señaló en esta respuesta , en .NET 4.5 hay 32BITREQ y 32BITPREF en lugar del indicador 32BIT. PE32 / 0/0 y PE32 / 0/1 son AnyCPU y AnyCPU de 32 bits preferidos, respectivamente.
angularsen
141

Solo para aclarar, CorFlags.exe es parte del SDK de .NET Framework . Tengo las herramientas de desarrollo en mi máquina, y la forma más sencilla para determinar si una DLL es solo de 32 bits es:

  1. Abra el símbolo del sistema de Visual Studio (en Windows: menú Inicio / Programas / Microsoft Visual Studio / Visual Studio Tools / Visual Studio 2008 Command Prompt)

  2. CD al directorio que contiene la DLL en cuestión

  3. Ejecute corflags como este: corflags MyAssembly.dll

Obtendrá salida algo como esto:

Microsoft (R) .NET Framework CorFlags Conversion Tool.  Version  3.5.21022.8
Copyright (c) Microsoft Corporation.  All rights reserved.

Version   : v2.0.50727
CLR Header: 2.5
PE        : PE32
CorFlags  : 3
ILONLY    : 1
32BIT     : 1
Signed    : 0

Según los comentarios, las banderas anteriores deben leerse de la siguiente manera:

  • Cualquier CPU: PE = PE32 y 32BIT = 0
  • x86: PE = PE32 y 32BIT = 1
  • 64 bits: PE = PE32 + y 32BIT = 0
JoshL
fuente
12
Esto parece haber cambiado mientras tanto; ahora se muestran corflags 32BITREQy 32BITPREFno un solo 32BITvalor.
O Mapper
1
Microsoft .NET 4.5 introdujo una nueva opción, Cualquier CPU de 32 bits preferida. Aquí están los detalles.
RBT
El "símbolo del sistema de Visual Studio" se llama hoy en día " símbolo del sistema del desarrollador de Visual Studio 2019 ".
Uwe Keim
22

¿Qué tal si solo escribes el tuyo? El núcleo de la arquitectura PE no ha cambiado seriamente desde su implementación en Windows 95. Aquí hay un ejemplo de C #:

    public static ushort GetPEArchitecture(string pFilePath)
    {
        ushort architecture = 0;
        try
        {
            using (System.IO.FileStream fStream = new System.IO.FileStream(pFilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read))
            {
                using (System.IO.BinaryReader bReader = new System.IO.BinaryReader(fStream))
                {
                    if (bReader.ReadUInt16() == 23117) //check the MZ signature
                    {
                        fStream.Seek(0x3A, System.IO.SeekOrigin.Current); //seek to e_lfanew.
                        fStream.Seek(bReader.ReadUInt32(), System.IO.SeekOrigin.Begin); //seek to the start of the NT header.
                        if (bReader.ReadUInt32() == 17744) //check the PE\0\0 signature.
                        {
                            fStream.Seek(20, System.IO.SeekOrigin.Current); //seek past the file header,
                            architecture = bReader.ReadUInt16(); //read the magic number of the optional header.
                        }
                    }
                }
            }
        }
        catch (Exception) { /* TODO: Any exception handling you want to do, personally I just take 0 as a sign of failure */}
        //if architecture returns 0, there has been an error.
        return architecture;
    }
}

Ahora las constantes actuales son:

0x10B - PE32  format.
0x20B - PE32+ format.

Pero con este método permite las posibilidades de nuevas constantes, solo valide el retorno como mejor le parezca.

Jason
fuente
1
Interesante, gracias por el código con explicación. Module.GetPEKind es probablemente la ruta más fácil. Pero esto es útil para el aprendizaje. Gracias.
Judá Gabriel Himango
3
Muy interesante, pero cuando tengo una aplicación compilada con Any CPU, el resultado es 0x10B. Esto está mal porque mi aplicación se ejecuta en un sistema x64. ¿Hay alguna otra bandera para verificar?
Samuel
GetPEArchitecture funciona para ensamblados compilados usando .net 3.5, 4.0, 4.5 y 4.5.1. De todos modos, creo que Module.GetPEKind falla en un proceso de 64 bits al verificar ensambles de 32 bits.
Kiquenet
9

Intente utilizar CorFlagsReader de este proyecto en CodePlex . No tiene referencias a otros ensamblajes y se puede usar como está.

Ludwo
fuente
1
Esta es la respuesta más precisa y útil.
Kirill Osenkov
El enlace aún funciona a partir de este escrito, pero como CodePlex está a punto de cerrarse, sería bueno tomar las medidas adecuadas antes de que sea demasiado tarde.
Peter Mortensen
7

DotPeek de JetBrians proporciona una forma rápida y fácil de ver msil (anycpu), x86, x64 dotPeek

Prabhakaran Rajagopal
fuente
6
[TestMethod]
public void EnsureKWLLibrariesAreAll64Bit()
{
    var assemblies = Assembly.GetExecutingAssembly().GetReferencedAssemblies().Where(x => x.FullName.StartsWith("YourCommonProjectName")).ToArray();
    foreach (var assembly in assemblies)
    {
        var myAssemblyName = AssemblyName.GetAssemblyName(assembly.FullName.Split(',')[0] + ".dll");
        Assert.AreEqual(ProcessorArchitecture.MSIL, myAssemblyName.ProcessorArchitecture);
    }
}
Morgan Mellor
fuente
Gracias por esto, una de nuestras aplicaciones debe construirse como x86, agregar una prueba unitaria asegura que las bibliotecas de compilación del servidor de compilación serán de 32 bits y evita que ocurran esos errores :)
Mido
5

A continuación se muestra un archivo por lotes que se ejecutará corflags.exeen todos dllsy exesen el directorio de trabajo actual y todos los subdirectorios, analiza los resultados y muestra la arquitectura de destino de cada uno.

Dependiendo de la versión corflags.exeutilizada, las líneas de pedido en la salida incluirán 32BIT, o 32BITREQ (y 32BITPREF). Cualquiera de estos dos está incluido en la salida es la línea de pedido crítica que debe verificarse para diferenciar entre Any CPUy x86. Si está utilizando una versión anterior de corflags.exe(anterior a Windows SDK v8.0A), solo la 32BITlínea de pedido estará presente en la salida, como lo han indicado otros en respuestas anteriores. De lo contrario 32BITREQ, y 32BITPREFreemplazarlo.

Esto supone que corflags.exeestá en el %PATH%. La forma más sencilla de garantizar esto es utilizar a Developer Command Prompt. Alternativamente, puede copiarlo desde su ubicación predeterminada .

Si el archivo por lotes siguiente se ejecuta contra un archivo no administrado dllo exe, lo mostrará incorrectamente como x86, ya que el resultado real de Corflags.exeserá un mensaje de error similar a:

corflags: error CF008: el archivo especificado no tiene un encabezado administrado válido

@echo off

echo.
echo Target architecture for all exes and dlls:
echo.

REM For each exe and dll in this directory and all subdirectories...
for %%a in (.exe, .dll) do forfiles /s /m *%%a /c "cmd /c echo @relpath" > testfiles.txt

for /f %%b in (testfiles.txt) do (
    REM Dump corflags results to a text file
    corflags /nologo %%b > corflagsdeets.txt

   REM Parse the corflags results to look for key markers   
   findstr /C:"PE32+">nul .\corflagsdeets.txt && (      
      REM `PE32+` indicates x64
        echo %%~b = x64
    ) || (
      REM pre-v8 Windows SDK listed only "32BIT" line item, 
      REM newer versions list "32BITREQ" and "32BITPREF" line items
        findstr /C:"32BITREQ  : 0">nul /C:"32BIT     : 0" .\corflagsdeets.txt && (
            REM `PE32` and NOT 32bit required indicates Any CPU
            echo %%~b = Any CPU
        ) || (
            REM `PE32` and 32bit required indicates x86
            echo %%~b = x86
        )
    )

    del corflagsdeets.txt
)

del testfiles.txt
echo.
Eric Lease
fuente
2

Una forma más sería usar dumpbin de las herramientas de Visual Studio en DLL y buscar la salida adecuada

dumpbin.exe /HEADERS <your dll path>
    FILE HEADER VALUE
                 14C machine (x86)
                   4 number of sections
            5885AC36 time date stamp Mon Jan 23 12:39:42 2017
                   0 file pointer to symbol table
                   0 number of symbols
                  E0 size of optional header
                2102 characteristics
                       Executable
                       32 bit word machine
                       DLL

Nota: Por encima de o / p es para 32 bits dll

Una opción más útil con dumpbin.exe es / EXPORTS, le mostrará la función expuesta por el dll

dumpbin.exe /EXPORTS <PATH OF THE DLL>
Ayush joshi
fuente
2

De forma más genérica: use la estructura de archivos para determinar el tipo de imagen y el bitness:

public static CompilationMode GetCompilationMode(this FileInfo info)
{
    if (!info.Exists) throw new ArgumentException($"{info.FullName} does not exist");

    var intPtr = IntPtr.Zero;
    try
    {
        uint unmanagedBufferSize = 4096;
        intPtr = Marshal.AllocHGlobal((int)unmanagedBufferSize);

        using (var stream = File.Open(info.FullName, FileMode.Open, FileAccess.Read))
        {
            var bytes = new byte[unmanagedBufferSize];
            stream.Read(bytes, 0, bytes.Length);
            Marshal.Copy(bytes, 0, intPtr, bytes.Length);
        }

        //Check DOS header magic number
        if (Marshal.ReadInt16(intPtr) != 0x5a4d) return CompilationMode.Invalid;

        // This will get the address for the WinNT header  
        var ntHeaderAddressOffset = Marshal.ReadInt32(intPtr + 60);

        // Check WinNT header signature
        var signature = Marshal.ReadInt32(intPtr + ntHeaderAddressOffset);
        if (signature != 0x4550) return CompilationMode.Invalid;

        //Determine file bitness by reading magic from IMAGE_OPTIONAL_HEADER
        var magic = Marshal.ReadInt16(intPtr + ntHeaderAddressOffset + 24);

        var result = CompilationMode.Invalid;
        uint clrHeaderSize;
        if (magic == 0x10b)
        {
            clrHeaderSize = (uint)Marshal.ReadInt32(intPtr + ntHeaderAddressOffset + 24 + 208 + 4);
            result |= CompilationMode.Bit32;
        }
        else if (magic == 0x20b)
        {
            clrHeaderSize = (uint)Marshal.ReadInt32(intPtr + ntHeaderAddressOffset + 24 + 224 + 4);
            result |= CompilationMode.Bit64;
        }
        else return CompilationMode.Invalid;

        result |= clrHeaderSize != 0
            ? CompilationMode.CLR
            : CompilationMode.Native;

        return result;
    }
    finally
    {
        if (intPtr != IntPtr.Zero) Marshal.FreeHGlobal(intPtr);
    }
}

Enumeración del modo de compilación

[Flags]
public enum CompilationMode
{
    Invalid = 0,
    Native = 0x1,
    CLR = Native << 1,
    Bit32 = CLR << 1,
    Bit64 = Bit32 << 1
}

Código fuente con explicación en GitHub

BlackGad
fuente
1

Otra forma de verificar la plataforma de destino de un ensamblado .NET es inspeccionar el ensamblaje con .NET Reflector ...

@ # ~ # € ~! ¡Me acabo de dar cuenta de que la nueva versión no es gratuita! Entonces, corrección, si tiene una versión gratuita del reflector .NET, puede usarla para verificar la plataforma de destino.

sakito
fuente
99
Use ILSpy , es una aplicación básica de código abierto que hace las mismas cosas que Reflector
Binary Worrier
1

Puede encontrar una aplicación más avanzada para eso aquí: CodePlex - ApiChange

Ejemplos:

C:\Downloads\ApiChange>ApiChange.exe -CorFlags c:\Windows\winhlp32.exe
File Name; Type; Size; Processor; IL Only; Signed
winhlp32.exe; Unmanaged; 296960; X86

C:\Downloads\ApiChange>ApiChange.exe -CorFlags c:\Windows\HelpPane.exe
File Name; Type; Size; Processor; IL Only; Signed
HelpPane.exe; Unmanaged; 733696; Amd64
Wernfried Domscheit
fuente
0

Una alternativa a las herramientas ya mencionadas es Telerik JustDecompile (herramienta gratuita) que mostrará la información junto al nombre del ensamblado:

Cualquier información o x86 o x64 en Telerik

Alexei
fuente