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

  • 相关阅读:
    LeetCode Best Time to Buy and Sell Stock
    LeetCode Scramble String
    LeetCode Search in Rotated Sorted Array II
    LeetCode Gas Station
    LeetCode Insertion Sort List
    LeetCode Maximal Rectangle
    Oracle procedure
    浏览器下载代码
    Shell check IP
    KVM- 存储池配置
  • 原文地址:https://www.cnblogs.com/mumuliang/p/4059610.html
Copyright © 2011-2022 走看看