PE文件结构
PE文件是portable File Format(可移植文件)的简写
PE的解释:PE文件是指某一种格式的文件 比如可执行文件(exe) 动态链接库文件(dll) 驱动文件(sys)等
PE文件大概分为两部分:头与主体 两部分会在内部进行细分
PE格式文件的组成:
DOS头部: 为兼容DOS程序设立
NT头部 : 存储PE文件的全部属性 初始化信息等
区段头表: 对于PE文件主体属性的分段描述 个数不定
各个区段: PE文件的主体 分别存储着可执行代码的各种数据 资源等
一些调试信息:未知
========================================================
RVA转FOA的实例代码:
DWORD RVAtoFOA(DWORD dwRva, char * buf)
{
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)buf;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + buf);//先获取NT的RVA
DWORD dwSectionCount = pNt->FileHeader.NumberOfSections;//获取区段的数量
PIMAGE_SECTION_HEADER pSec = IMAGE_FIRST_SECTION(pNt);//找到第一个区段的地址
for (DWORD i = 0; i < dwSectionCount; i++)
{
//判断形参1是否在在该区段中
if (dwRva >= pSec->VirtualAddress && dwRva < pSec->VirtualAddress + pSec->SizeOfRawData)
{
return dwRva - pSec->VirtualAddress + pSec->PointerToRawData;
}
pSec++;//找到下一个区段 继续循环
}
return 0;
}
参数1:需要转换的RVA
参数2:这段内存的起始地址(4A5D)
返回值:FOA的地址 DWORD类型
DOS头:
结构体说明:
DOS头一般占64个字节0x40 它之后就是DOS stub
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; // PE文件的标识
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; // NT头部的偏移地址
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
该头部两个字段有用: e_magic 与 e_lfanew
- e_magic 是一个标记 DOS头标志位 其值恒为 5A4D 系统宏定义为 IMAGE_DOS_SIGNATURE
- e_lfanew 意思为新exe文件的偏移 它表示NT头部在文件中的偏移
使用:
e_magic字段 可以作为判断该文件是否为PE文件 如果e_magic成员不是IMAGE_DOS_SIGNATRUE 那么它就不是一个PE文件
if((PIMAGE_DOS_HEADER)pFile->e_magic != IMAGE_DOS_SIGNATRUE)
return false;
e_lfanew 可以用来算出NT头在文件中的偏移 找到地址 e_lfanew一般都为0x100
PIMAGE_NT_HEADER pNt = (PIMAGE_NT_HEADER)(pFile+pFile->e_lfanew);
DOS与NT头部之间:
这两个头部之间存在一些区域 存储着一些被DOS头使用的数据 因为这部分数据的大小不确定 所以NT头部的地址需要用DOS头的 e_lfanew 字段来确定 没有DOS stub程序也可以正常执行
NT头部:
结构体说明:
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature; //标记 用于判断文件是否为PE文件
IMAGE_FILE_HEADER FileHeader; //文件头 存储着PE文件的基本信息
IMAGE_OPTIONAL_HEADER32 OptionalHeader; //扩展头 存储着关于PE文件加载时的信息
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32
一些字段的解释:
- Signature:如果是PE文件 标记的值为 0x00004550 其ASCII值为 PE00 系统宏定义:IMAGE_NT_SIGNATURE
其实该字段与DOS头部中e_magic字段作用以及用法都差不多 可以用来判断是否为PE文件
文件头:(在NT头中)FileHeader
文件头占24个字节
结构体说明:
typedef struct _IMAGE_FILE_HEADER {
WORD Machine; //文件的运行平台
WORD NumberOfSections; //区段的数量 最大为96
DWORD TimeDateStamp; //文件创建时间
DWORD PointerToSymbolTable; //符号表转移
DWORD NumberOfSymbols; //符号个数
WORD SizeOfOptionalHeader; //扩展头的大小
WORD Characteristics; //PE文件的属性
} IMAGE_FILE