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;
}

最新文章

  1. Android 亮度调节
  2. [cross compile]cygwin和mingw
  3. Discuz DB层跨库映射关系表名前缀BUG修复后产生的新bug
  4. 【jmeter】元件的作用域与执行顺序
  5. 《Publish or Perish》——从某种角度来说,我们也算和世界同步了呢。
  6. table 的thead th 固定 tbody滚动例子
  7. NPOI组件集锦
  8. [wikioi]数字三角形
  9. HDU 4632 CF 245H 区间DP(回文)
  10. ZCTF-ARM64-Re300
  11. Oracle树反向查询的优化(转载)
  12. Java9新特性之——JShell
  13. 设计模式——单例设计模式(C++实现)
  14. 【原】用Java编写第一个区块链(二)
  15. Docker镜像构建的两种方式(六)--技术流ken
  16. [开发笔记]--把input框设置成font-size:0埋下的坑。
  17. tornado web框架简介
  18. scala-actor线程间通信
  19. HDU 2048:神、上帝以及老天爷(错排公式,递推)
  20. HTML表格元素

热门文章

  1. newusers 拷贝服务器A上的用户,批量添加到其它服务器
  2. Dataworks批量刷数优化方案探讨
  3. Jmeter 踩坑记录(七)
  4. Go语言并发模型 G源码分析
  5. vue+elementUI中单选框el-radio设置默认值和唯一标识某个单选框
  6. Ubuntu1804命令行安装vmtool
  7. k8s网络模型与集群通信
  8. CodeBlocks调试器缺少(gdb.exe)文件
  9. [cf1349D]Slime and Biscuits
  10. 小程序嵌套H5的方式和技巧(一)