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;
重点介绍几个字段
- Machine
主要是exe可以运行在哪种架构的机器上 - NumberOfSections
节区的数量 - SizeOfOptionalHeader
可选头的大小,32位和64位的exe,可选头大小是不同的,32位为0xe0,64位则为0xf0 - 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;
- AddressOfEntryPoint
程序入口点,也是之后写shellcode需要修改的地方,是rva,需要加上imagebase才是虚拟地址 - 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上