PE文件头格式解析

前言:

昨天写了一题de1ctf的题,发现要脱壳,手脱之后发现要iat修复,我就发现自己在这块知识缺失了,win逆向,好像一直都是打ctf,然后用逆向方法论去肝的

其他方面倒是没有很深入学习,但实际上win的一些思想沿用到移动端也是很不错的,所以接下来会把pe格式这块搞清楚,接下来再搞别的,不过估计没那么多精力玩了

pe文件格式

图解


发现从上往下,大体结构为dos头,dos体,nt头(魔数,文件头,可选头),节区表,各种节(如code section,data section等),接下来我会从上往下说明

DOS头

typedef struct _IMAE_DOS_HEADER {       
    WORD e_magic;        
    WORD e_cblp;
    WORD e_cp;
    WORD e_crlc;
    WORD e_cparhdr;
    WORD e_minalloc;
    WORD e_maxalloc;
    WORD e_ss;
    WORD e_sp;
    WORD e_csum;
    WORD e_ip;
    WORD e_cs;
    WORD e_lfarlc;
    WORD e_ovno;
    WORD e_res[4];
    WORD e_oemid;
    WORD e_oeminfo;
    WORD e_res2[10];
    LONG e_lfanew;       
} IMAGE_DOS-HEADER, *PIMAGE_DOS_HEADER;

重点关注,e_magic和e_lfanew两个字段,第一个是dos头的魔数MZ ,4D5A,第二个e_lfanew则为nt头的文件偏移

dos体

略,里面的内容即使都为空,都是不影响exe的功能的

nt头

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

Signature

这个其实就是"PE",占了4个字节

FileHeader(文件头)

typedef struct _IMAGE_FILE_HEADER {
  WORD  Machine;                    **        机器号     相对该结构的偏移0x00**
  WORD  NumberOfSections;           **重要成员 节区数量   相对该结构的偏移0x02**
  DWORD TimeDateStamp;              **        时间戳     相对该结构的偏移0x04**
  DWORD PointerToSymbolTable;       **        符号表偏移  相对该结构的偏移0x08**
  DWORD NumberOfSymbols;            **        符号表数量  相对该结构的偏移0x0C**
  WORD  SizeOfOptionalHeader;       **重要成员 可选头大小  相对该结构的偏移0x10**
  WORD  Characteristics;            **重要成员 PE文件属性  相对该结构的偏移0x12**
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

重点介绍几个字段

  1. Machine
    主要是exe可以运行在哪种架构的机器上
  2. NumberOfSections
    节区的数量
  3. SizeOfOptionalHeader
    可选头的大小,32位和64位的exe,可选头大小是不同的,32位为0xe0,64位则为0xf0
  4. Characteristics

可选头

typedef struct _IMAGE_OPTIONAL_HEADER {
  WORD                 Magic;                        **魔术字                     偏移0x00
  BYTE                 MajorLinkerVersion;           **链接器主版本                偏移0x02
  BYTE                 MinorLinkerVersion;           **链接器副版本                偏移0x03
  DWORD                SizeOfCode;                   **所有含代码的节的总大小       偏移0x04
  DWORD                SizeOfInitializedData;        **所有含初始数据的节的总大小    偏移0x08
  DWORD                SizeOfUninitializedData;      **所有含未初始数据的节的总大小  偏移0x0C    
  DWORD                AddressOfEntryPoint;          **程序执行入口地址             偏移0x10   重要
  DWORD                BaseOfCode;                   **代码节的起始地址             偏移0x14
  DWORD                BaseOfData;                   **数据节的起始地址             偏移0x18
  DWORD                ImageBase;                    **程序首选装载地址             偏移0x1C   重要
  DWORD                SectionAlignment;             **内存中节区对齐大小           偏移0x20   重要
  DWORD                FileAlignment;                **文件中节区对齐大小           偏移0x24   重要
  WORD                 MajorOperatingSystemVersion;  **操作系统的主版本号           偏移0x28
  WORD                 MinorOperatingSystemVersion;  **操作系统的副版本号           偏移0x2A
  WORD                 MajorImageVersion;            **镜像的主版本号               偏移0x2C
  WORD                 MinorImageVersion;            **镜像的副版本号               偏移0x2E
  WORD                 MajorSubsystemVersion;        **子系统的主版本号             偏移0x30
  WORD                 MinorSubsystemVersion;        **子系统的副版本号             偏移0x32
  DWORD                Win32VersionValue;            **保留,必须为0               偏移0x34
  DWORD                SizeOfImage;                  **镜像大小                    偏移0x38   重要
  DWORD                SizeOfHeaders;                **PE头大小                    偏移0x3C   重要
  DWORD                CheckSum;                     **校验和                      偏移0x40
  WORD                 Subsystem;                    **子系统类型                   偏移0x44
  WORD                 DllCharacteristics;           **DLL文件特征                  偏移0x46
  DWORD                SizeOfStackReserve;           **栈的保留大小                 偏移0x48
  DWORD                SizeOfStackCommit;            **栈的提交大小                 偏移0x4C
  DWORD                SizeOfHeapReserve;            **堆的保留大小                 偏移0x50
  DWORD                SizeOfHeapCommit;             **堆的提交大小                 偏移0x54
  DWORD                LoaderFlags;                  **保留,必须为0                偏移0x58
  DWORD                NumberOfRvaAndSizes;          **数据目录的项数               偏移0x5C
  IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
  1. AddressOfEntryPoint
    程序入口点,也是之后写shellcode需要修改的地方,是rva,需要加上imagebase才是虚拟地址
  2. ImageBase
    程序加载到内存的基地址
    3.SectionAlignment和FileAlignment
    都是用来对齐的,不过站在的角度是不同的,第一个是以加载在内存中,节区地址是sectionalignment的整数倍,第二个是文件的偏移,是需要是filealignment整数倍,是站在磁盘角度考虑的
    4.SizeOfImage
    加载到内存中所占的大小
    5.SizeOfHeaders;
    整个头部的大小,包括dos头dos体nt头,节区表

节区表

typedef struct _IMAGE_SECTION_HEADER {
  BYTE  Name[IMAGE_SIZEOF_SHORT_NAME];         **节区名                 偏移0x00
  union {
    DWORD PhysicalAddress;
    DWORD VirtualSize;                         **节区的虚拟大小          偏移0x08      重要
  } Misc;                                     
  DWORD VirtualAddress;                        **节区的虚拟地址          偏移0x0C      重要  
  DWORD SizeOfRawData;                         **节区在硬盘上的大小       偏移0x10      重要
  DWORD PointerToRawData;                      **节区在硬盘上的地址       偏移0x14      重要
  DWORD PointerToRelocations;                  **指向重定位项开头的地址   偏移0x18
  DWORD PointerToLinenumbers;                  **指向行号项开头的地址     偏移0x1C
  WORD  NumberOfRelocations;                   **节区的重定位项数         偏移0x20
  WORD  NumberOfLinenumbers;                   **节区的行号数            偏移0x22
  DWORD Characteristics;                       **节区的属性              偏移0x24       重要
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

1.VirtualSize
实际上加载到内存的节区大小
2.VirtualAddress
节区的虚拟地址,但是并不是va,而是rva
3.SizeOfRawData
节区在硬盘上的大小
3. PointerToRawData
节区在磁盘上相对于文件的偏移
4.Characteristics
节区的属性

编写解析器,解析pe文件头

#define _CRT_SECURE_NO_WARNINGS
#include "stdio.h"
#include"stdlib.h"
#include "windows.h"
#include "pe.h"
//typedef struct _IMAE_DOS_HEADER {        
//WORD e_magic;        **重要成员 相对该结构的偏移0x00**
//WORD e_cblp;
//WORD e_cp;
//WORD e_crlc;
//WORD e_cparhdr;
//WORD e_minalloc;
//WORD e_maxalloc;
//WORD e_ss;
//WORD e_sp;
//WORD e_csum;
//WORD e_ip;
//WORD e_cs;
//WORD e_lfarlc;
//WORD e_ovno;
//WORD e_res[4];
//WORD e_oemid;
//WORD e_oeminfo;
//WORD e_res2[10];
//LONG e_lfanew;        **重要成员 相对该结构的偏移0x3C**
//} IMAGE_DOS - HEADER, *PIMAGE_DOS_HEADER;

int  PrintPEDosHeader(PVOID pFileAddress)
{
	int ret = 0;
	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileAddress;

	printf("*********************DOS_Header STAR*********************");
	printf("Dos->e_magic   :%02X\n", pDosHeader->e_magic);
	printf("Dos->e_cblp    :%02X\n", pDosHeader->e_cblp); 
	printf("Dos->e_cp      :%02X\n", pDosHeader->e_cp);
	printf("Dos->crlc      :%02X\n", pDosHeader->e_crlc);
	printf("Dos->e_cparhdr :%02X\n", pDosHeader->e_cparhdr);
	printf("Dos->e_minalloc:%02X\n", pDosHeader->e_minalloc);
	printf("Dos->e_maxalloc:%02X\n", pDosHeader->e_maxalloc);
	printf("Dos->e_ss      :%02X\n", pDosHeader->e_ss);
	printf("Dos->e_sp      :%02X\n", pDosHeader->e_sp);
	printf("Dos->e_csum    :%02X\n", pDosHeader->e_csum);
	printf("Dos->e_ip      :%02X\n", pDosHeader->e_ip);
	printf("Dos->e_cs      :%02X\n", pDosHeader->e_cs);
	printf("Dos->e_lfarlc   :%02X\n", pDosHeader->e_lfarlc);
	printf("Dos->e_ovno     :%02X\n", pDosHeader->e_ovno);
	for (int i = 0; i < 4; i++)
	{
		printf("Dos->e_res[%d]   :%02X\n", i, pDosHeader->e_res[i]);
	}
	printf("Dos->e_oemid     :%02X\n", pDosHeader->e_oemid);
	printf("Dos->e_oeminfo     :%02X\n", pDosHeader->e_oeminfo);
	for (int i = 0; i < 10; i++)
	{
		printf("Dos->e_res2[%d]    :%02X\n", i, pDosHeader-> e_res2[i]);
	}
	printf("Dos->e_lfanew   :%04X", pDosHeader->e_lfanew); //NT头的文件偏移
	return ret;

	
}
int PrintPEFileHeader(PVOID pFileAddress)
{
	int ret = 0;
	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileAddress;
	PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pFileAddress + (DWORD)(pDosHeader->e_lfanew + 4));
	
	printf("*********FILE_HEADER START*********");
	printf("FileHeader->Machine     :%02X\n", pFileHeader->Machine);
	printf("FileHeader->NumberOfSections  :%02X\n", pFileHeader->NumberOfSections);
	printf("FileHeader->TimeDataStamp:    :%04X\n", pFileHeader->TimeDateStamp);
	printf("FileHeader->PointerToSymbolTable: %04X\n", pFileHeader->PointerToSymbolTable);
	printf("FileHeader->NumberOfSymbols   :%04X\n", pFileHeader->NumberOfSymbols);
	printf("FileHeader->SizeOfOptionalHeader :%02X\n", pFileHeader->SizeOfOptionalHeader);
	printf("FileHeader->Characteristics      :%02X\n", pFileHeader->Characteristics);

	printf("**********************FILE_HEADER END*************************");
	return ret;
	
}
int PrintPEOptionalHeader(PVOID pFileAddress)
{
	int ret = 0;
	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileAddress;
	PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + (DWORD)(pDosHeader->e_lfanew + 4));
	PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + sizeof(PIMAGE_FILE_HEADER));

	printf("****************OPTIONAL_HEADER32 STAR*************************\n");
	printf("OptionalHeader->Magic                        : %02X\n", pOptionalHeader->Magic);
	printf("OptionalHeader->MajorLinkerVersion           : %01X\n", pOptionalHeader->MajorLinkerVersion);
	printf("OptionalHeader->MinorLinkerVersion           : %01X\n", pOptionalHeader->MinorLinkerVersion);
	printf("OptionalHeader->SizeOfCode                   : %04X\n", pOptionalHeader->SizeOfCode);
	printf("OptionalHeader->SizeOfInitializedData        : %04X\n", pOptionalHeader->SizeOfInitializedData);
	printf("OptionalHeader->SizeOfUninitializedData      : %04X\n", pOptionalHeader->SizeOfUninitializedData);
	printf("OptionalHeader->AddressOfEntryPoint          : %04X\n", pOptionalHeader->AddressOfEntryPoint);
	printf("OptionalHeader->BaseOfCode                   : %04X\n", pOptionalHeader->BaseOfCode);
	printf("OptionalHeader->BaseOfData                   : %04X\n", pOptionalHeader->BaseOfData);
	printf("OptionalHeader->ImageBase                    : %04X\n", pOptionalHeader->ImageBase);
	printf("OptionalHeader->SectionAlignment             : %04X\n", pOptionalHeader->SectionAlignment);
	printf("OptionalHeader->FileAlignment                : %04X\n", pOptionalHeader->FileAlignment);
	printf("OptionalHeader->MajorOperatingSystemVersion  : %02X\n", pOptionalHeader->MajorOperatingSystemVersion);
	printf("OptionalHeader->MinorOperatingSystemVersion  : %02X\n", pOptionalHeader->MinorOperatingSystemVersion);
	printf("OptionalHeader->MajorImageVersion            : %02X\n", pOptionalHeader->MajorImageVersion);
	printf("OptionalHeader->MinorImageVersion            : %02X\n", pOptionalHeader->MinorImageVersion);
	printf("OptionalHeader->MajorSubsystemVersion        : %02X\n", pOptionalHeader->MajorSubsystemVersion);
	printf("OptionalHeader->MinorSubsystemVersion        : %02X\n", pOptionalHeader->MinorSubsystemVersion);
	printf("OptionalHeader->Win32VersionValue            : %04X\n", pOptionalHeader->Win32VersionValue);
	printf("OptionalHeader->SizeOfImage                  : %04X\n", pOptionalHeader->SizeOfImage);
	printf("OptionalHeader->SizeOfHeaders                : %04X\n", pOptionalHeader->SizeOfHeaders);
	printf("OptionalHeader->CheckSum                     : %04X\n", pOptionalHeader->CheckSum);
	printf("OptionalHeader->Subsystem                    : %02X\n", pOptionalHeader->Subsystem);
	printf("OptionalHeader->DllCharacteristics           : %02X\n", pOptionalHeader->DllCharacteristics);
	printf("OptionalHeader->SizeOfStackReserv            : %04X\n", pOptionalHeader->SizeOfStackReserve);
	printf("OptionalHeader->SizeOfStackCommit            : %04X\n", pOptionalHeader->SizeOfStackCommit);
	printf("OptionalHeader->SizeOfHeapReserve            : %04X\n", pOptionalHeader->SizeOfHeapReserve);
	printf("OptionalHeader->SizeOfHeapCommit             : %04X\n", pOptionalHeader->SizeOfHeapCommit);
	printf("OptionalHeader->LoaderFlags                  : %04X\n", pOptionalHeader->LoaderFlags);
	printf("OptionalHeader->NumberOfRvaAndSizes          : %04X\n", pOptionalHeader->NumberOfRvaAndSizes);

	printf("*****************OPTIONAL_HEADER32 END************************\n");

	return ret;
}
int PrintPESectionHeader(PVOID pFileAddress)
{
	int ret = 0;
	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileAddress;
	PIMAGE_FILE_HEADER pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + (DWORD)pDosHeader->e_lfanew + 4);
	PIMAGE_OPTIONAL_HEADER32 pOptionalHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + sizeof(IMAGE_FILE_HEADER));
	PIMAGE_SECTION_HEADER pSectionGroup = (PIMAGE_SECTION_HEADER)((DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader);

	printf("****************SECTION_HEADER STAR*************************\n");
	for (int i = 0; i < pFileHeader->NumberOfSections; i++)
	{
		printf("pSectionGroup[%d].Name                   : %s\n", i, pSectionGroup[i].Name);
		printf("pSectionGroup[%d].Misc.VirtualSize       : %04X\n", i, pSectionGroup[i].Misc.VirtualSize);
		printf("pSectionGroup[%d].VirtualAddress         : %04X\n", i, pSectionGroup[i].VirtualAddress);
		printf("pSectionGroup[%d].SizeOfRawData          : %04X\n", i, pSectionGroup[i].SizeOfRawData);
		printf("pSectionGroup[%d].PointerToRawData       : %04X\n", i, pSectionGroup[i].PointerToRawData);
		printf("pSectionGroup[%d].PointerToRelocations   : %04X\n", i, pSectionGroup[i].PointerToRelocations);
		printf("pSectionGroup[%d].PointerToLinenumbers   : %04X\n", i, pSectionGroup[i].PointerToLinenumbers);
		printf("pSectionGroup[%d].NumberOfRelocations    : %02X\n", i, pSectionGroup[i].NumberOfRelocations);
		printf("pSectionGroup[%d].NumberOfLinenumbers    : %02X\n", i, pSectionGroup[i].NumberOfLinenumbers);
		printf("pSectionGroup[%d].Characteristics        : %04X\n\n\n", i, pSectionGroup[i].Characteristics);
	}

	printf("*****************SECTION_HEADER END************************\n");

	return ret;
}
int main(int argc,char const* argv[])
{

		int ret = 0;
		PVOID pFileAddress=NULL;
		ret=MyReadFile(&pFileAddress);
		if (ret != 0)
		{
			return ret;
		}
		printf("将文件载入内存成功!");
		ret=PrintPEDosHeader(pFileAddress); //DOS头
		if (ret != 0)
		{
			if (pFileAddress != NULL)
			{
				free(pFileAddress);
			}
			return ret;
		}
		ret = PrintPEFileHeader(pFileAddress);
		if (ret != 0)
		{
			if (pFileAddress != NULL)
			{
				free(pFileAddress);
			}
			return ret;
		}
		ret = PrintPEOptionalHeader(pFileAddress);
		if (ret != 0)
		{
			if (pFileAddress != NULL)
			{
				free(pFileAddress);
			}
			return ret;
		}
		ret = PrintPESectionHeader(pFileAddress);
		if (ret != 0)
		{
			if (pFileAddress != NULL)
			{
				free(pFileAddress);
			}
			return ret;
		}	
}

之后还有一篇关于目录表的解析,之后代码都传github上

posted @ 2021-03-14 14:30  YenKoc  阅读(550)  评论(0编辑  收藏  举报