联合体

1
2
3
4
union TestUnion {
char x;
int y;
};

特点

  1. 共享空间:联合体的所有成员共享同一内存空间。
  2. 内存大小:联合体的大小由最大成员的大小决定。例如,以上例子中,联合体的大小为4字节(int类型)。
  3. 有效性:联合体中最多只有一个成员是有效的,但仍然可以访问所有成员。
1
2
3
4
5
6
7
8
9
union TestUnion {
char x;
int y;
};

union {
char x;
int y;
} TestUnion;

节表(Section Table)

节表是Windows PE/COFF格式可执行文件中的一个重要数据结构,记录了各个代码段、数据段、资源段、重定向表等在文件中的位置和大小信息。操作系统通过节表进行各个段的映射和初始化。

在执行PE文件时,Windows不会立即将整个文件读入内存,而是由PE装载器建立虚拟地址与PE文件之间的映射关系。只有当执行到某个内存页中的指令或访问页中的数据时,才会将该页面从磁盘加载到内存。这种机制极大地节约了内存资源,并使文件的装入速度与文件大小无关。

程序中各种节的位置由节表决定,节表位于可选PE头之后。

找到节表的位置

在标准PE头中,有一个WORD SizeOfOptionalHeader,该值表示可选PE头的大小,32位文件的默认值为E0h,64位PE文件的默认值为F0h,大小可以自定义。因此,节表的内存地址为:

1
DOS + 标准PE头 + 可选PE头

在标准PE头中,有一个WORD NumberOfSections,表示文件中存在的节的种数的数量。如果需要新增或合并节,必须修改此值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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中各字段的详细解析

  • Name:段名,8字节的ASCII字符串,不足8字节用0补齐。
  • VirtualSize:虚拟大小,标识节在内存中占用的大小,请勿与PhysicalSize(物理大小)混淆。此字段存储该节在未对齐时的真实尺寸,该值可能不准确(在内存中拉伸后的实际大小)。
  • VirtualAddress:虚拟地址,标识内存中的偏移地址,实际加载的位置与此相关(在内存中的偏移地址加上ImageBase即为在内存中的真实地址)。
  • SizeOfRawData:物理大小,节在PE文件中占用的大小,若不足文件对齐单位则会进行填充(对齐后的长度)。
  • PointerToRawData:物理地址,标识该段在文件中的偏移位置。
  • PointerToRelocations:重定向表的偏移位置。
  • PointerToLinenumbers:行号表的偏移位置。
  • NumberOfRelocations:重定向表的数量。
  • NumberOfLinenumbers:行号表的数量。
  • Characteristics:标识该段的各种属性信息,包括常用属性:
    • IMAGE_SCN_MEM_READ:可读;
    • IMAGE_SCN_MEM_WRITE:可写;
    • IMAGE_SCN_MEM_EXECUTE:可执行;
    • IMAGE_SCN_CNT_CODE:代码段;
    • IMAGE_SCN_CNT_INITIALIZED_DATA:已初始化数据段;
    • IMAGE_SCN_CNT_UNINITIALIZED_DATA:未初始化数据段;
    • IMAGE_SCN_LNK_INFO:包含附加信息。

image-20241017184611807

image-20241017184829520

Work

实例代码

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// 2015_03_13.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <windows.h>
#include <malloc.h>

char* ReadFilePe(const char* peFileName)
{
FILE* peFile = nullptr;
fopen_s(&peFile, peFileName, "rb");
size_t peSize = NULL;
char* peData = nullptr;
if (peFile != NULL)
{
if (fseek(peFile, 0, SEEK_END) == 0)//执行成功
{
peSize = ftell(peFile);
fseek(peFile, 0, SEEK_SET);
peData = (char*)malloc(peSize);
if (peData != nullptr)
{
memset(peData, '\0', peSize);
fread_s(peData, peSize, 1, peSize, peFile);
}
else
{
printf("申请内存空间失败!(%d)\n", __LINE__);
}
}
else
{
printf("指针移动到数据末尾失败!(%d)\n", __LINE__);
}
}
else
{
printf("文件打开失败!(%d)\n", __LINE__);
}

if (peFile)
fclose(peFile);

return peData;
}

void printSectionTable(const char* peFileName)
{
char* peData = ReadFilePe(peFileName);
char* tempData = peData;
if (peData == nullptr)
{
printf("获取数据失败!");
return;
}


WORD(*wp)[1];
wp = (WORD(*)[1])peData;
DWORD(*dp)[1];
dp = (DWORD(*)[1])peData;

size_t docSize = 60;
size_t fileSize = 20;
//读取DOC结构 lfanew属性
//这里的15是因为dp的大小是4字节,而lfanew在60-64字节处, dp每次+1就等于+4字节,所以是15
size_t lfanew = *(*(dp)/*这里得到结果为地址*/ +15);
//读取FILE结构 SizeOfOptionalHeader属性
//lfanew / 2 是因为SizeOfOptionalHeader是二字节数据,如果不 / 2则等于 DOC头往后移动lfanew*2(因为wp是2字节大小)
size_t OptionalSize = *(*(wp)+(lfanew / 2) + 10);
//读取FILE结构 NumberOfSection属性(同上)
size_t NumberOfSection = *(*(wp)+(lfanew / 2) + 3);

//转到FILE结构
tempData = &tempData[lfanew + 4];
//转到OPTIONAL结构
tempData = &tempData[20];
//转到节表
tempData = &tempData[OptionalSize];
PIMAGE_SECTION_HEADER tempSectionHeader = nullptr;
for (size_t i = 0; i < NumberOfSection; i++)
{
tempSectionHeader = (PIMAGE_SECTION_HEADER)tempData;
tempData = &tempData[40];
printf(">>>>>>>>>> 节表:%d <<<<<<<<<<<\n", i);
printf("Name = %s\n", tempSectionHeader->Name);
printf("Misc = %x\n", tempSectionHeader->Misc.VirtualSize);
printf("VirtualAddress = %x\n", tempSectionHeader->VirtualAddress);
printf("SizeOfRawData = %x\n", tempSectionHeader->SizeOfRawData);
printf("PointerToRawData = %x\n", tempSectionHeader->PointerToRawData);
printf("PointerToRelocations = %x\n", tempSectionHeader->PointerToRelocations);
printf("PointerToLinenumbers = %x\n", tempSectionHeader->PointerToLinenumbers);
printf("NumberOfRelocations = %x\n", tempSectionHeader->NumberOfRelocations);
printf("NumberOfLinenumbers = %x\n", tempSectionHeader->NumberOfLinenumbers);
printf("Characteristics = %x\n", tempSectionHeader->Characteristics);
}


free(peData);
}

int main()
{
printSectionTable("C:\\Windows\\System32\\notepad.exe");
getchar();
return 0;
}

运行结果

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
>>>>>>>>>> 节表:0 <<<<<<<<<<<
Name = .text
Misc = 27900
VirtualAddress = 1000
SizeOfRawData = 27a00
PointerToRawData = 400
PointerToRelocations = 0
PointerToLinenumbers = 0
NumberOfRelocations = 0
NumberOfLinenumbers = 0
Characteristics = 60000020
>>>>>>>>>> 节表:1 <<<<<<<<<<<
Name = .data
Misc = 1ee0
VirtualAddress = 29000
SizeOfRawData = a00
PointerToRawData = 27e00
PointerToRelocations = 0
PointerToLinenumbers = 0
NumberOfRelocations = 0
NumberOfLinenumbers = 0
Characteristics = c0000040
>>>>>>>>>> 节表:2 <<<<<<<<<<<
Name = .idata
Misc = 2c7a
VirtualAddress = 2b000
SizeOfRawData = 2e00
PointerToRawData = 28800
PointerToRelocations = 0
PointerToLinenumbers = 0
NumberOfRelocations = 0
NumberOfLinenumbers = 0
Characteristics = 40000040
>>>>>>>>>> 节表:3 <<<<<<<<<<<
Name = .didat
Misc = 7c
VirtualAddress = 2e000
SizeOfRawData = 200
PointerToRawData = 2b600
PointerToRelocations = 0
PointerToLinenumbers = 0
NumberOfRelocations = 0
NumberOfLinenumbers = 0
Characteristics = c0000040
>>>>>>>>>> 节表:4 <<<<<<<<<<<
Name = .rsrc
Misc = 1e1d0
VirtualAddress = 2f000
SizeOfRawData = 1e200
PointerToRawData = 2b800
PointerToRelocations = 0
PointerToLinenumbers = 0
NumberOfRelocations = 0
NumberOfLinenumbers = 0
Characteristics = 40000040
>>>>>>>>>> 节表:5 <<<<<<<<<<<
Name = .reloc
Misc = 25b0
VirtualAddress = 4e000
SizeOfRawData = 2600
PointerToRawData = 49a00
PointerToRelocations = 0
PointerToLinenumbers = 0
NumberOfRelocations = 0
NumberOfLinenumbers = 0
Characteristics = 42000040