PE结构浅析

知识导向:

程序最开始是存放在磁盘上的,运行程序首先需要申请4GB的内存,将程序从磁盘copy到内存,但不是直接复制,而是进行拉伸处理。



这也就是为什么会有一个文件中地址和一个VirtualAddress,即所谓的FOA和VA

RVA是相对地址,也就是相对于可选头中ImageBase的存放地址,文件中的VA都是RVA

先上pe总览图

:)好吧,看上去还是挺复杂的,但慢慢分析,还是可以大概分析清楚的

  • DOS_HEADER
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;

e_magic 用于标识是否是可执行文件

e_lfanew NT头的偏移,也就是说DOS头和NT头之间不是连续的,中间有一部分的空闲空间可用于存放说明信息

通过偏移得到NT头所在的位置,NT头中主要是文件头和可选头

NT头的第一个DWORD 是NT头签名,用于说明可执行文件的类型,例如PE32 PE64等

  • FileHeader 文件头
typedef struct _IMAGE_FILE_HEADER {
WORD Machine;
WORD NumberOfSections;
DWORD TimeDateStamp;
DWORD PointerToSymbolTable;
DWORD NumberOfSymbols;
WORD SizeOfOptionalHeader;
WORD Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

NumberOfSections 节的数量,用于解析节

SizeOfOptionalHeader 可选头的大小,32位为F0 64位为E0

比邻的是OptionalHeader 可选头,说是可选头,实际是必需的

typedef struct _IMAGE_OPTIONAL_HEADER {
//
// Standard fields.
// WORD Magic;
BYTE MajorLinkerVersion;
BYTE MinorLinkerVersion;
DWORD SizeOfCode;
DWORD SizeOfInitializedData;
DWORD SizeOfUninitializedData;
DWORD AddressOfEntryPoint;
DWORD BaseOfCode;
DWORD BaseOfData; DWORD ImageBase;
DWORD SectionAlignment;
DWORD FileAlignment;
WORD MajorOperatingSystemVersion;
WORD MinorOperatingSystemVersion;
WORD MajorImageVersion;
WORD MinorImageVersion;
WORD MajorSubsystemVersion;
WORD MinorSubsystemVersion;
DWORD Win32VersionValue;
DWORD SizeOfImage;
DWORD SizeOfHeaders;
DWORD CheckSum;
WORD Subsystem;
WORD DllCharacteristics;
DWORD SizeOfStackReserve;
DWORD SizeOfStackCommit;
DWORD SizeOfHeapReserve;
DWORD SizeOfHeapCommit;
DWORD LoaderFlags;
DWORD NumberOfRvaAndSizes;
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

ImageBase 程序加载到内存时,默认的存放位置,但有可能实际情况不一样。(可能设置的默认位置已经被占据了)

FileAlignment 文件在磁盘上的对齐大小

SectionAlignment 文件在内存中的对齐大小

什么是文件对齐?什么是内存对齐?为什么要对齐?

SizeOfImage 文件在内存中对齐后的大小

SizeOfHeaders 头和节表在内存对齐后的大小

  • SectionHeader
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc; //可以与实际不一致
DWORD VirtualAddress;
DWORD SizeOfRawData;
DWORD PointerToRawData;
DWORD PointerToRelocations;
DWORD PointerToLinenumbers;
WORD NumberOfRelocations;
WORD NumberOfLinenumbers;
DWORD Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

知识拓展

union联合体

联合体声明和结构体声明差不多,唯一的区别在于底层存储。union中的数据在底层按最大的元素分配一块内存,其余所有属性共享这一片内存。

可以参考:https://www.cnblogs.com/leezhxing/p/4619185.html



每个节的大小都一样,这也就是为什么文件头中需要设置节的数量,如果不设置,解析器循环读多少次节表才停止呢?

节存储的时候不是连续的,所以需要节表中确定节的位置和大小

DWORD   VirtualAddress;	//节在内存中的RVA
DWORD SizeOfRawData; //节在磁盘上的大小
DWORD PointerToRawData;//节在文件中的偏移

可选头中最后的16个表

- 导出表

- 导入表

- 重定位表

- 导入绑定表

#include<iostream>
#include<Windows.h>
using namespace std;
#define ESize 0x2000 //设定拓展节的大小 char FilePath[] = "E:\\Code\\DLL01\\Debug\\notepad.exe"; //Target File
char SaveFilePath[] = "E:\\Code\\DLL01\\Debug\\DLL03.dll"; //Save File char* Buffer;
int FileSize; PIMAGE_DOS_HEADER dosHeader;
PIMAGE_FILE_HEADER fileHeader;
PIMAGE_OPTIONAL_HEADER optionalHeader;
PIMAGE_SECTION_HEADER NewsectionHeader; void readFile2Buffer() { FILE* fp = fopen(FilePath,"rb");
fseek(fp, 0, SEEK_END);
FileSize = ftell(fp);
rewind(fp); Buffer = (char*)malloc(sizeof(char)*(FileSize+ ESize));
fread(Buffer,1,FileSize,fp); fclose(fp); } void readBuffer2File(int BufferSize,char* Buffer) {
FILE* fp = fopen(SaveFilePath, "wb");
fwrite(Buffer,1,BufferSize,fp);
fclose(fp);
} DWORD RVA2FOA(DWORD RVA) {
//在header节
if (RVA < optionalHeader->SizeOfHeaders) {
return RVA;
}
//在其他节
DWORD Alignment = optionalHeader->SectionAlignment;
for (int i = 0; i < fileHeader->NumberOfSections; i++) {
NewsectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)Buffer +dosHeader->e_lfanew + 4 + 20 + fileHeader->SizeOfOptionalHeader + i * 40);
if (RVA >= NewsectionHeader->VirtualAddress && RVA < (NewsectionHeader->VirtualAddress + (NewsectionHeader->SizeOfRawData / Alignment + 1) * Alignment)) {
return NewsectionHeader->PointerToRawData + RVA- NewsectionHeader->VirtualAddress;
}
}
} DWORD FOA2RVA(DWORD FOA) {
//在header节
if (FOA < optionalHeader->SizeOfHeaders) {
return FOA;
}
//在其他节
DWORD Alignment = optionalHeader->FileAlignment;
for (int i = 0; i < fileHeader->NumberOfSections; i++) {
NewsectionHeader = (PIMAGE_SECTION_HEADER)(Buffer+dosHeader->e_lfanew + 4 + 0x14 + fileHeader->SizeOfOptionalHeader + i * 40);
if (FOA >= NewsectionHeader->PointerToRawData && FOA < ( NewsectionHeader->PointerToRawData + (NewsectionHeader->SizeOfRawData / Alignment+1)* Alignment)) {
return FOA - NewsectionHeader->PointerToRawData+NewsectionHeader->VirtualAddress;
}
}
} void PEParse() {
if (*(WORD*)Buffer != IMAGE_DOS_SIGNATURE) {
cout << "Error Format" << endl;
return;
}
//dosHeader
dosHeader = (PIMAGE_DOS_HEADER)Buffer; if (*(PDWORD)(Buffer +dosHeader->e_lfanew) != IMAGE_NT_SIGNATURE) {
cout << "Not PE" << endl;
return;
}
//fileHeader
fileHeader = (PIMAGE_FILE_HEADER)(Buffer + dosHeader->e_lfanew + 4);
cout << "NumberOfSections:" << fileHeader->NumberOfSections<<endl;
cout << "SizeOfOptionalHeader:" << fileHeader->SizeOfOptionalHeader<<endl; //optionalHeader
optionalHeader = (PIMAGE_OPTIONAL_HEADER)(Buffer + dosHeader->e_lfanew + 24); //NewsectionHeader
for (int i = 0; i < fileHeader->NumberOfSections; i++) {
NewsectionHeader = (PIMAGE_SECTION_HEADER)(Buffer+dosHeader->e_lfanew + 4 + 0x14 + fileHeader->SizeOfOptionalHeader + i * 40);
cout << "************NewsectionHeader**************" << endl;
cout << "VirtualAddress:" << NewsectionHeader->VirtualAddress << endl;
cout << "PointerToRawData:" << NewsectionHeader->PointerToRawData << endl;
cout << "SizeOfRawData:" << NewsectionHeader->SizeOfRawData << endl;
}
cout << "************************************************" << endl;
cout << "********* PE 解析完毕 ************" << endl;
cout << "************************************************" << endl;
cout << endl;
cout << endl;
}
/*
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory
// IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 // (X86 usage)
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor
*/ DWORD ExportTable() {
/*
typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base; //Base of sequence
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions; // RVA from base of image DWORD
DWORD AddressOfNames; // RVA from base of image DWORD
DWORD AddressOfNameOrdinals; // RVA from base of image WORD
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;
*/
cout << "===============================" << endl;
cout << "IMAGE_DIRECTORY_ENTRY_BASERELOC" << endl;
cout << "===============================" << endl; if (optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size == 0) {
cout << "No export table!!!" << endl;
return 0;
} cout << "VirtualAddress:" << hex << optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress<<endl;
cout << "Size:" << hex << optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size << endl;
cout << sizeof(IMAGE_EXPORT_DIRECTORY) << endl; DWORD FOA = RVA2FOA(optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
PIMAGE_EXPORT_DIRECTORY ExportTable = (PIMAGE_EXPORT_DIRECTORY)(FOA + (DWORD)Buffer);
cout << "^^^^^^^^^^^^^Export Table^^^^^^^^^^^^^^" << endl;
cout << "Export Table Name:" << (char*)((DWORD)Buffer+RVA2FOA(ExportTable->Name)) << endl;
cout << "Base:" << ExportTable->Base << endl;
cout << "NumberOfFunctions:" << ExportTable->NumberOfFunctions << endl;
cout << "NumberOfNames:" << ExportTable->NumberOfNames << endl; cout << (DWORD)Buffer << endl;
cout << RVA2FOA(ExportTable->AddressOfNames) << endl; for (int i = 0; i < ExportTable->NumberOfNames; i++) {
cout << "==============Export Function============" << endl;
cout << "FunctionName:" << (char*)((DWORD)Buffer + RVA2FOA(*(DWORD*)((DWORD)Buffer + RVA2FOA(ExportTable->AddressOfNames) + i * 4))) << endl;
//cout << "FunctionOrd:" << (*(WORD*)((DWORD)Buffer + RVA2FOA(ExportTable->AddressOfNameOrdinals))+i*2) << endl;
cout << "FunctionAddr(RVA):" << *(DWORD*)((DWORD)Buffer + RVA2FOA(ExportTable->AddressOfFunctions) + 4 * (*(WORD*)((DWORD)Buffer + RVA2FOA(ExportTable->AddressOfNameOrdinals) + i * 2)))<<endl;
}
return (DWORD)ExportTable-(DWORD)Buffer;
} DWORD RelocationTable() {
cout << "===============================" << endl;
cout << "IMAGE_DIRECTORY_ENTRY_BASERELOC" << endl;
cout << "===============================" << endl; if (optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress == 0 && optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size == 0) {
cout << "Relocation Table Is Null" << endl;
return 0;
} DWORD VirtualAddress = optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
DWORD SizeOfBlock = optionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size; DWORD FOA = (DWORD)Buffer+RVA2FOA(VirtualAddress);
while (*((DWORD*)FOA) != 0 || *((DWORD*)FOA + 1) != 0) {
DWORD VirtualAddress = *((DWORD*)FOA);
DWORD SizeOfBlock = *((DWORD*)FOA + 1); cout << "*********************************" << endl;
cout << "VirtualAddress:" << hex <<VirtualAddress << endl;
for (int i = 0; i < (SizeOfBlock - 8) / 2; i++) {
WORD li = *((WORD*)(FOA + 8) + i);
printf("*%02X* RVA:%08X ATTR:%d\n", i, VirtualAddress + (li & 0x0fff), (li & 0xf000) >> 12);
}
FOA += SizeOfBlock;
}
return RVA2FOA(VirtualAddress); //返回重定位表在文件中的位置
} void ImportTable() {
DWORD importTable = (DWORD)Buffer + RVA2FOA(optionalHeader->DataDirectory[3].VirtualAddress);
//下一个导入表为全0导入表结束
while (1) {
PIMAGE_IMPORT_DESCRIPTOR table = (PIMAGE_IMPORT_DESCRIPTOR)importTable;
if (
table->FirstThunk == 0&&
table->OriginalFirstThunk == 0
) {
cout << "*******************" << endl;
cout << "End of Import Table" << endl;
cout << "*******************" << endl;
break;
} cout << (char*)((DWORD)Buffer + RVA2FOA(table->Name)) << endl;
bool isBounded = false;
if (table->TimeDateStamp == 0xffffffff)
isBounded = true; DWORD table1 = (DWORD)Buffer + RVA2FOA(table->OriginalFirstThunk);
DWORD table2 = (DWORD)Buffer + RVA2FOA(table->FirstThunk); cout << "-------------------------------" << endl;
while (1) {
cout << "FirstThunk:";
DWORD data = *(DWORD*)table2;
if (data == 0)
break;
if (isBounded) {
cout << (data & 0x7fffffff) << endl;
}
else if (((data & 0x80000000) >> 31) == 1)
cout << (data & 0x7fffffff);
else {
PIMAGE_IMPORT_BY_NAME tmp = (PIMAGE_IMPORT_BY_NAME)((DWORD)Buffer + RVA2FOA(data));
cout << "HINT:" << hex << tmp->Hint << "-";
cout << "Name:" << tmp->Name;
}
table2 += 4; cout << "OrigionalThunk:";
DWORD data1 = *(DWORD*)table1;
if (((data1 & 0x80000000) >> 31) == 1)
cout << (data1 & 0x7fffffff) << endl;
else {
PIMAGE_IMPORT_BY_NAME tmp = (PIMAGE_IMPORT_BY_NAME)((DWORD)Buffer + RVA2FOA(data1));
cout << "HINT:" << hex << tmp->Hint << "-";
cout << "Name:" << tmp->Name << endl;
}
table1 += 4;
}
importTable += sizeof(IMAGE_IMPORT_DESCRIPTOR);
}
} int main() {
//Copy File to FileBuffer
readFile2Buffer();
//PE Parse
PEParse();
/*
Print Export Table
*/
//ExportTable(); /*
Print Relocation Table
*/
//RelocationTable(); /*
Print Import Table
*/
//ImportTable(); //Free Buffer
free(Buffer);
return 0;
}

[PE]结构分析与代码实现的更多相关文章

  1. [PE结构分析] 10.基址重定位

    源代码如下: typedef struct _IMAGE_BASE_RELOCATION { DWORD VirtualAddress; DWORD SizeOfBlock; // WORD Type ...

  2. [PE结构分析] 6.IMAGE_SECTION_HEADER

    IMAGE_SECTION_HEADER 的源代码如下: typedef struct _IMAGE_SECTION_HEADER { BYTE Name[IMAGE_SIZEOF_SHORT_NAM ...

  3. [PE结构分析] 5.IMAGE_OPTIONAL_HEADER

    结构体源代码如下: typedef struct _IMAGE_OPTIONAL_HEADER { // // Standard fields. // +18h WORD Magic; // 标志字, ...

  4. PE结构分析(二)

    在表中,我们知道了0x01 4c对应的平台结构是i386: 我们接着分析下一个字段,给出PE结构图 向后推移2个字节,现在来到(图片高亮部分): 高亮部分对应IMAGE_NT_HEADERS结构的Nu ...

  5. [PE结构分析] 9.导出表 IMAGE_EXPORT_DIRECTORY

    typedef struct _IMAGE_EXPORT_DIRECTORY { DWORD Characteristics; // 未使用,总为0 DWORD TimeDateStamp; // 文 ...

  6. [PE结构分析] 8.输入表结构和输入地址表(IAT)

    在 PE文件头的 IMAGE_OPTIONAL_HEADER 结构中的 DataDirectory(数据目录表) 的第二个成员就是指向输入表的.每个被链接进来的 DLL文件都分别对应一个 IMAGE_ ...

  7. [PE结构分析] 7.相对虚拟地址(RVA)和文件偏移间的转换

    RVA是相对虚拟地址(Relative Virtual Address)的缩写.RVA是当PE 文件被装载到内存中后,某个数据位置相对于文件头的偏移量. 例如:导入表的位置和大小可以从PE文件头中IM ...

  8. [PE结构分析] 11.资源表结构

    资源表是一个树形结构,可以设置成2的31次方的层数,Windows 使用了3级: 类型->名称->语言 其中涉及到四个结构: Data Description Resource Direc ...

  9. PE头的应用---插入代码到EXE或DLL文件中

    三.代码实现(DELPHI版本),采用第三种方式实现代码插入. 1. 定义两个类,一个用来实现在内存中建立输入表:一个用来实现对PE头的代码插入. DelphiCode: const MAX_SECT ...

随机推荐

  1. Python学习笔记总结

    目录 Python学习笔记总结 前言 安装 数据类型 Hello,World 变量 字符串 首字母大写 全部小写 全部大写 Tab和换行符 格式化 去除空格 List列表 列表增删改查排序 遍历列表 ...

  2. .Net Minimal Api 介绍

    Minimal API是.Net 6中新增的模板,借助C# 10的一些特性以最少的代码运行一个Web服务.本文脱离VS通过VS Code,完成一个简单的Minimal Api项目的开发. 创建项目 随 ...

  3. 手把手从0到1:搭建Kubernetes集群

    搭建 k8s 集群网上很多教程,如果是手工部署或者实验环境可以直接使用 MiniKube 或者 Kind,来在本地启动简单的 Kubernetes 集群进行后面的学习即可.如果是使用 MiniKube ...

  4. SQL里ORDER BY 对查询的字段进行排序,字段为空不想排在最前

    在安字段排序时 空字段往往都是在最前,我只是想空字段在排序的后面,不为空的在前,这个如何修改呢 order by datatime desc   这样的句子也一样  不管是正排还是倒排  为空的都在最 ...

  5. SpringCloud远程服务调用

    笔记 在微服务中,若想要使用远程调用,需要引入spring-cloud-starter-openfeign(在使用注册中心的环境下) <dependency> <groupId> ...

  6. [luogu7417]Minimizing Edges P

    令$e_{G}(a)$和$o_{G}(a)$分别表示在图$G$中从1到$a$的长度为奇数/偶数的最短路(若该类最短路不存在则为$\infty$),不难得到有以下结论--$f_{G}(a,b)=\beg ...

  7. [atAGC106F]Figures

    考虑purfer序列,若生成树的pufer序列为$p_{i}$,则答案为$(\prod_{i=1}^{n}a_{i})\sum_{p}\prod_{i=1}^{n}\frac{(a_{i}-1)!}{ ...

  8. appScan安全软件的使用

    1.点击文件,新建 2.常规扫描 3.点击下一步 4.输入需要扫描的网站 5.下一步,如果有账号 密码可以使用记录. 6.选择缺省值,下一步 7.启动全面扫描.

  9. appdata 文件夹

    appdata file AppData 的位置在 c:\Users\{UserName}\Appdata ,它是从 Windows Vista 开始引入的,直至今天的 Windows 7, 8, 1 ...

  10. C++ 编译错误记录

    C++ _ZSt28__throw_bad_array_new_lengthv1 编译错误 出现场景:类似代码 vector<vector<int>> grid = {{1, ...