PE 格式详解与试验

Posted Debroon

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PE 格式详解与试验相关的知识,希望对你有一定的参考价值。

 


可执行文件结构分析

可执行文件有:

  • windows 的 PE,如 .exe、.dll、.ocx、.sys、.com
  • Linux 的 ELF

PE 整体结构图:

几个重点:

  • DOS 部首的 header 头
  • PE 文件头的 file_header、optioal_header 头

具体结构:

 


DOS 部首的 header 头

DOS 部首的 header 头,是 PE 文件第一个结构,总共占 64 个字节。


第一个成员 e_magic,叫魔术,占 2 个字节,固定俩个字符 MZ 的内存编码,这是发明人的首字母缩写。

  • M 的内存编码:4D
  • Z 的内存编码:5A

但在 x86 平台,是低位优先,看到的是 5A4D,而不是 4D5A。

第二个成员 e_cbip,PE 文件最后那一页所包含的字节数。

第三个成员 e_cp,PE 文件包含多少页。

第四个成员 e_crlc,PE 文件有多少重地位个数。

第五个成员 e_cparhdr,PE 文件有多少断头。

第六个成员 e_minalloc,所需的最小附加段。

第七个成员 e_maxalloc,所需的最大附加段。

第八个成员 e_ss,栈底寄存器的初始值。

第九个成员 e_sp,栈顶寄存器的初始值。

第十个成员 e_csum,校验码,验证 PE 文件有木有被改。

第十一成员 e_ip,指令寄存器的初始值。

第十二成员 e_cs,段寄存器的初始值。

第十三成员 e_lfarlc,重定向表的文件地址。

第十四成员 e_ovno,覆盖号,早期内存小,运行大程序就需要这个。

第十五成员 e_res[4],保留字段。

第十六成员 e_oemid,oem 的 id。

第十七成员 e_oeminfo,oem 的信息。

第十八成员 e_res2[10],保留字段。

第十九成员 e_lfanew,PE 头的起始地址 0x0000003C。

  • 判断 PE 文件的有效性: e_magic(5A4D)、e_lfanew( PE,0,0,(x00004550) )

解析 DOS Header:

int peDosHeaderAnalyze(char *file)
{
	if(file==NULL)
		return -1;

	FILE *fp = NULL;
	IMAGE_DOS_HEADER dosheader;                  // PE 结构体公开的
	unsigned long pesig;
	
	fp = fopen(file, "r+b");                     // 打开文件
	if(fp == NULL)
		return -1;
	
	fread(&dosheader, sizeof(dosheader), 1, fp); // 读出 dos header 
	fseek(fp, dosheader.e_lfanew,SEEK_SET);      // 读出 dos header 最后一个成员 
	fread(&pesig, 4, 1, fp);                     // 读 4 个字节,PE 头签名 PE,0,0
	fclose(fp);
	
	printf("IMAGE_DOS_HEADER info:\\n");
	printf("e_magic  : %04x\\n", dosheader.e_magic); // "MZ"-->"ZM":0x5A4D
	printf("e_cblp  : %04x\\n", dosheader.e_cblp);
	printf("e_cp   : %04x\\n", dosheader.e_cp);
	printf("e_crlc  : %04x\\n", dosheader.e_crlc);
	printf("e_cparhdr : %04x\\n", dosheader.e_cparhdr);
	printf("e_minalloc: %04x\\n", dosheader.e_minalloc);
	printf("e_maxalloc: %04x\\n", dosheader.e_maxalloc);
	printf("e_ss   : %04x\\n", dosheader.e_ss);
	printf("e_sp   : %04x\\n", dosheader.e_sp);
	printf("e_csum  : %04x\\n", dosheader.e_csum);
	printf("e_ip   : %04x\\n", dosheader.e_ip);
	printf("e_cs   : %04x\\n", dosheader.e_cs);
	printf("e_lfarlc : %04x\\n", dosheader.e_lfarlc);
	printf("e_ovno  : %04x\\n", dosheader.e_ovno);
	printf("e_res[0] : %04x\\n", dosheader.e_res[0]);
	printf("e_oemid  : %04x\\n", dosheader.e_oemid);
	printf("e_oeminfo : %04x\\n", dosheader.e_oeminfo);
	printf("res2[0]  : %04x\\n", dosheader.e_res2[0]);
	printf("lfanew  : %08x\\n", dosheader.e_lfanew);
	
	return 0;
}

判断 PE 有效性:

bool isPeValid(char *file) {
	if(file == NULL)
		return FALSE;

	FILE *fp = NULL;
	IMAGE_DOS_HEADER dosheader;                   // PE 结构体公开的
	unsigned long pesig;
	
	fp = fopen(file, "r+b");                      // 打开文件
	if(fp == NULL)
		return false;
	
	fread(&dosheader, sizeof(dosheader), 1, fp);  // 读出 dos header 
	fseek(fp, dosheader.e_lfanew, SEEK_SET);      // 读出 dos header 最后一个成员 e_lfanew
	fread(&pesig, 4, 1, fp);                      // 读 4 个字节,PE 头签名 PE,0,0
	fclose(fp);
	
	if((dosheader.e_magic ==IMAGE_DOS_SIGNATURE) &&
		(pesig == IMAGE_NT_SIGNATURE))
		//dos header e_magic和PE头部里的signature必须为固定值:MZ(0x5A4D )和PE00(0x00004550)
		return true;

	return false;
}

调用:

int main(int argc, char* argv[])
{
	char *pefile="D:\\\\mfpedemo_x64debug.exe";

	if(isPeValid(pefile))
		peDosHeaderAnalyze(pefile);
	else
		printf("not a valid pe file\\n");

	return 0;
}

 


PE 文件头的 file_header

PE 文件头的 file_header 结构,三部分组成:

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;

#ifdef _WIN64
typedef  IMAGE_NT_HEADERS64 IMAGE_NT_HEADERS;
typedef PIMAGE_NT_HEADERS64 PIMAGE_NT_HEADERS;
#else
typedef  IMAGE_NT_HEADERS32 IMAGE_NT_HEADERS;
typedef PIMAGE_NT_HEADERS32 PIMAGE_NT_HEADERS;
#endif

 

IMAGE_FILE_HEADER 占 20 个字节:

typedef struct _IMAGE_FILE_HEADER {
 WORD Machine;                           // 运行平台
 WORD NumberOfSections;                  // 块数目
 DWORD TimeDataStamp;                    // 时间日期标记
 DWORD PointerToSymbolTable;             // COFF 符号指针,这是程序调试信息
 DWORD NumberOfSymbols;                  // 符号数
 WORD SizeOfOptionalHeader;              // 可选部首长度,是 IMAGE_FILE_HEADER 的长度
 WORD Characteristics;                   // 文件属性
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

 

Machine 指定运行平台:

#define IMAGE_FILE_MACHINE_UNKNOWN 0

#define IMAGE_FILE_MACHINE_I386 0x014c     // Intel 386.

#define IMAGE_FILE_MACHINE_ALPHA 0x0184    // Alpha_AXP

#define IMAGE_FILE_MACHINE_POWERPC 0x01F0  // IBM PowerPC Little-Endian

#define IMAGE_FILE_MACHINE_AMD64 0x8664    // AMD64(K8)

Characteristics 文件属性取值:

#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 
// Relocation info stripped from file.

#define IMAGE_FILE_ EXECUTABLE_IMAGE 0x0002 
// File is executable.

#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 
// Line nunbers stripped from file.

#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 
// Local symbols stripped from file.

#define IMAGE_FILE_AGGRESIVE_WS_TRIM 0x0010 
// Agressively trim working set

#define IMAGE_FILE_LARGE_ADDRESS_AMARE 0x0020 
// App can handle >2gb addresses

#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 
// Bytes of machine word are reversed.

#define IMAGE_FILE_32BIT_MACHINE 0x0100 
// 32 bit word nachine.

#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 
// Debugging info stripped .DBG file

#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 

#define IMAGE_FILE_NET_RUN _FROH _SWAP 0x0800
// If Image is on Net.

#define IMAGE_FILE_SYSTEM 0x1000 
// System File.

#define IMAGE_FILE_DLL 0x2000
// File is a DLL.

#define IMAGE_FILE_UP_SYSTEM_CNLY 0x4000

#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 
// Bytes of machine word are reversed.

从 PE 头进入 IMAGE_FILE_HEADER,只需要在成员 e_ifanew + 4 个字节即可。
 


PE 文件头的 Optioal_header

Optioal_header 分 32、64 位,俩者相差不大。


可以下载软件 CFF Explorer 直接查看。

 


PE RVA 地址与文件地址转换

 


块表

 


导入表

 


基址重定位

 


编程解析 PE 结构

 


ELF 格式

 


readelf 命令解析 ELF 格式

以上是关于PE 格式详解与试验的主要内容,如果未能解决你的问题,请参考以下文章

PE文件格式详解,第一讲,DOS头文件格式

PE文件格式详解,第二讲,NT头文件格式,以及文件头格式

PE文件格式详解

PE文件格式详解

PE文件格式详解,第三讲,可选头文件格式,以及节表

PE 文件格式 详解 二