您的位置:首页 > 其它

如何判断exe或dll的目标平台及是否是.NET?

2014-10-29 15:12 357 查看
1.

COFF文件头中偏移0处的Machine指示目标机器类型(IMAGE_FILE_MACHINE_AMD64等),偏移18处的Characteristics位指示文件属性(IMAGE_FILE_32BIT_MACHINE0x0100,IMAGE_FILE_LARGE_ADDRESS_AWARE0x0020)。

但我们判断dll或exe支持的目标平台并不使用COFF头,而使用可选文件头(PE32,PE32+即位于此处),因为可选文件头用于为加载器提供信息

可选文件头分为3个部分:标准域,windows特定域和数据目录。PE32/PE32+,由位于标准域处的首个标识幻数(Magic),长度为2,它的可能值和含义为:

0x10b PE32可执行文件

0x107 一个ROM镜像

0x20b PE32+可执行文件

位于可选文件头标准域的magic标志位的值,也确定了标准域和特定域的大小。

标准域:PE32 28,PE32+24;(PE32比PE32+多了一个BaseOfData)

特定域:PE32 68,PE32+ 88。

数据目录:可变,可选文件头的总大小由COFF文件头中的SizeofOptionalHader指定。

数据目录的第15个即CLR Runtime Header,记录了CLR运行时头部的地址和大小。

从CLR运行时偏移16byte处的uint32即Corflags。

2.

VS2012生成的程序集使用的CORFLAGS版本是2.5; 早前版本都是2.0。

2.0版本的Corflags的标识值包含:

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,


它们和目标平台的关系是:

Any CPU: PE = PE32 and 32BIT = 0
x86: PE = PE32 and 32BIT = 1
64-bit: PE = PE32+ and 32BIT = 0


2.5版本中多了一个32BITPREF,含义如下。暂时未找到32BITPREF存放在哪位上(4.节已找到)。

CorFlags  : Hexadecimal value, computed based on below 4 flags.
ILONLY    : 1 if MSIL otherwise 0
32BITREQ  : 1 if 32-bit x86 only assembly otherwise 0
32BITPREF : 1 if 32-bit x86 only preferred in Any CPU architecture otherwise 0
Signed    : 1 if signed with strong name otherwise 0


3.

.NET 4.5(即CLR Header 2.5)的CorFlags新增的标志位32BITPREF貌似有点坑爹,引自msdn:

Sets the 32BITPREFERRED flag. The app runs as a 32-bit process even on 64-bit platforms. Set this flag only on EXE files. If the flag is set on a DLL, the DLL fails to load in 64-bit processes, and a BadImageigeFormatException exception is thrown. An EXE file with this flag can be loaded into a 64-bit process.

首先,它说设置了该标志的应用程序即使是在64位平台上也运行在32位环境下。

其次如果把DLL项目设置了该属性的话,会导致DLL无法被64位进程加载,并抛出异常。

再其次,设置了该标志位的EXE也可以被加载到一个64位进程中。

各种不确定。

4.

CorHdr.h记录了CLR文件头的结构。

用vs2012新建一个c++ win32 控制台项目,在ConsoleApplication1.cpp头部添加

#include <CorHdr.h>

然后使用Shift+Ctrl+G打开该H文件。

该文件中有一个CorPEKind枚举

typedef enum CorPEKind
{
peNot       = 0x00000000,   // not a PE file
peILonly    = 0x00000001,   // flag IL_ONLY is set in COR header
pe32BitRequired=0x00000002,  // flag 32BITREQUIRED is set and 32BITPREFERRED is clear in COR header
pe32Plus    = 0x00000004,   // PE32+ file (64 bit)
pe32Unmanaged=0x00000008,    // PE32 without COR header
pe32BitPreferred=0x00000010  // flags 32BITREQUIRED and 32BITPREFERRED are set in COR header
} CorPEKind;


这应该就是2.5版本的CORFLAGS位的含义。
注意这个CLR文件头结构体仍然叫IMAGE_COR20_HEADER,直接COPY出来如下:

// #ManagedHeader
//
// A managed code EXE or DLL uses the same basic format that unmanaged executables use call the Portable
// Executable (PE) format. See http://en.wikipedia.org/wiki/Portable_Executable or
// http://msdn.microsoft.com/msdnmag/issues/02/02/PE/default.aspx for more on this format and RVAs.
//
// PE files define fixed table of well known entry pointers call Directory entries. Each entry holds the
// relative virtual address (RVA) and length of a blob of data within the PE file. You can see these using
// the command
//
// link /dump /headers <EXENAME>
//
//
// Managed code has defined one of these entries (the 14th see code:IMAGE_DIRECTORY_ENTRY_COMHEADER) and the RVA points
// that the IMAGE_COR20_HEADER.  This header shows up in the previous dump as the following line
//
// // Managed code is identified by is following line
//
//             2008 [      48] RVA [size] of COM Descriptor Directory
//
// The IMAGE_COR20_HEADER is mostly just RVA:Length pairs (pointers) to other interesting data structures.
// The most important of these is the MetaData tables.   The easiest way of looking at meta-data is using
// the IlDasm.exe tool.
//
// MetaData holds most of the information in the IL image.  THe exceptions are resource blobs and the IL
// instructions streams for individual methods.  Intstead the Meta-data for a method holds an RVA to a
// code:IMAGE_COR_ILMETHOD which holds all the IL stream (and exception handling information).
//
// Precompiled (NGEN) images use the same IMAGE_COR20_HEADER but also use the ManagedNativeHeader field to
// point at structures that only exist in precompiled images.
//
typedef struct IMAGE_COR20_HEADER
{
// Header versioning
DWORD                   cb;
WORD                    MajorRuntimeVersion;
WORD                    MinorRuntimeVersion;

// Symbol table and startup information
IMAGE_DATA_DIRECTORY    MetaData;
DWORD                   Flags;

// The main program if it is an EXE (not used if a DLL?)
// If COMIMAGE_FLAGS_NATIVE_ENTRYPOINT is not set, EntryPointToken represents a managed entrypoint.
// If COMIMAGE_FLAGS_NATIVE_ENTRYPOINT is set, EntryPointRVA represents an RVA to a native entrypoint
// (depricated for DLLs, use modules constructors intead).
union {
DWORD               EntryPointToken;
DWORD               EntryPointRVA;
};

// This is the blob of managed resources. Fetched using code:AssemblyNative.GetResource and
// code:PEFile.GetResource and accessible from managed code from
// System.Assembly.GetManifestResourceStream.  The meta data has a table that maps names to offsets into
// this blob, so logically the blob is a set of resources.
IMAGE_DATA_DIRECTORY    Resources;
// IL assemblies can be signed with a public-private key to validate who created it.  The signature goes
// here if this feature is used.
IMAGE_DATA_DIRECTORY    StrongNameSignature;

IMAGE_DATA_DIRECTORY    CodeManagerTable;            // Depricated, not used
// Used for manged codee that has unmaanaged code inside it (or exports methods as unmanaged entry points)
IMAGE_DATA_DIRECTORY    VTableFixups;
IMAGE_DATA_DIRECTORY    ExportAddressTableJumps;

// null for ordinary IL images.  NGEN images it points at a code:CORCOMPILE_HEADER structure
IMAGE_DATA_DIRECTORY    ManagedNativeHeader;

} IMAGE_COR20_HEADER, *PIMAGE_COR20_HEADER;


红色标出的Flags,即记录CLR信息的位。将该值和CorPEKind枚举进行逻辑运算就能得到dll/exe的目标平台属性。

5.

在4.打开的CorHdr.h位于C:\Program Files (x86)\Windows Kits\8.0\Include\um,是windows sdk 8.0使用的版本。

6.0,7.0,7.1中的CorPEKind枚举定义如下

// PE file kind bits, returned by IMetaDataImport2::GetPEKind()
typedef enum CorPEKind
{
peNot       = 0x00000000,   // not a PE file
peILonly    = 0x00000001,   // flag IL_ONLY is set in COR header
pe32BitRequired=0x00000002, // flag 32BIT_REQUIRED is set in COR header
pe32Plus    = 0x00000004,   // PE32+ file (64 bit)
pe32Unmanaged=0x00000008    // PE32 without COR header
} CorPEKind;


另外在6.0的IMAGE_COR20_HEADER定义头部有一行注释称其为“COM+ 2.0 header structure”,后续版本都删掉了。

保存着.NET运行时信息的这个结构化数据,在Microsoft可执行文件和通用目标文件格式规范中称之为"CLR Runtime Header",在另一些地方又被成为 CLI Header,或者COR Header,这里又被称作COM+ 2.0 header,感觉也是醉了。

6. 32BitPre并不是0x10

在CorChr.h中定义了32BitPref的值是0x10

typedef enum CorPEKind
{
peNot       = 0x00000000,   // not a PE file
peILonly    = 0x00000001,   // flag IL_ONLY is set in COR header
pe32BitRequired=0x00000002,  // flag 32BITREQUIRED is set and 32BITPREFERRED is clear in COR header
pe32Plus    = 0x00000004,   // PE32+ file (64 bit)
pe32Unmanaged=0x00000008,    // PE32 without COR header
pe32BitPreferred=0x00000010  // flags 32BITREQUIRED and 32BITPREFERRED are set in COR header
} CorPEKind;


要注意的是,如果我们读取文件头获得了CorFlags的值corflags,如果对corflags和0x10逻辑求与,得到的并不是32BitPref。
也就是说虽然这里CorPEKind枚举定义的32BitPref是0x10,但corflags这个32位无符号整数的第9bit的含义并不是32BitPref。

可能它只是IMetaDataImport2::GetPEKind()方法的返回值,例如Assembly.ManifestModule.GetPEKind(..)返回的值之一。

通过把corflags的bit逐个打印出来,真正存放32BitPref的位像是第18位,即0x20000。试了试貌似是对的。

后来在CorChr.h中看到了COMIMAGE_FLAGS_32BITPREFERRED,也即是该值。

7.AnyCPU 32-bit Prefer 的条件不一定是PE==PE32 &&32BitReq==0&&32BitPref==1

使用VS提供的CorFlags工具查看AnyCPU 32bit Prefer的dll,得到的是:

PE:PE32
32BitReq:0
32BItPref:1


所以我们在代码中解析CorFlags位也是使用这个逻辑来判定目标平台是AnyCPU 32bit Prefer吗?

答案出人意料的为不是。通过解析CorFlags位需要使用如下逻辑:

PE==PE32 &&32BitReq==1&&32BitPref==1


这一点和CorFlags工具有冲突。之所以这样大约是为了COR2.5和COR2.0和的兼容,同时AnyCPU 32-bit Prefer的DLL又必须通知Loader是32优先。(笔者随时都在瞎猜)

结论来了:

通过读取文件头得到Corflag,判定AnyCPU 32-bit Prefer的条件是:

PE==PE32 &&32BitReq==1&&32BitPref==1

使用Corflags工具查看各标识,判定AnyCPU 32-bit Prefer的条件是:

PE==PE32 &&32BitReq==0&&32BitPref==1

8.

参考目录:

http://stackoverflow.com/tags/corflags/info
http://illuminatedcomputing.com/posts/2010/02/sorting-out-the-confusion-32-vs-64-bit-clr-vs-native-cs-vs-cpp/
http://msdn.microsoft.com/en-us/library/ms164699.aspx
http://blog.csdn.net/breaksoftware/article/category/1294269
http://stackoverflow.com/questions/18608785/how-to-interpret-the-corflags-flags/23614024#23614024
http://www1.huachu.com.cn/read/readbookinfo.asp?sectionid=1000001727
http://msdn.microsoft.com/en-us/magazine/cc301805.aspx
http://weblog.ikvm.net/2011/11/14/ManagedPEFileTypes.aspx
http://blogs.microsoft.co.il/sasha/2012/04/04/what-anycpu-really-means-as-of-net-45-and-visual-studio-11/
http://www.ntcore.com/files/dotnetformat.htm
http://apichange.codeplex.com/SourceControl/changeset/view/76c98b8c7311
http://msdn.microsoft.com/library/windows/hardware/gg463119.aspx
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐