zoukankan      html  css  js  c++  java
  • 如何判断exe或dll的目标平台及是否是.NET?

    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 Kits8.0Includeum,是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

  • 相关阅读:
    新浪微博OAuth2授权错误 error:redirect_uri_mismatch
    [Eclipse]自动注释功能
    android 官网访问不了
    [转]Java开发中的23种设计模式详解
    [转]JAVA Iterator 的用法
    [转] Java集合类详解
    MySQL(root用户)密码重置
    SpringMVC项目接入Springfox实战遇到的问题集合
    linux中mysql完整卸载命令操作
    linux-centos挂载新硬盘操作
  • 原文地址:https://www.cnblogs.com/mumuliang/p/4059610.html
Copyright © 2011-2022 走看看