PE文件结构(二)

Last updated on October 7, 2023 pm

PE文件结构(二) 节表

回顾

在此之前,我们已经知道PE文件结构前面几部分是由DOS头(通常把DOS stub也算入DOS头中)、NT头,NT头又可以分为Signature、标准PE头、可选PE头,紧接着可选PE头的就是节表。

节表

节表的索引

在解析节表之前,我们首先要知道如何在文件中找到这个节表

根据PE结构图有一个很朴素的思想去找到这个节表在文件中位置,即节表地址 = DOS头大小+Signature大小+标准PE头大小+可选PE头大小即可,然而借助于DOS头中 e_lfanew 字段我们可以直接跳到Signature处,此时变成 e_lfanew + Signature大小 +标准PE头大小+可选PE头大小

其中Signature大小是4个字节(PE的标识),标准PE头大小是固定的0x14字节,标准PE头中又有 SizeOfOptionalHeader字段标识可选头大小,所以节表地址就可以表示为 e_lfanew + 4 + 0x14 + SizeofOptionalHeader

节区数量

既然提到了表,就说明这是一个线性结构,像一个数组一样存储了若干小表的信息,这个小表其实就是一个结构体,也即图中的Image_Section_Table,自然大小也就是确定的。每一个结构体都包含了对应节区的相关信息,比如节区的名字、大小等。我们现在可以找到节表,但是怎么确定这个节表中有多少个Image_Section_Table

这就又要回想到之前的标准PE头,里面有一个字段NumberOfSections,顾名思义,就是存储了节表的数量。

那么现在有了节表的起始位置,又有了节区的数量,节表的大小,尽管不知道节表里的结构体都有什么成员,但是最起码现在可以确定每一个结构体的起始终止了

结构体成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];//8个字节 IMAGE_SIZEOF_SHORT_NAME是宏 等于8 一般是ascii码 节区名字
union {
DWORD PhysicalAddress;
DWORD VirtualSize;
} Misc; // 注意是联合体,其含义是没有对齐的在内存中真实尺寸 该值可以不准确
DWORD VirtualAddress; // 节区在内存中的偏移地址,是RVA
DWORD SizeOfRawData; // 节区在文件中对齐后的尺寸
DWORD PointerToRawData; // 节区在文件中偏移
DWORD PointerToRelocations; // 在obj文件中使用 对exe无意义
DWORD PointerToLinenumbers; // 行号表的位置 调试时使用
WORD NumberOfRelocations; // 在obj中使用 对exe无意义
WORD NumberOfLinenumbers; // 行号表的数量 调试时使用
DWORD Characteristics; // 节区属性 可写可读可执行等 通过或的形式来添加属性
//0x00000020 含可执行代码 0x20000000 该块可执行 0x40000000 可读 0x80000000 可写
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

以一个对比图来说明

左边是文件形式,右边是运行时形式

  • Name - 从A开始的8个字节是名字
  • Misc - 从A到B的大小,可以不准确
  • VirtualAddress - 右图中 ImageBase到A的距离
  • SizeOfRawData - 左图中绿色框的大小
  • PointerToRawData - 左图中0到A的距离

代码查找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include<stdio.h>
#include<windows.h>

DWORD Size(FILE* fp){
DWORD size = 0;
fseek(fp,0,SEEK_END);
size = ftell(fp);
fseek(fp,0,SEEK_SET);
return size;
}


void Section_Info(FILE* fp,char* buffer){
PIMAGE_DOS_HEADER DosHeader = NULL;
PIMAGE_NT_HEADERS NTHeader = NULL;
PIMAGE_FILE_HEADER FileHeader= NULL;
PIMAGE_OPTIONAL_HEADER OptionalHeader = NULL;
PIMAGE_SECTION_HEADER SectionHeader = NULL;

DosHeader = (PIMAGE_DOS_HEADER)buffer;
NTHeader = (PIMAGE_NT_HEADERS)(buffer + DosHeader->e_lfanew);
FileHeader = (PIMAGE_FILE_HEADER)(buffer + DosHeader->e_lfanew + 4);
OptionalHeader = (PIMAGE_OPTIONAL_HEADER)(buffer + DosHeader->e_lfanew + 4 +IMAGE_SIZEOF_FILE_HEADER);
SectionHeader = (PIMAGE_SECTION_HEADER)(buffer + DosHeader->e_lfanew + 4 +IMAGE_SIZEOF_FILE_HEADER + FileHeader->SizeOfOptionalHeader);

printf("=================Section PE_INFO=================\n");
for(int i=0;i<FileHeader->NumberOfSections;i++){
printf("The %dth sec\n",i+1);
printf("name:%s\n",SectionHeader->Name);
printf("VitualAddress:%08x\n", SectionHeader->VirtualAddress);
printf("SizeOfRawData:%08x\n", SectionHeader->SizeOfRawData);
printf("PointerToRawRata:%08x\n", SectionHeader->PointerToRawData);
printf("Characteristic:%X\n", SectionHeader->Characteristics);
printf("==================================\n");
SectionHeader = (PIMAGE_SECTION_HEADER)(buffer + DosHeader->e_lfanew + 4 + IMAGE_SIZEOF_FILE_HEADER + FileHeader->SizeOfOptionalHeader + 40 * (i + 1));
}
}
int main(){
char* buffer;

FILE* fp = NULL;
errno_t err_1 = fopen_s(&fp, "YOU PATH TO EXE HERE", "rb");

DWORD size = Size(fp);
buffer = (char*)malloc(size);
fread(buffer,size,1,fp);

Section_Info(fp,buffer);
return 0;
}

PE文件结构(二)
http://example.com/2023/07/01/PE_1/
Author
yring
Posted on
July 1, 2023
Licensed under