Como posso determinar se um assembly .NET foi criado para x86 ou x64?

327

Eu tenho uma lista arbitrária de assemblies .NET.

Preciso verificar programaticamente se cada DLL foi criada para x86 (em oposição a x64 ou qualquer CPU). Isso é possível?

Judah Gabriel Himango
fonte
2
Você também pode querer verificar este: verifique se a DLL não gerenciada é de 32 bits ou 64 bits .
Matt
2
Na versão posterior do CorFlags, correspondente ao .NET 4.5, "32BIT" foi substituído por "32BITREQ" e "32BITPREF". .
Peter Mortensen

Respostas:

281

Olhe para System.Reflection.AssemblyName.GetAssemblyName(string assemblyFile)

Você pode examinar os metadados do assembly a partir da instância AssemblyName retornada:

Usando o PowerShell :

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

Nome: Microsoft.GLEE
Versão: 1.0.0.0
CultureInfo:
CodeBase: arquivo: /// C: / projects / powershell / BuildAnalyzer / ...
EscapedCodeBase: file: /// C: / projects / powershell / BuildAnalyzer / ...
Arquitetura do Processador: MSIL
Sinalizadores: PublicKey
HashAlgorithm: SHA1
VersãoCompatibilidade: SameMachine
KeyPair:
Nome completo: Microsoft.GLEE, versão = 1.0.0.0, Culture = neut ... 

Aqui, ProcessorArchitecture identifica a plataforma de destino.

  • Amd64 : um processador de 64 bits baseado na arquitetura x64.
  • Arm : um processador ARM.
  • IA64 : Apenas um processador Intel Itanium de 64 bits.
  • MSIL : neutro em relação ao processador e bits por palavra.
  • X86 : Um processador Intel de 32 bits, nativo ou no ambiente Windows no Windows em uma plataforma de 64 bits (WOW64).
  • Nenhum : uma combinação desconhecida ou não especificada de processador e bits por palavra.

Estou usando o PowerShell neste exemplo para chamar o método

x0n
fonte
60
Perdoe a pergunta estúpida - mas o que isso diz a você que é x86?
George Mauer
53
O campo ProcessorArchitecture é uma enumeração; no exemplo acima, ele é definido como MSIL, que significa "Neutro em relação ao processador e bits por palavra". Outros valores incluem X86, IA64, Amd64. Consulte msdn.microsoft.com/en-us/library/… para obter mais detalhes.
Brian Gillespie
4
Tente com [reflection.assemblyname]::GetAssemblyName("${pwd}\name.dll")como às vezes diretório atual do processo não é o mesmo que o provedor atual de (que é onde eu assumir a DLL é para você)
x0n
2
Outra ressalva a ser observada é esquecer de "desbloquear" a DLL se você a tiver baixado das internets. Use unblock-file ou clique com o botão direito do mouse em / properties / unblock do explorer. Você precisará reiniciar o shell para que ele reconheça o status desbloqueado se você já falhou uma vez na sessão atual (culpa Internet Explorer para isso -. Sim, realmente)
x0n
1
No código do ASP.NET MVC, há um comentário // 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.Infelizmente, não há como ler ProcessorArchitecture sem usar o GetName instance method; usando AssemblyName constructor, o campo é sempre definido como None.
metadings 13/08/14
221

Você pode usar a ferramenta CLI do CorFlags (por exemplo, C: \ Arquivos de programas \ Microsoft SDKs \ Windows \ v7.0 \ Bin \ CorFlags.exe) para determinar o status de um assembly, com base em sua saída e abrir um assembly como um ativo binário, você deve poder determinar onde precisa determinar se o sinalizador 32BIT está definido como 1 ( x86 ) ou 0 ( qualquer CPU ou x64 , dependendo ):PE

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

A postagem do blog x64 Development with .NET possui algumas informações sobre corflags.

Ainda melhor, você pode usarModule.GetPEKind para determinar se um assembly é PortableExecutableKindsvalor PE32Plus(64 bits), Required32Bit(32 bits e WOW) ou ILOnly(qualquer CPU) junto com outros atributos.

cfeduke
fonte
1
Depois de ver sua atualização, usar o GetPEKind parece ser a maneira correta de fazer isso. Eu marquei o seu como a resposta.
Judah Gabriel Himango 7/11
9
GetPEKind falha num processo de 64 bits ao verificar 32 conjuntos de bits
jjxtra
2
Você tem que chamar GetPEKind de processo de 32 bits
Ludwo
2
Eu instalo o VS 2008, VS 2010, VS 2012 e VS 2013. Tenho 8 arquivos CorFlags.exe em subpastas em C: \ Arquivos de Programas (x86) \ Microsoft SDKs \ Windows \. Qual devo usar?
25414
5
Conforme indicado nesta resposta , no .NET 4.5 há 32BITREQ e 32BITPREF em vez do sinalizador 32BIT. PE32 / 0/0 e PE32 / 0/1 são os preferidos AnyCPU e AnyCPU 32 bits, respectivamente.
angularsen
141

Apenas para esclarecimento, CorFlags.exe faz parte do .NET Framework SDK . Eu tenho as ferramentas de desenvolvimento em minha máquina e a maneira mais simples para eu determinar se uma DLL é apenas de 32 bits é:

  1. Abra o prompt de comando do Visual Studio (no Windows: menu Iniciar / Programas / Microsoft Visual Studio / Ferramentas do Visual Studio / prompt de comando do Visual Studio 2008)

  2. CD para o diretório que contém a DLL em questão

  3. Execute corflags assim: corflags MyAssembly.dll

Você obterá algo como isto:

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

Conforme comentários, os sinalizadores acima devem ser lidos da seguinte forma:

  • Qualquer CPU: PE = PE32 e 32BIT = 0
  • x86: PE = PE32 e 32BIT = 1
  • 64 bits: PE = PE32 + e 32BIT = 0
JoshL
fonte
12
Isso parece ter mudado enquanto isso; corflags agora é exibido 32BITREQe 32BITPREFnão um único 32BITvalor.
OR Mapper
1
O Microsoft .NET 4.5 introduziu uma nova opção, Qualquer CPU de 32 bits preferida. Aqui estão os detalhes.
RBT
Atualmente, o "Prompt de Comando do Visual Studio" é chamado " Prompt de Comando do Visual Studio 2019 Developer ".
Uwe Keim
22

Que tal você escrever sua própria? O núcleo da arquitetura do PE não foi seriamente alterado desde sua implementação no Windows 95. Aqui está um exemplo 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;
    }
}

Agora, as constantes atuais são:

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

Mas com este método, ele permite as possibilidades de novas constantes, apenas valida o retorno como achar melhor.

Jason
fonte
1
Interessante, obrigado pelo código com explicação. Module.GetPEKind é provavelmente o caminho mais fácil. Mas isso é útil pelo bem da aprendizagem. Obrigado.
Judah Gabriel Himango 19/03/12
3
Muito interessante, mas quando tenho um aplicativo compilado com Qualquer CPU, o resultado é 0x10B. Isso está errado porque meu aplicativo é executado em um sistema x64. Existe alguma outra bandeira para verificar?
Samuel
GetPEArchitecture funciona para montagens compiladas usando .net 3.5, 4.0, 4.5 e 4.5.1? De qualquer forma, acho que o Module.GetPEKind falha em um processo de 64 bits ao verificar conjuntos de 32 bits.
25414 Kanjet
9

Tente usar CorFlagsReader deste projeto no CodePlex . Não possui referências a outros conjuntos e pode ser usado como está.

Ludwo
fonte
1
Esta é a resposta mais precisa e útil.
Kirill Osenkov
O link ainda funciona no momento da redação deste documento, mas, como o CodePlex está prestes a ser desligado, seria bom executar a ação apropriada antes que seja tarde demais.
Peter Mortensen
7

O DotPeek da JetBrians fornece uma maneira rápida e fácil de visualizar msil (anycpu), x86, x64 dotPeek

Prabhakaran Rajagopal
fonte
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
fonte
Obrigado por isso, uma das nossas aplicações tem que ser construído como x86, adicionando um garante de teste de unidade que as bibliotecas de compilação do servidor construção será de 32 bits e evita esses erros aconteçam :)
Mido
5

Abaixo está um arquivo em lotes que será executado corflags.exeem todos dllse exesno diretório de trabalho atual e em todos os subdiretórios, analisará os resultados e exibirá a arquitetura de destino de cada um.

Dependendo da versão do corflags.exeque é usado, os itens de linha na saída ou incluir 32BIT, ou 32BITREQ (e 32BITPREF). Qualquer um desses dois está incluído na saída é o item de linha crítico que deve ser verificado para diferenciar entre Any CPUe x86. Se você estiver usando uma versão mais antiga do corflags.exe(anterior ao SDK do Windows v8.0A), somente o 32BITitem de linha estará presente na saída, como outros indicaram nas respostas anteriores. Caso contrário, 32BITREQe 32BITPREFsubstituí-lo.

Isso assume que corflags.exeestá no %PATH%. A maneira mais simples de garantir isso é usar a Developer Command Prompt. Como alternativa, você pode copiá-lo de seu local padrão .

Se o arquivo em lotes abaixo for executado em um não gerenciado dllou exe, ele será exibido incorretamente como x86, pois a saída real de Corflags.exeserá uma mensagem de erro semelhante a:

corflags: erro CF008: o arquivo especificado não possui um cabeçalho gerenciado 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
fonte
2

Mais uma maneira seria usar o dumpbin das ferramentas do Visual Studio na DLL e procurar a saída apropriada

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: Acima o / p é para 32 bits dll

Uma opção mais útil com o dumpbin.exe é / EXPORTS, que mostra a função exposta pela DLL

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

Maneira mais genérica - use a estrutura de arquivos para determinar o tipo de imagem e imagem:

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

Enumeração do modo de compilação

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

Código fonte com explicação no GitHub

BlackGad
fonte
1

Outra maneira de verificar a plataforma de destino de um assembly .NET é inspecionar o assembly com o .NET Reflector ...

@ # ~ # € ~! Acabei de perceber que a nova versão não é gratuita! Portanto, correção, se você possui uma versão gratuita do refletor .NET, pode usá-lo para verificar a plataforma de destino.

sakito
fonte
9
Use ILSpy , é um aplicativo de código aberto básico que faz muito as mesmas coisas que refletor
Binary Worrier
1

Um aplicativo mais avançado para o qual você pode encontrar aqui: CodePlex - ApiChange

Exemplos:

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
fonte
0

Uma alternativa às ferramentas já mencionadas é o Telerik JustDecompile (ferramenta gratuita) que exibirá as informações ao lado do nome da montagem:

Qualquer informação ou x86 ou x64 no Telerik

Alexei
fonte