设为首页 加入收藏

TOP

2.2 PE结构:文件头详细解析(一)
2023-09-09 10:25:33 】 浏览:222
Tags:2.2 结构 文件头 解析

PE结构是Windows系统下最常用的可执行文件格式,理解PE文件格式不仅可以理解操作系统的加载流程,还可以更好的理解操作系统对进程和内存相关的管理知识,DOS头是PE文件开头的一个固定长度的结构体,这个结构体的大小为64字节(0x40)。DOS头包含了很多有用的信息,该信息可以让Windows操作系统使用正确的方式加载可执行文件。从DOS文件头IMAGE_DOS_HEADERe_lfanew字段向下偏移003CH的位置,就是真正的PE文件头的位置,该文件头是由IMAGE_NT_HEADERS结构定义的,IMAGE_NT_HEADERS是PE文件格式的一部分,它包含了PE头和可选头的信息,用于描述PE文件的结构和属性。

2.2 DOS文件头详细解析

DOS头是PE文件开头的一个固定长度的结构体,这个结构体的大小为64字节(0x40)。DOS头包含了很多有用的信息,该信息可以让Windows操作系统使用正确的方式加载可执行文件。一个DOS头通常会包含以下一些主要信息:

  • Magic Number: 接下来64字节的文件内容的开始是以MZ(Mark Zbikowski)2个字符(即0x4D, 0x5A)开头,被称为DOS签名。
  • PE头偏移:DOS头中的e_lfanew(这是一个类型为LONG的成员)指示了PE头的偏移量,即PE头的起始位置距离DOS头的偏移量,Windows操作系统根据DOS头的这个属性来定位PE头的位置。
  • DOS头结束标识:保留用于以后增加的内容, 用于确认DOS头的结束,通常被赋值给字节0x0B。

如上图所示,图中的4D5A则表示这是一个PE文件,其下08010000则代表DOS头的最后一个数据集e_lfanew字段,该字段指向了PE头的开始50450000用于表示NT头的其实位置,而途中的英文单词则是一个历史遗留问题,在某些时候可通过删除此标识已让PE文件缩小空间占用,总的来说DOS头是PE文件中的一个重要的标志,它使得Windows操作系统能够在正确的位置开始加载可执行文件。由于DOS头中包含了PE头的偏移位置,Windows操作系统可以很容易地找到PE头,并通过PE头来加载程序并执行。

DOS头结构时PE文件中的重要组成部分,PE文件中的DOS部分由MZ格式的文件头和可执行代码部分组成,可执行代码被称为DOS块(DOS stub),MZ格式的文件头由IMAGE_DOS_HEADER结构定义,在C语言头文件winnt.h中有对这个DOS结构详细定义,如下所示:

typedef struct _IMAGE_DOS_HEADER { 
    WORD   e_magic;                     // DOS的头部
    WORD   e_cblp;                      // Bytes on last page of file
    WORD   e_cp;                        // Pages in file
    WORD   e_crlc;                      // Relocations
    WORD   e_cparhdr;                   // Size of header in paragraphs
    WORD   e_minalloc;                  // Minimum extra paragraphs needed
    WORD   e_maxalloc;                  // Maximum extra paragraphs needed
    WORD   e_ss;                        // Initial (relative) SS value
    WORD   e_sp;                        // Initial SP value
    WORD   e_csum;                      // Checksum
    WORD   e_ip;                        // Initial IP value
    WORD   e_cs;                        // Initial (relative) CS value
    WORD   e_lfarlc;                    // File address of relocation table
    WORD   e_ovno;                      // Overlay number
    WORD   e_res[4];                    // Reserved words
    WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
    WORD   e_oeminfo;                   // OEM information; e_oemid specific
    WORD   e_res2[10];                  // Reserved words
    LONG   e_lfanew;                    // 指向了PE文件的开头(重要)
  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

在DOS文件头中,第一个字段e_magic被定义为MZ,标志着DOS文件的开头部分,最后一个字段e_lfanew则指明了PE文件的开头位置,现在来说除了第一个字段和最后一个字段有些用处,其他字段几乎已经废弃,当读者通过调用OpenPeFile打开一个PE文件时,则下一步我们需要实现对PE文件有效性及位数的判断,并以此作为参考在后续的解析中使用不同的变量长度。

首先将镜像转换为PIMAGE_DOS_HEADER格式,并通过pDosHead->e_magic属性找到PIMAGE_NT_HEADERS结构,然后判断其是否符合PE文件规范,这里需要注意32位于64位PE结构所使用的的结构定义略有不同,代码中已经对其进行了区分。

BOOL IsPeFile(HANDLE ImageBase, BOOL Is64 = FALSE)
{
    PIMAGE_DOS_HEADER pDosHead = NULL;
    if (ImageBase == NULL)
        return FALSE;

    // 将映射文件转为DOS结构,并判断开头是否为MZ
    pDosHead = (PIMAGE_DOS_HEADER)ImageBase;
    if (IMAGE_DOS_SIGNATURE != pDosHead->e_magic)
        return FALSE;

    if (Is64 == TRUE)
    {
        // 根据 IMAGE_DOS_HEADER 的 e_lfanew 的值得到 64位 NT 头的位置
        PIMAGE_NT_HEADERS64 pNtHead64 = NULL;
        pNtHead64 = (PIMAGE_NT_HEADERS64)((DWORD64)pDosHead + pDosHead->e_lfanew);
        if (pNtHead64->Signature != IMAGE_NT_SIGNATURE)
            return FALSE;
    }
    else if (Is64 == FALSE)
    {
        // 根据 IMAGE_DOS_HEADER 的 e_lfanew 的值得到 32位 NT 头的位置
        PIMAGE_NT_HEADERS pNtHead32 = NULL;
        pNtHead32 = (PIMAGE_NT_HEADERS)((DWORD)pDosHead + pDosHead->e_lfanew);
        if (pNtHead32->Signature != IMAGE_NT_SIGNATURE)
            return FALSE;
    }
    return TRUE;
}

int main(int argc, char * argv[])
{
    BOOL PE = IsPeFile(OpenPeFile("c://pe/x86.exe"), 0);

    if (PE == TRUE)
    {
        printf("程序是标准的PE文件 \n");
    }
    else
    {
        printf("非标准程序 \n");
    }

    system("pause");
首页 上一页 1 2 3 4 5 6 下一页 尾页 1/6/6
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇C++系列十:日常学习-元编程 下一篇C++的编译链接与在vs中build提速

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目