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 =
x86: PE = PE32 and 32BIT =
-bit: PE = PE32+ and 32BIT =

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:
32BItPref:

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

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

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

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

结论来了:

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

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

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

PE==PE32 &&32BitReq==&&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

如何判断exe或dll的目标平台及是否是.NET?的更多相关文章

  1. 检查.net dll构建的目标平台是any cpu、x86、x64

    有时候,需要检查构建的dll是否针对正确的平台 可以使用CorFlags.exe(它是.NET Framework SDK的一部分)从dll中查找此信息.运行CorFlags.exe将产生以下输出: ...

  2. QT5.7.0在win10下使用visual studio 2015编译(目标平台 xp)

    环境:win10+vs2015+QT5.7.0 目标:编译出能在windows xp上运行的QT 通过baidu和bing找出来的结果没有一个能成功运行,大部分都能编译成功,并完美解决“exe不是有效 ...

  3. [转]C#程序无法在64位系统上运行之.NET编译的目标平台

    今天将编译的C#的exe拷贝到测试机上(Win7_64bit),一运行就挂了,提示“stop working”,一开始怀疑测试机上没有安装.net framework框架,追究半天原来是编译的目标平台 ...

  4. .NET编译的目标平台(AnyCPU,x86,x64)

    转载:http://blog.sina.com.cn/s/blog_78b94aa301014i8r.html 今天有项目的代码收到客户的反馈,要求所有的EXE工程的目标平台全部指定成x86,而所有D ...

  5. 关于.NET编译的目标平台(AnyCPU,x86,x64)

    转载:http://blog.sina.com.cn/s/blog_78b94aa301014i8r.html 今天有项目的代码收到客户的反馈,要求所有的EXE工程的目标平台全部指定成x86,而所有D ...

  6. 关于.NET编译的目标平台(AnyCPU,x86,x64) (转)

    关于.NET编译的目标平台(AnyCPU,x86,x64)(转) 今天有项目的代码收到客户的反馈,要求所有的EXE工程的目标平台全部指定成x86,而所有DLL工程的目标平台全部指定成AnyCPU . ...

  7. 无法为目标平台“Microsoft.Data.Tools.Schema.Sql.Sql120DatabaseSchemaProvider”创建扩展管理器

    很久没写博客了,这段时间情绪不那么稳定,还是心态的问题... 就简单写个问题的解决方法吧,其实最近遇到的问题蛮多的,就拿这个解决过后又遇到的来写吧. 正如标题一样:VS2013 无法为目标平台“Mic ...

  8. PE头的应用---插入代码到EXE或DLL文件中

    三.代码实现(DELPHI版本),采用第三种方式实现代码插入. 1. 定义两个类,一个用来实现在内存中建立输入表:一个用来实现对PE头的代码插入. DelphiCode: const MAX_SECT ...

  9. 使用VS的生成事件命令行指令将生成的exe,dll文件复制到指定文件夹中

    VS预生成事件命令行 和 生成后事件命令行 宏说明 $(ConfigurationName)            当前项目配置的名称(例如,“Debug|Any CPU”). $(OutDir)   ...

随机推荐

  1. 服务器sh脚本自动登录(mac)

    一不小心自己也有三台虚拟机了,每次都ssh -username@ip 然后在输入密码着实蛋疼,第一反应时脚本登录,但是作为脚本是逐行执行命令,是无法做到等待一个密码提示出现在输入密码的.查到mac下可 ...

  2. Android Handler 最佳的理解资料

  3. jquery.extend方法

    jquery.extend()用来扩展jquery中方法,实现插件. 1.jQuery.extend函数详细用法! 扩展jQuery静态方法. 1$.extend({ 2test:function() ...

  4. 手动刷新magento的索引管理方法

    当我们网站商品很多的时候,比如有几千件,我们刷新Magento的索引管理(Index Management)经常会失败.那么后台刷新不了,我们还可以通过命令行来刷新. 使用命令行来刷新索引管理会极大降 ...

  5. SQL函数创建错误

    [Err] 1418 - This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration a ...

  6. redis-cluster 单个节点不可用

    背景: 公司的音视频 盗版检测业务,使用redis-cluster作为 key-value 数据库, 使用张图片的 特征hash值作key.因为数据量太大,机器有限,集群内没有slaver 问题描述 ...

  7. keras 入门之 regression

    本实验分三步: 1. 建立数据集 2. 建立网络并训练 3. 可视化 import numpy as np from keras.models import Sequential from keras ...

  8. 2-Spark高级数据分析-第二章 用Scala和Spark进行数据分析

    数据清洗时数据科学项目的第一步,往往也是最重要的一步. 本章主要做数据统计(总数.最大值.最小值.平均值.标准偏差)和判断记录匹配程度. Spark编程模型 编写Spark程序通常包括一系列相关步骤: ...

  9. dedecms 后台发布后的文章不能编辑出现一片空白的解决办法

    dede后台无法写新文章也无法编辑以前的文档,文章编辑的地方无法写入,出现无法显示该页面的问题,这是怎么回事?我的dede在ftp中换过文件夹,是不是跟这个有关?该如何解决这个问题? 以下修改是针对d ...

  10. JDBC查询数据库中的数据

    只用JDBC技术查询表中的全部内容时,需要使用查询全部的SQL语句,把查询结果放到List集合中. package qddx.JDBC; import java.util.*; import java ...