前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >PE文件和COFF文件格式分析——签名、COFF文件头和可选文件头3

PE文件和COFF文件格式分析——签名、COFF文件头和可选文件头3

作者头像
方亮
发布2019-01-16 10:32:25
1.2K0
发布2019-01-16 10:32:25
举报
文章被收录于专栏:方亮方亮

        《PE2》中介绍了一些可选文件头中重要的属性,为了全面起见,本文将会讲解那些不是那么重要的属性。虽然不重要,但是还是可以发现很多好玩的情况。首先看一下32位的可选文件头详细定义。(转载请指明来源于breaksoftware的CSDN博客)

代码语言:javascript
复制
typedef struct _IMAGE_OPTIONAL_HEADER {
    //
    // Standard fields.
    //

    WORD    Magic;
    BYTE    MajorLinkerVersion;
    BYTE    MinorLinkerVersion;
    DWORD   SizeOfCode;
    DWORD   SizeOfInitializedData;
    DWORD   SizeOfUninitializedData;
    DWORD   AddressOfEntryPoint;
    DWORD   BaseOfCode;
    DWORD   BaseOfData;

    //
    // NT additional fields.
    //

    DWORD   ImageBase;
    DWORD   SectionAlignment;
    DWORD   FileAlignment;
    WORD    MajorOperatingSystemVersion;
    WORD    MinorOperatingSystemVersion;
    WORD    MajorImageVersion;
    WORD    MinorImageVersion;
    WORD    MajorSubsystemVersion;
    WORD    MinorSubsystemVersion;
    DWORD   Win32VersionValue;
    DWORD   SizeOfImage;
    DWORD   SizeOfHeaders;
    DWORD   CheckSum;
    WORD    Subsystem;
    WORD    DllCharacteristics;
    DWORD   SizeOfStackReserve;
    DWORD   SizeOfStackCommit;
    DWORD   SizeOfHeapReserve;
    DWORD   SizeOfHeapCommit;
    DWORD   LoaderFlags;
    DWORD   NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

        64位版本和32位版本很类似:没有BaseOfCode属性;ImageBase、SizeofStackReserve、SizeOfStackCommit、SizeOfHeapReserve和SizeOfHeapCommit等5个属性由32位版的DWORD改成ULONGLONG。看下详细的64位版定义

代码语言:javascript
复制
typedef struct _IMAGE_OPTIONAL_HEADER64 {
    WORD        Magic;
    BYTE        MajorLinkerVersion;
    BYTE        MinorLinkerVersion;
    DWORD       SizeOfCode;
    DWORD       SizeOfInitializedData;
    DWORD       SizeOfUninitializedData;
    DWORD       AddressOfEntryPoint;
    DWORD       BaseOfCode;
    ULONGLONG   ImageBase;
    DWORD       SectionAlignment;
    DWORD       FileAlignment;
    WORD        MajorOperatingSystemVersion;
    WORD        MinorOperatingSystemVersion;
    WORD        MajorImageVersion;
    WORD        MinorImageVersion;
    WORD        MajorSubsystemVersion;
    WORD        MinorSubsystemVersion;
    DWORD       Win32VersionValue;
    DWORD       SizeOfImage;
    DWORD       SizeOfHeaders;
    DWORD       CheckSum;
    WORD        Subsystem;
    WORD        DllCharacteristics;
    ULONGLONG   SizeOfStackReserve;
    ULONGLONG   SizeOfStackCommit;
    ULONGLONG   SizeOfHeapReserve;
    ULONGLONG   SizeOfHeapCommit;
    DWORD       LoaderFlags;
    DWORD       NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER64, *PIMAGE_OPTIONAL_HEADER64;

        Magic字段是可选文件头幻数,0x10b是32位版,0x20b是64位版。注意该属性不能说明这个文件是64位文件还是32位文件,至于判断是多少位文件的方案我在《PE2》中已经有了说明。

        MajorLinkerVersion和MinorLinkerVersion分别对应于链接器的版本号,比如我电脑上VS2005编译的文件的这两个版本号是8.0;VS2008编译的是9.0;VS2010编译的是10.0。

        MajorOperatingSystemVersion和MinorOperatingSystemVersion是所需要的最低的系统版本号的主版本号和次版本号。我看了下我电脑上文件,基本上是4.0。

        MajorImageVersion和MinorImageVersion是映像文件的主版本号和次版本号。注意:我们在资源中定义的文件版本号不是通过这两个属性来体现的!目前我也没找到在VC工程设置中可以设置这两个属性的地方。

        Subsystem是该文件运行于的子系统信息。一般我们在windows平台上遇到的是2,它对应于IMAGE_SUBSYSTEM_WINDOWS_GUI。

        MajorSubsystemVersion和MinorSubsystemVersion是子系统的版本号。熟悉windows的朋友应该知道,微软刚开始设计系统时,是设计成一个平台性质——可以运行3个子系统(OS/2、POSIX和Windows)的系统。这个就是这两个属性的由来。

        SectionAlignment是当映像文件加载到内存中时节的对齐值,该大小使用字节来衡量的。它必须要大于我们之后介绍的FileAlignment。它的默认值是相应系统的页面大小。

        FileAlignment 是映像文件节中的对齐值,它也是用字节来衡量的。英文文档中说该字段的值要在2^9 ~ 2^16之间,我扫描了下我的系统,发现我系统中文件并不是如此,特别是sys文件,它们的FileAlignment小于2^9(512)。

        SizeOfCode是文件中代码段的总共大小。要注意一点,这个大小和.text的大小不一定一致,因为有些代码可能还保存在其他节中。如我电脑上AliAppLoader.exe文件,其SizeOfCode大小是0x1D600,而.text节大小只有0x1D400,另外的0x200是在.orpc这个节中。         SizeOfInitializedData是文件中所有已经初始化数据节的大小。和SizeOfCode一样,初始化数据不一定只在一个节中。

        SizeOfUninitializedData是文件中所有未初始化数据节的大小。和SizeOfCode一样,未初始化数据不一定只在一个节中。

        Win32VersionValue是保留字段,应该为0。那么目前这个字段就是程序不关心的了,我们可以利用这个位置保存一些私密信息。

        SizeOfImage的官方说明是该映像文件被加载入内存时的大小,理论上它应该是SectionAlignment的倍数。但是实际并非如此,我发现我电脑上很多文件的该字段不是SectionAlignment的倍数,而有时SizeOfImage是该文件在磁盘上的大小。可以见得这个不是一个关键字段。

代码语言:javascript
复制
        if ( m_lpFileEnd - m_lpFileStart != m_dwSizeOfImage ) {
            _ASSERT(FALSE);
        }

        SizeOfHeaders的官方解释是MS-DOS占位程序、PE文件头和节头的总大小,且其应该是FileAlignment的倍数。但是实际上,我发现我电脑上很多文件的该字段并非FileAlignment的倍数。        

代码语言:javascript
复制
        DWORD dwHeaderSize = (DWORD)(m_lpPEStart - m_lpFileStart) + (DWORD)(sizeof(IMAGE_FILE_HEADER)) 
            + (DWORD) ( m_FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER));

        DWORD dwTmp = m_dwFileAlignment - 1;
        dwHeaderSize = (dwHeaderSize + dwTmp) &~dwTmp;
        if ( dwHeaderSize != m_dwSizeOfHeaders ) {
            //_ASSERT(FALSE);
        }

        CheckSum字段是映像文件的校验和。其计算算法保存在imagehlp.dll中,导出函数名为CheckSumMappedFile。我发现我电脑上很多文件的该PE字段和计算出来的不等。官方解释说当驱动程序、在引导时被加载的Dll以及加载到关键windows进程中的DLL都需要校验该字段以确认其合法性。

代码语言:javascript
复制
typedef PIMAGE_NT_HEADERS(WINAPI *PCheckSumMappedFile)( PVOID, DWORD, PDWORD, PDWORD );
        HMODULE hModule = LoadLibrary(L"imagehlp.dll");
        if ( NULL == hModule ) {
            _ASSERT(FALSE);
        }
        PCheckSumMappedFile pCheckSumMappedFile = (PCheckSumMappedFile) GetProcAddress( hModule, "CheckSumMappedFile" );
        if ( NULL == pCheckSumMappedFile ) {
            _ASSERT(FALSE);
        }
        DWORD dwHeaderSum = 0;
        DWORD dwCheckSum = 0;
        PIMAGE_NT_HEADERS lpImgNtHeader = pCheckSumMappedFile( m_lpFileStart, (DWORD)(m_lpFileEnd - m_lpFileStart), &dwHeaderSum, &dwCheckSum );
        if ( m_dwCheckSum != dwCheckSum ) {
            // 不等的很多
            //_ASSERT(FALSE);
        }
        if ( NULL != hModule ) {
            FreeLibrary(hModule);
            hModule = NULL;
        }

        SizeOfStackReserve、SizeOfStackCommit、SizeOfHeapReserve和SizeOfHeapCommit分别对应于保留的栈大小、提交的栈大小、保留的堆大小和提交的堆大小。

        LoaderFlags字段是保留字段,应该为0,当然你可以不把它设为0。

        NumberOfRvaAndSizes是用来指明DataDirectory元素的个数。这儿我们要说一下,我们在IMAGE_FILE_HEADER::SizeOfOptionalHeader得到了可选文件头的大小,而影响可选文件头大小的就是DataDirectory元素的个数(NumberOfRvaAndSizes),那么IMAGE_FILE_HEADER::SizeOfOptionalHeader和NumberOfRvaAndSizes之间应该存在着一种换算关系。的确,理论上 换算关系是:

代码语言:javascript
复制
SizeOfOptionalHeader == offsetof(IMAGE_OPTIONAL_HEADER32(64),NumberOfRvaAndSizes) + NumberOfRvaAndSizes * sizeof(IMAGE_DATA_DIRECTORY)

        我跑了下我电脑上的所有文件,发现这个关系是一直成立的。这应该是种强关系。         DllCharacteristics是属性字段,我们看个官方说明

Constant

Value

Description

0x0001

Reserved, must be zero.

0x0002

Reserved, must be zero.

0x0004

Reserved, must be zero.

0x0008

Reserved, must be zero.

IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE

0x0040

DLL can be relocated at load time.

IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY

0x0080

Code Integrity checks are enforced.

IMAGE_DLL_CHARACTERISTICS_NX_COMPAT

0x0100

Image is NX compatible.

IMAGE_DLLCHARACTERISTICS_ NO_ISOLATION

0x0200

Isolation aware, but do not isolate the image.

IMAGE_DLLCHARACTERISTICS_ NO_SEH

0x0400

Does not use structured exception (SE) handling. No SE handler may be called in this image.

IMAGE_DLLCHARACTERISTICS_ NO_BIND

0x0800

Do not bind the image.

0x1000

Reserved, must be zero.

IMAGE_DLLCHARACTERISTICS_ WDM_DRIVER

0x2000

A WDM driver.

IMAGE_DLLCHARACTERISTICS_ TERMINAL_SERVER_AWARE

0x8000

Terminal Server aware.

        MAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE是说Dll可以在加载时被重定向,我发现我电脑上文件SDKDBLib.dll是特例,它没有设置这个属性,这个文件也没有设置IMAGE_DLLCHARACTERISTICS_ NO_SEH,即该文件不使用SEH。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2012年08月31日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档