Windows PE文件结构笔记:初见

selph
selph
发布于 2020-08-20 / 2548 阅读
0
0

Windows PE文件结构笔记:初见

Windows PE文件结构初学,本次学习目标在于了解PE结构并能够解析出其中的内容。
内容如有不妥之处,希望大家能够在评论区或私信我指出,谢谢大家。
先挂个本文的思维导图:
PE文件结构
概述:
PE文件:Portable Executable,可移植的可执行文件,常见的有exe、dll、sys、com、ocx
PE结构包括:

  • MS-DOS头
  • 标准PE头
  • 扩展PE头
    • 数据目录
  • 节表

MS-DOS头结构解析

PE文件结构

MS-DOS头是为了兼容MS-DOS16位程序而保留,对于32位或者64位程序,就不会执行DOS头

MS-DOS头在每个可执行文件都有,用二进制编辑器(这里我用的010 Editor)随便打开一个exe文件:

image-20200805164957141

可以看到程序分了三个部分:

  • 最上面标红的四行就是MS-DOS头

  • 中间的部分是DOS-Stub部分:就是在DOS系统下所运行的东西

    在DOS系统下运行,会显示“This program cannot be run in DOS mode”

  • 下面那个标红的部分是PE头结构这一块了

MS-DOS头结构

这个结构,操作系统在winnt.h里已经定义了,在使用的时候包含windows.h头文件即可

#include<Windows.h>

MS-DOS头的结构体:

typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
    WORD   e_magic;                     // Magic number
    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;                    // File address of new exe header
  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

其中最重要的是第一个和最后一个

第一个是MS-DOS头的标志,判断是不是可执行文件,基本上都是4D5A

最后一个是指向PE头的字段的偏移量

使用C语言打印MS-DOS头信息

#include<stdio.h>
#include<Windows.h>

int main() {
	FILE* pFile = NULL;
	char* buffer;
	int nFileLength = 0;
	pFile = fopen("D:\\VS2019_repos\\cppbook\\Release\\cppbook.exe", "rb");
	fseek(pFile, 0, SEEK_END);
	nFileLength = ftell(pFile);
	rewind(pFile);
	int imageLength = nFileLength * sizeof(char) + 1;
	buffer = (char*)malloc(imageLength);
	memset(buffer, 0, nFileLength * sizeof(char) + 1);
	fread(buffer, 1, imageLength, pFile);

	PIMAGE_DOS_HEADER ReadDosHeader; //定义一个_IMAGE_DOS_HEADER结构体的指针
	ReadDosHeader = (PIMAGE_DOS_HEADER)buffer;	//都是指针,可以强制转换

	printf("MS-DOS Info:\n");
	printf("MZ标志位:%x\n", ReadDosHeader->e_magic);
	printf("PE头地址:%x\n", ReadDosHeader->e_lfanew);

	free(buffer);
    fclose(pFile);
	return 0;
}

PE头结构解析

PE头包括 标准PE头 和 扩展PE头

PE头结构

PE头结构体:分为64位和32位的

typedef struct _IMAGE_NT_HEADERS64 {
    DWORD Signature;
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER64 OptionalHeader;
} IMAGE_NT_HEADERS64, *PIMAGE_NT_HEADERS64;
typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;

他们的成员都有三个:

  • 第一个成员Signature,PE的标识是5045

    Virtual Studio提供了宏:IMAGE_NT_SIGNATURE 表示这个值,可以用宏来对比

  • 第二个成员FileHeader,标准的NT头

  • 第三个成员OptionalHeader是可选的头

从010Editor打开PE文件可以看到如下:

image-20200805185620524

标准头结构

typedef struct _IMAGE_FILE_HEADER {
    WORD    Machine;				//运行平台,也有宏来表示,	IMAGE_FILE_MACHINE_
    WORD    NumberOfSections;		//区段的数量
    DWORD   TimeDateStamp;			//文件创建时间,格里尼治时间
    DWORD   PointerToSymbolTable;	//符号表地址,用的少
    DWORD   NumberOfSymbols;		//符号表中符号的数量
    WORD