Как найти, если собственный DLL файл скомпилирован как x64 или x86?
Я хочу определить, выполняется ли собственная сборка как x64 или x86 из приложения управляемого кода (С#).
Я думаю, что он должен где-то в заголовке PE, так как загрузчик ОС должен знать эту информацию, но я не мог ее найти. Конечно, я предпочитаю делать это в управляемом коде, но если это необходимо, я могу использовать собственный С++.
Ответы
Ответ 1
Вы можете использовать DUMPBIN. Используйте флаг /headers
или /all
и его первый заголовок файла.
dumpbin /headers cv210.dll
64-битный
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
32-битный
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
'find' может облегчить жизнь:
dumpbin /headers cv210.dll |find "machine"
8664 machine (x64)
Ответ 2
Существует простой способ сделать это с помощью CorFlags. Откройте командную строку Visual Studio и введите "corflags [ваша сборка]". Вы получите что-то вроде этого:
c:\Program Files (x86)\Microsoft Visual Studio 9.0\VC > corflags "C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Data.dll"
Microsoft (R).NET Framework CorFlags Конверсионный инструмент. Версия 3.5.21022.8 Copyright (c) Корпорация Microsoft. Все права защищены.
Версия: v2.0.50727 Заголовок CLR: 2.5 PE: PE32 CorFlags: 24 ОДНО: 0 32BIT: 0 Подпись: 1
Вы смотрите конкретно на PE и 32BIT.
-
Любой процессор:
PE: PE32
32BIT: 0
-
x86:
PE: PE32
32BIT: 1
-
64
PE: PE32 +
32BIT: 0
Ответ 3
Поле Magic
IMAGE_OPTIONAL_HEADER
(хотя в заголовке в исполняемых файлах Windows (файлы DLL/EXE) нет ничего необязательного в заголовке) сообщит вам об архитектуре PE.
Вот пример захвата архитектуры из файла.
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 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();
}
}
Единственные две константы архитектуры на данный момент:
0x10b - PE32
0x20b - PE32+
Приветствия
UPDATE
Прошло некоторое время с тех пор, как я опубликовал этот ответ, но все же вижу, что он получает несколько оборотов снова и снова, поэтому я решил, что стоит обновить. Я написал способ получить архитектуру образа Portable Executable
, которая также проверяет, скомпилирована ли она как AnyCPU
. К сожалению, ответ на С++, но не слишком сложно переносить на С#, если у вас есть несколько минут для поиска структур в WinNT.h
. Если люди заинтересованы, я напишу порт на С#, но, если люди на самом деле этого не хотят, я не трачу много времени на это.
#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;
}
Функция принимает указатель на образ PE в памяти (так что вы можете выбрать свой яд, как его получить, сопоставить память или прочитать все это в памяти... что угодно).
Ответ 4
Для неуправляемого DLL файла вам нужно сначала проверить, является ли он 16-разрядным DLL файлом (надеюсь, нет).
Затем проверьте поле IMAGE\_FILE_HEADER.Machine
.
Кто-то еще нашел время, чтобы это исправить, поэтому я просто повторю здесь:
Чтобы различать 32-битный и 64-разрядный PE файл, вы должны проверить IMAGE_FILE_HEADER.Machine. На основе Microsoft PE и COFF ниже, я перечислил все возможные значения для этого поля: http://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/pecoff_v8.doc
IMAGE_FILE_MACHINE_UNKNOWN 0x0 Предполагается, что содержимое этого поля применимо к любому типу машины
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 код байта EFI 0xebc
IMAGE_FILE_MACHINE_I386 0x14c Процессоры Intel 386 или более поздних версий и совместимые процессоры
IMAGE_FILE_MACHINE_IA64 0x200 семейство процессоров Intel Itanium
IMAGE_FILE_MACHINE_M32R 0x9041 Mitsubishi M32R little endian
IMAGE_FILE_MACHINE_MIPS16 0x266 MIPS16
IMAGE_FILE_MACHINE_MIPSFPU 0x366 MIPS с FPU
IMAGE_FILE_MACHINE_MIPSFPU16 0x466 MIPS16 с FPU
IMAGE_FILE_MACHINE_POWERPC 0x1f0 Power PC little endian
IMAGE_FILE_MACHINE_POWERPCFP 0x1f1 Power PC с поддержкой с плавающей запятой
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
Да, вы можете проверить IMAGE_FILE_MACHINE_AMD64 | IMAGE_FILE_MACHINE_IA64 для 64 бит и IMAGE_FILE_MACHINE_I386 для 32 бит.
Ответ 5
Этот трюк работает и требует только Блокнот.
Откройте файл dll с помощью текстового редактора (например, Блокнот) и найдите первое вхождение строки PE
. Следующий символ определяет, является ли dll 32 или 64 бит.
32 бит:
PE L
64 бит:
PE d†
Ответ 6
Вы можете найти реализацию примера С# здесь для решения IMAGE_FILE_HEADER
Ответ 7
64-битные двоичные файлы хранятся в формате PE32+. Попробуйте прочитать http://www.masm32.com/board/index.php?action=dlattach;topic=6687.0;id=3486
Ответ 8
Откройте DLL с шестнадцатеричным редактором, например HxD
Если на 9-й строке есть "dt", то она равна 64 бит.
Если есть "L." на 9-й строке - 32 бит.
Ответ 9
Быстрый и, вероятно, грязный способ сделать это описан здесь: https://superuser.com/a/889267. Вы открываете DLL в редакторе и проверяете первые символы после последовательности "PE".
Ответ 10
Очевидно, вы можете найти его в заголовке портативного исполняемого файла. Утилита corflags.exe может показать вам, стоит ли ей x64. Надеюсь, это поможет вам найти дополнительную информацию об этом.
Ответ 11
Я переписал решение С++ в первом ответе в powershell script. Script может определять эти типы файлов .exe и .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
это решение имеет некоторые преимущества перед corflags.exe и загрузкой сборки через Assembly.Load в С# - вы никогда не получите BadImageFormatException или сообщение о недопустимом заголовке.
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;
}
Пример использования :
#$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;
вы можете опустить второй параметр, если вам не нужно видеть детали