Je veux déterminer si un assembly natif est respecté en tant que x64 ou x86 à partir d'une application de code managé ( C # ).
Je pense que cela doit figurer quelque part dans l'en-tête PE, car le chargeur de système d'exploitation a besoin de connaître ces informations, mais je ne les ai pas trouvées. Bien sûr, je préfère le faire en code managé, mais si nécessaire, je peux utiliser le C++ natif.
Vous pouvez aussi utiliser [~ # ~] dumpbin [~ # ~] . Utilisez l'indicateur /headers
Ou /all
Et son premier en-tête de fichier répertorié.
dumpbin /headers cv210.dll
Microsoft (R) COFF/PE Dumper Version 10.00.30319.01
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file cv210.dll
PE signature found
File Type: DLL
FILE HEADER VALUES
8664 machine (x64)
6 number of sections
4BBAB813 time date stamp Tue Apr 06 12:26:59 2010
0 file pointer to symbol table
0 number of symbols
F0 size of optional header
2022 characteristics
Executable
Application can handle large (>2GB) addresses
DLL
Microsoft (R) COFF/PE Dumper Version 10.00.30319.01
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file acrdlg.dll
PE signature found
File Type: DLL
FILE HEADER VALUES
14C machine (x86)
5 number of sections
467AFDD2 time date stamp Fri Jun 22 06:38:10 2007
0 file pointer to symbol table
0 number of symbols
E0 size of optional header
2306 characteristics
Executable
Line numbers stripped
32 bit Word machine
Debug information stripped
DLL
'trouver' peut rendre la vie un peu plus facile:
dumpbin /headers cv210.dll |find "machine"
8664 machine (x64)
Il existe un moyen simple de faire cela avec CorFlags . Ouvrez l'invite de commandes de Visual Studio et tapez "corflags [votre assembly]". Vous obtiendrez quelque chose comme ça:
c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC> annotations "C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Data.dll"
Outil de conversion CorFlags Microsoft .NET Framework. Version 3.5.21022.8 Droits d'auteur (c) Microsoft Corporation. Tous les droits sont réservés.
Version: v2.0.50727 CLR En-tête: 2.5 PE: Balises CorFlag: 32 ILONLY: 0 32BITS: 0 Signé: 1
Vous regardez spécifiquement PE et 32BIT.
Tout processeur :
PE: PE32
32BIT: 0
x86 :
PE: PE32
32BIT: 1
x64:
PE: PE32 +
32BIT: 0
Ce astuce fonctionne et nécessite uniquement le Bloc-notes.
Ouvrez le fichier dll en utilisant un éditeur de texte (comme Notepad) et recherchez la première occurrence de la chaîne PE
. Le caractère suivant définit si la DLL est 32 ou 64 bits.
PE L
PE d†
Le champ Magic
du IMAGE_OPTIONAL_HEADER
(même s’il n’ya rien d’optionnel dans l’en-tête des images exécutables Windows (fichiers DLL/EXE)), il vous indiquera l’architecture du PE.
Voici un exemple de saisie de l'architecture à partir d'un fichier.
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();
}
}
Les deux seules constantes d'architecture pour le moment sont:
0x10b - PE32
0x20b - PE32+
À votre santé
UPDATE Cela fait longtemps que je n'ai pas posté cette réponse, mais je vois encore qu'elle reçoit quelques votes positifs de temps en temps, alors j'ai pensé qu'il valait la peine de la mettre à jour. J'ai écrit un moyen d'obtenir l'architecture d'un Portable Executable
image, qui vérifie également si elle a été compilée sous la forme AnyCPU
. Malheureusement, la réponse est en C++, mais il ne devrait pas être trop difficile de porter en C # si vous avez quelques minutes pour rechercher les structures dans WinNT.h
. Si les gens sont intéressés, j'écrirai un port en C #, mais à moins que les gens le souhaitent vraiment, je ne passerai pas beaucoup de temps à le stresser.
#include <Windows.h>
#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;
}
La fonction accepte un pointeur sur une image PE en mémoire (vous pouvez donc choisir votre poison pour savoir comment l'obtenir; mappage de la mémoire ou lecture du tout dans la mémoire ... peu importe).
Pour un fichier non géré DLL, vous devez d’abord vérifier s’il s’agit d’un fichier 16 bits DLL (heureusement non)). Ensuite, vérifiez le fichier IMAGE\_FILE_HEADER.Machine
champ.
Quelqu'un d'autre a pris le temps de résoudre ce problème, je vais donc répéter ici:
Pour distinguer un fichier PE 32 bits et 64 bits, vous devez vérifier le champ IMAGE_FILE_HEADER.Machine. Sur la base de la spécification Microsoft PE et COFF ci-dessous, j’ai répertorié toutes les valeurs possibles pour ce champ: http://download.Microsoft.com/download/9/c/5/9c5b2167-8017-4bae- 9fde-d599bac8184a/pecoff_v8.doc
IMAGE_FILE_MACHINE_UNKNOWN 0x0 Le contenu de ce champ est supposé être applicable à tout type d'ordinateur.
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 code d'octet EFI
IMAGE_FILE_MACHINE_I386 0x14c Processeurs Intel 386 ou ultérieurs et processeurs compatibles
IMAGE_FILE_MACHINE_IA64 0x200 famille de processeurs Intel Itanium
IMAGE_FILE_MACHINE_M32R 0x9041 little endian Mitsubishi M32R
IMAGE_FILE_MACHINE_MIPS16 0x266 MIPS16
IMAGE_FILE_MACHINE_MIPSFPU 0x366 MIPS avec FPU
IMAGE_FILE_MACHINE_MIPSFPU16 0x466 MIPS16 avec FPU
IMAGE_FILE_MACHINE_POWERPC 0x1f0 Alimentation petit PC
IMAGE_FILE_MACHINE_POWERPCFP 0x1f1 Power PC avec prise en charge de la virgule flottante
IMAGE_FILE_MACHINE_R4000 0x166 MIPS little endian
IMAGE_FILE_MACHINE_SH3 0x1a2 Hitachi SH3
IMAGE_FILE_MACHINE_SH3DSP 0x1a3 DSP Hitachi SH3
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 WCE little-endian MIPS v2
Oui, vous pouvez vérifier IMAGE_FILE_MACHINE_AMD64 | IMAGE_FILE_MACHINE_IA64 pour 64 bits et IMAGE_FILE_MACHINE_I386 pour 32 bits.
Vous pouvez trouver un exemple d'implémentation C # ici pour le IMAGE_FILE_HEADER
Solution
Ouvrez la dll avec un éditeur hexadécimal, comme HxD
Si il y a un "dt" sur la 9ème ligne, il est 64 bits.
S'il y a un "L." sur la 9ème ligne c'est 32bit.
Les fichiers binaires 64 bits sont stockés au format PE32 +. Essayez de lire http://www.masm32.com/board/index.php?action=dlattach;topic=6687.0;id=3486
J'ai réécrit solution c ++ dans la première réponse en script PowerShell. Le script peut déterminer ces types de fichiers .exe et .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
cette solution présente certains avantages par rapport à corflags.exe et au chargement de Assembly via Assembly.Load en C # - vous ne recevrez jamais d’exception BadImageFormatException ni de message concernant un en-tête non valide.
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;
}
exemple d'utilisation:
#$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;
vous pouvez omettre le second paramètre si vous n'avez pas besoin de voir les détails
Une façon rapide et probablement sale de le faire est décrite ici: https://superuser.com/a/889267 . Vous ouvrez le DLL dans un éditeur et vérifiez les premiers caractères après la séquence "PE".
Apparemment, vous pouvez le trouver dans l'en-tête de l'exécutable portable. L'utilitaire corflags.exe est en mesure de vous indiquer s'il cible ou non x64. J'espère que cela vous aidera à trouver plus d'informations à ce sujet.