Como encontrar se um arquivo DLL nativo é compilado como x64 ou x86?

Eu quero determinar se um assembly nativo é cumprido como x64 ou x86 de um aplicativo de código gerenciado ( c # ).

Eu acho que deve estar em algum lugar no header do PE, já que o carregador do sistema operacional precisa saber essas informações, mas não consegui encontrá-lo. Claro que eu prefiro fazê-lo no código gerenciado, mas se for necessário, eu posso usar o C ++ nativo.

Existe uma maneira fácil de fazer isso com o CorFlags . Abra o prompt de comando do Visual Studio e digite “corflags [seu assembly]”. Você terá algo assim:

c: \ Arquivos de Programas (x86) \ Microsoft Visual Studio 9.0 \ VC> corflags “C: \ Windows \ Microsoft.NET \ Framework \ v2.0.50727 \ System.Data.dll”

Ferramenta de conversão do Microsoft (R) .NET Framework CorFlags. Versão 3.5.21022.8 Copyright (c) Microsoft Corporation. Todos os direitos reservados.

Versão: v2.0.50727 Cabeçalho do CLR: 2.5 PE: PE32 CorFlags: 24 ILONLY: 0 32BIT: 0 Assinado: 1

Você está olhando para PE e 32BIT especificamente.

  • Qualquer CPU :

    PE: PE32
    32 bits: 0

  • x86 :

    PE: PE32
    32BIT: 1

  • x64:

    PE: PE32 +
    32 bits: 0

Este truque funciona e requer apenas o Bloco de Notas.

Abra o arquivo dll usando um editor de texto (como o Bloco de notas) e encontre a primeira ocorrência da string PE . O caractere a seguir define se a dll é 32 ou 64 bits.

32 bits:

 PE L 

64 bits:

 PE d† 

O campo Magic do IMAGE_OPTIONAL_HEADER (embora não haja nada opcional sobre o header nas imagens executáveis ​​do Windows (arquivos DLL / EXE)) informará a arquitetura do PE.

Aqui está um exemplo de pegar a arquitetura de um arquivo.

 public static ushort GetImageArchitecture(string filepath) { using (var stream = new System.IO.FileStream(filepath, System.IO.FileMode.Open, System.IO.FileAccess.Read)) using (var reader = new System.IO.BinaryReader(stream)) { //check the MZ signature to ensure it's a valid Portable Executable image if (reader.ReadUInt16() != 23117) throw new BadImageFormatException("Not a valid Portable Executable image", filepath); // seek to, and read, e_lfanew then advance the stream to there (start of NT header) stream.Seek(0x3A, System.IO.SeekOrigin.Current); stream.Seek(reader.ReadUInt32(), System.IO.SeekOrigin.Begin); // Ensure the NT header is valid by checking the "PE\0\0" signature if (reader.ReadUInt32() != 17744) throw new BadImageFormatException("Not a valid Portable Executable image", filepath); // seek past the file header, then read the magic number from the optional header stream.Seek(20, System.IO.SeekOrigin.Current); return reader.ReadUInt16(); } } 

As duas únicas constantes de arquitetura no momento são:

 0x10b - PE32 0x20b - PE32+ 

Felicidades

ATUALIZAÇÃO Já faz um tempo desde que eu postei essa resposta, mas ainda vejo que ela recebe algumas reviravoltas de vez em quando, então achei que valeria a pena atualizar. Eu escrevi uma maneira de obter a arquitetura de uma imagem Portable Executable , que também verifica se ela foi compilada como AnyCPU . Infelizmente a resposta está em C ++, mas não deve ser muito difícil portar para C # se você tiver alguns minutos para procurar as estruturas em WinNT.h . Se as pessoas estiverem interessadas, escreverei uma porta em C #, mas, a menos que as pessoas realmente queiram, não vou gastar muito tempo insistindo nisso.

 #include  #define MKPTR(p1,p2) ((DWORD_PTR)(p1) + (DWORD_PTR)(p2)) typedef enum _pe_architecture { PE_ARCHITECTURE_UNKNOWN = 0x0000, PE_ARCHITECTURE_ANYCPU = 0x0001, PE_ARCHITECTURE_X86 = 0x010B, PE_ARCHITECTURE_x64 = 0x020B } PE_ARCHITECTURE; LPVOID GetOffsetFromRva(IMAGE_DOS_HEADER *pDos, IMAGE_NT_HEADERS *pNt, DWORD rva) { IMAGE_SECTION_HEADER *pSecHd = IMAGE_FIRST_SECTION(pNt); for(unsigned long i = 0; i < pNt->FileHeader.NumberOfSections; ++i, ++pSecHd) { // Lookup which section contains this RVA so we can translate the VA to a file offset if (rva >= pSecHd->VirtualAddress && rva < (pSecHd->VirtualAddress + pSecHd->Misc.VirtualSize)) { DWORD delta = pSecHd->VirtualAddress - pSecHd->PointerToRawData; return (LPVOID)MKPTR(pDos, rva - delta); } } return NULL; } PE_ARCHITECTURE GetImageArchitecture(void *pImageBase) { // Parse and validate the DOS header IMAGE_DOS_HEADER *pDosHd = (IMAGE_DOS_HEADER*)pImageBase; if (IsBadReadPtr(pDosHd, sizeof(pDosHd->e_magic)) || pDosHd->e_magic != IMAGE_DOS_SIGNATURE) return PE_ARCHITECTURE_UNKNOWN; // Parse and validate the NT header IMAGE_NT_HEADERS *pNtHd = (IMAGE_NT_HEADERS*)MKPTR(pDosHd, pDosHd->e_lfanew); if (IsBadReadPtr(pNtHd, sizeof(pNtHd->Signature)) || pNtHd->Signature != IMAGE_NT_SIGNATURE) return PE_ARCHITECTURE_UNKNOWN; // First, naive, check based on the 'Magic' number in the Optional Header. PE_ARCHITECTURE architecture = (PE_ARCHITECTURE)pNtHd->OptionalHeader.Magic; // If the architecture is x86, there is still a possibility that the image is 'AnyCPU' if (architecture == PE_ARCHITECTURE_X86) { IMAGE_DATA_DIRECTORY comDirectory = pNtHd->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR]; if (comDirectory.Size) { IMAGE_COR20_HEADER *pClrHd = (IMAGE_COR20_HEADER*)GetOffsetFromRva(pDosHd, pNtHd, comDirectory.VirtualAddress); // Check to see if the CLR header contains the 32BITONLY flag, if not then the image is actually AnyCpu if ((pClrHd->Flags & COMIMAGE_FLAGS_32BITREQUIRED) == 0) architecture = PE_ARCHITECTURE_ANYCPU; } } return architecture; } 

A function aceita um ponteiro para uma imagem PE na memory (assim você pode escolher o seu veneno sobre como obtê-lo; mapeamento de memory ou ler a coisa toda na memory … o que for).

Para um arquivo DLL não gerenciado, você precisa primeiro verificar se é um arquivo DLL de 16 bits (esperançosamente não). Em seguida, verifique o campo IMAGE\_FILE_HEADER.Machine .

Alguém já teve tempo para resolver isso, então vou repetir aqui:

Para distinguir entre um arquivo PE de 32 e 64 bits, você deve verificar o campo IMAGE_FILE_HEADER.Machine. Com base nas especificações do Microsoft PE e COFF abaixo, listei todos os valores possíveis para este campo: http://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/ pecoff_v8.doc

IMAGE_FILE_MACHINE_UNKNOWN 0x0 Presume-se que o conteúdo deste campo seja aplicável a qualquer tipo de máquina

IMAGE_FILE_MACHINE_AM33 0x1d3 Matsushita AM33

IMAGE_FILE_MACHINE_AMD64 0x8664 x64

IMAGE_FILE_MACHINE_ARM 0x1c0 ARM little endian

IMAGE_FILE_MACHINE_EBC 0xebc código de bytes EFI

IMAGE_FILE_MACHINE_I386 0x14c Processadores Intel 386 ou posterior e processadores compatíveis

IMAGE_FILE_MACHINE_IA64 0x200 família de processadores Intel Itanium

IMAGE_FILE_MACHINE_M32R 0x9041 Mitsubishi M32R little endian

IMAGE_FILE_MACHINE_MIPS16 0x266 MIPS16

IMAGE_FILE_MACHINE_MIPSFPU MIPS de 0x366 com FPU

IMAGE_FILE_MACHINE_MIPSFPU16 0x466 MIPS16 com FPU

IMAGE_FILE_MACHINE_POWERPC 0x1f0 Power PC little endian

IMAGE_FILE_MACHINE_POWERPCFP 0x1f1 Power PC com suporte a ponto flutuante

IMAGE_FILE_MACHINE_R4000 0x166 MIPS little endian

IMAGE_FILE_MACHINE_SH3 0x1a2 Hitachi SH3

IMAGE_FILE_MACHINE_SH3DSP 0x1a3 Hitachi SH3 DSP

IMAGE_FILE_MACHINE_SH4 0x1a6 Hitachi SH4

IMAGE_FILE_MACHINE_SH5 0x1a8 Hitachi SH5

IMAGE_FILE_MACHINE_THUMB 0x1c2 Thumb

IMAGE_FILE_MACHINE_WCEMIPSV2 0x169 MIPS little-endian WCE v2

Sim, você pode verificar IMAGE_FILE_MACHINE_AMD64 | IMAGE_FILE_MACHINE_IA64 por 64 bits e IMAGE_FILE_MACHINE_I386 por 32 bits.

Você pode encontrar uma implementação de amostra C # aqui para a solução IMAGE_FILE_HEADER

Binários de 64 bits são armazenados no formato PE32 +. Tente ler http://www.masm32.com/board/index.php?action=dlattach;topic=6687.0;id=3486

Abra a dll com um editor hexadecimal, como o HxD

Se existe um “dt” na 9ª linha é 64 bits.

Se houver um “L.” na 9ª linha é 32 bits.

Eu reescrevi a solução c ++ na primeira resposta no script powershell. O script pode determinar esses tipos de arquivos .exe e .dll:

 #Description C# compiler switch PE type machine corflags #MSIL /platform:anycpu (default) PE32 x86 ILONLY #MSIL 32 bit pref /platform:anycpu32bitpreferred PE32 x86 ILONLY | 32BITREQUIRED | 32BITPREFERRED #x86 managed /platform:x86 PE32 x86 ILONLY | 32BITREQUIRED #x86 mixed n/a PE32 x86 32BITREQUIRED #x64 managed /platform:x64 PE32+ x64 ILONLY #x64 mixed n/a PE32+ x64 #ARM managed /platform:arm PE32 ARM ILONLY #ARM mixed n/a PE32 ARM 

essa solução tem algumas vantagens sobre o corflags.exe e o carregamento de assembly via Assembly.Load em C # – você nunca obterá BadImageFormatException ou mensagem sobre header inválido.

 function GetActualAddressFromRVA($st, $sec, $numOfSec, $dwRVA) { [System.UInt32] $dwRet = 0; for($j = 0; $j -lt $numOfSec; $j++) { $nextSectionOffset = $sec + 40*$j; $VirtualSizeOffset = 8; $VirtualAddressOffset = 12; $SizeOfRawDataOffset = 16; $PointerToRawDataOffset = 20; $Null = @( $curr_offset = $st.BaseStream.Seek($nextSectionOffset + $VirtualSizeOffset, [System.IO.SeekOrigin]::Begin); [System.UInt32] $VirtualSize = $b.ReadUInt32(); [System.UInt32] $VirtualAddress = $b.ReadUInt32(); [System.UInt32] $SizeOfRawData = $b.ReadUInt32(); [System.UInt32] $PointerToRawData = $b.ReadUInt32(); if ($dwRVA -ge $VirtualAddress -and $dwRVA -lt ($VirtualAddress + $VirtualSize)) { $delta = $VirtualAddress - $PointerToRawData; $dwRet = $dwRVA - $delta; return $dwRet; } ); } return $dwRet; } function Get-Bitness2([System.String]$path, $showLog = $false) { $Obj = @{}; $Obj.Result = ''; $Obj.Error = $false; $Obj.Log = @(Split-Path -Path $path -Leaf -Resolve); $b = new-object System.IO.BinaryReader([System.IO.File]::Open($path,[System.IO.FileMode]::Open,[System.IO.FileAccess]::Read, [System.IO.FileShare]::Read)); $curr_offset = $b.BaseStream.Seek(0x3c, [System.IO.SeekOrigin]::Begin) [System.Int32] $peOffset = $b.ReadInt32(); $Obj.Log += 'peOffset ' + "{0:X0}" -f $peOffset; $curr_offset = $b.BaseStream.Seek($peOffset, [System.IO.SeekOrigin]::Begin); [System.UInt32] $peHead = $b.ReadUInt32(); if ($peHead -ne 0x00004550) { $Obj.Error = $true; $Obj.Result = 'Bad Image Format'; $Obj.Log += 'cannot determine file type (not x64/x86/ARM) - exit with error'; }; if ($Obj.Error) { $b.Close(); Write-Host ($Obj.Log | Format-List | Out-String); return $false; }; [System.UInt16] $machineType = $b.ReadUInt16(); $Obj.Log += 'machineType ' + "{0:X0}" -f $machineType; [System.UInt16] $numOfSections = $b.ReadUInt16(); $Obj.Log += 'numOfSections ' + "{0:X0}" -f $numOfSections; if (($machineType -eq 0x8664) -or ($machineType -eq 0x200)) { $Obj.Log += 'machineType: x64'; } elseif ($machineType -eq 0x14c) { $Obj.Log += 'machineType: x86'; } elseif ($machineType -eq 0x1c0) { $Obj.Log += 'machineType: ARM'; } else{ $Obj.Error = $true; $Obj.Log += 'cannot determine file type (not x64/x86/ARM) - exit with error'; }; if ($Obj.Error) { $b.Close(); Write-Output ($Obj.Log | Format-List | Out-String); return $false; }; $curr_offset = $b.BaseStream.Seek($peOffset+20, [System.IO.SeekOrigin]::Begin); [System.UInt16] $sizeOfPeHeader = $b.ReadUInt16(); $coffOffset = $peOffset + 24;#PE header size is 24 bytes $Obj.Log += 'coffOffset ' + "{0:X0}" -f $coffOffset; $curr_offset = $b.BaseStream.Seek($coffOffset, [System.IO.SeekOrigin]::Begin);#+24 byte magic number [System.UInt16] $pe32 = $b.ReadUInt16(); $clr20headerOffset = 0; $flag32bit = $false; $Obj.Log += 'pe32 magic number: ' + "{0:X0}" -f $pe32; $Obj.Log += 'size of optional header ' + ("{0:D0}" -f $sizeOfPeHeader) + " bytes"; #COMIMAGE_FLAGS_ILONLY =0x00000001, #COMIMAGE_FLAGS_32BITREQUIRED =0x00000002, #COMIMAGE_FLAGS_IL_LIBRARY =0x00000004, #COMIMAGE_FLAGS_STRONGNAMESIGNED =0x00000008, #COMIMAGE_FLAGS_NATIVE_ENTRYPOINT =0x00000010, #COMIMAGE_FLAGS_TRACKDEBUGDATA =0x00010000, #COMIMAGE_FLAGS_32BITPREFERRED =0x00020000, $COMIMAGE_FLAGS_ILONLY = 0x00000001; $COMIMAGE_FLAGS_32BITREQUIRED = 0x00000002; $COMIMAGE_FLAGS_32BITPREFERRED = 0x00020000; $offset = 96; if ($pe32 -eq 0x20b) { $offset = 112;#size of COFF header is bigger for pe32+ } $clr20dirHeaderOffset = $coffOffset + $offset + 14*8;#clr directory header offset + start of section number 15 (each section is 8 byte long); $Obj.Log += 'clr20dirHeaderOffset ' + "{0:X0}" -f $clr20dirHeaderOffset; $curr_offset = $b.BaseStream.Seek($clr20dirHeaderOffset, [System.IO.SeekOrigin]::Begin); [System.UInt32] $clr20VirtualAddress = $b.ReadUInt32(); [System.UInt32] $clr20Size = $b.ReadUInt32(); $Obj.Log += 'clr20VirtualAddress ' + "{0:X0}" -f $clr20VirtualAddress; $Obj.Log += 'clr20SectionSize ' + ("{0:D0}" -f $clr20Size) + " bytes"; if ($clr20Size -eq 0) { if ($machineType -eq 0x1c0) { $Obj.Result = 'ARM native'; } elseif ($pe32 -eq 0x10b) { $Obj.Result = '32-bit native'; } elseif($pe32 -eq 0x20b) { $Obj.Result = '64-bit native'; } $b.Close(); if ($Obj.Result -eq '') { $Obj.Error = $true; $Obj.Log += 'Unknown type of file'; } else { if ($showLog) { Write-Output ($Obj.Log | Format-List | Out-String); }; return $Obj.Result; } }; if ($Obj.Error) { $b.Close(); Write-Host ($Obj.Log | Format-List | Out-String); return $false; }; [System.UInt32]$sectionsOffset = $coffOffset + $sizeOfPeHeader; $Obj.Log += 'sectionsOffset ' + "{0:X0}" -f $sectionsOffset; $realOffset = GetActualAddressFromRVA $b $sectionsOffset $numOfSections $clr20VirtualAddress; $Obj.Log += 'real IMAGE_COR20_HEADER offset ' + "{0:X0}" -f $realOffset; if ($realOffset -eq 0) { $Obj.Error = $true; $Obj.Log += 'cannot find COR20 header - exit with error'; $b.Close(); return $false; }; if ($Obj.Error) { $b.Close(); Write-Host ($Obj.Log | Format-List | Out-String); return $false; }; $curr_offset = $b.BaseStream.Seek($realOffset + 4, [System.IO.SeekOrigin]::Begin); [System.UInt16] $majorVer = $b.ReadUInt16(); [System.UInt16] $minorVer = $b.ReadUInt16(); $Obj.Log += 'IMAGE_COR20_HEADER version ' + ("{0:D0}" -f $majorVer) + "." + ("{0:D0}" -f $minorVer); $flagsOffset = 16;#+16 bytes - flags field $curr_offset = $b.BaseStream.Seek($realOffset + $flagsOffset, [System.IO.SeekOrigin]::Begin); [System.UInt32] $flag32bit = $b.ReadUInt32(); $Obj.Log += 'CorFlags: ' + ("{0:X0}" -f $flag32bit); #Description C# compiler switch PE type machine corflags #MSIL /platform:anycpu (default) PE32 x86 ILONLY #MSIL 32 bit pref /platform:anycpu32bitpreferred PE32 x86 ILONLY | 32BITREQUIRED | 32BITPREFERRED #x86 managed /platform:x86 PE32 x86 ILONLY | 32BITREQUIRED #x86 mixed n/a PE32 x86 32BITREQUIRED #x64 managed /platform:x64 PE32+ x64 ILONLY #x64 mixed n/a PE32+ x64 #ARM managed /platform:arm PE32 ARM ILONLY #ARM mixed n/a PE32 ARM $isILOnly = ($flag32bit -band $COMIMAGE_FLAGS_ILONLY) -eq $COMIMAGE_FLAGS_ILONLY; $Obj.Log += 'ILONLY: ' + $isILOnly; if ($machineType -eq 0x1c0) {#if ARM if ($isILOnly) { $Obj.Result = 'ARM managed'; } else { $Obj.Result = 'ARM mixed'; } } elseif ($pe32 -eq 0x10b) {#pe32 $is32bitRequired = ($flag32bit -band $COMIMAGE_FLAGS_32BITREQUIRED) -eq $COMIMAGE_FLAGS_32BITREQUIRED; $is32bitPreffered = ($flag32bit -band $COMIMAGE_FLAGS_32BITPREFERRED) -eq $COMIMAGE_FLAGS_32BITPREFERRED; $Obj.Log += '32BIT: ' + $is32bitRequired; $Obj.Log += '32BIT PREFFERED: ' + $is32bitPreffered if ($is32bitRequired -and $isILOnly -and $is32bitPreffered) { $Obj.Result = 'AnyCpu 32bit-preffered'; } elseif ($is32bitRequired -and $isILOnly -and !$is32bitPreffered){ $Obj.Result = 'x86 managed'; } elseif (!$is32bitRequired -and !$isILOnly -and $is32bitPreffered) { $Obj.Result = 'x86 mixed'; } elseif ($isILOnly) { $Obj.Result = 'AnyCpu'; } } elseif ($pe32 -eq 0x20b) {#pe32+ if ($isILOnly) { $Obj.Result = 'x64 managed'; } else { $Obj.Result = 'x64 mixed'; } } $b.Close(); if ($showLog) { Write-Host ($Obj.Log | Format-List | Out-String); } if ($Obj.Result -eq ''){ return 'Unknown type of file';}; $flags = ''; if ($isILOnly) {$flags += 'ILONLY';} if ($is32bitRequired) { if ($flags -ne '') {$flags += ' | ';} $flags += '32BITREQUIRED'; } if ($is32bitPreffered) { if ($flags -ne '') {$flags += ' | ';} $flags += '32BITPREFERRED'; } if ($flags -ne '') {$flags = ' (' + $flags +')';} return $Obj.Result + $flags; } 

exemplo de uso:

 #$filePath = "C:\Windows\SysWOW64\regedit.exe";#32 bit native on 64bit windows $filePath = "C:\Windows\regedit.exe";#64 bit native on 64bit windows | should be 32 bit native on 32bit windows Get-Bitness2 $filePath $true; 

você pode omitir o segundo parâmetro se não precisar ver detalhes

Uma maneira rápida e provavelmente suja de fazer isso é descrita aqui: https://superuser.com/a/889267 . Você abre a DLL em um editor e verifica os primeiros caracteres após a seqüência “PE”.

Aparentemente, você pode encontrá-lo no header do executável portátil. O utilitário corflags.exe é capaz de mostrar se você está ou não direcionado para x64. Espero que isso ajude você a encontrar mais informações sobre isso.