项目地址
pe-parse/ at master · helsome/pe-parse (github.com)

理论部分见令一篇文章:PE结构浅析。

源码

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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include<iostream>
#include<windows.h>

/*运行前需要为项目赋予admin权限,点击项目右键->属性->配置属性->链接器->清单文件->UAC执行级别->requireAdministrator(选择)。 这样生成的程序在运行时就可以获得管理员权限,向C盘写入文件
否则szBuffer会为NULL,无法运行*/
//文件加载函数
char* LoadFile(const char* szFilePath)
{
//打开文件
HANDLE hFile = CreateFileA(szFilePath, GENERIC_READ | GENERIC_WRITE, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)//判断文件是否正确打开
return NULL;
DWORD dwFileSize = GetFileSize(hFile, NULL);
char* szBuffer = new char [dwFileSize] {0};
DWORD dwReadSize = 0;
BOOL bRet = ReadFile(hFile, szBuffer, dwFileSize, &dwFileSize, NULL);//lpbuffer指向接收从文件或设备读取的数据的缓冲区的指针。
if (bRet)
return szBuffer;
else
return 0;
}
DWORD RvaToFoa(DWORD dwRva, char* szBuffer)//计算地址偏移量
{
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)szBuffer;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(szBuffer + pDos->e_lfanew);
PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNt);
if (dwRva<pSectionHeader[0].VirtualAddress)//判断此时rva是否在头部
{
return dwRva;
}
for (size_t i = 0; i < pNt->FileHeader.NumberOfSections; i++)
{
if (dwRva >= pSectionHeader[i].VirtualAddress && dwRva <= pSectionHeader[i].VirtualAddress + pSectionHeader[i].Misc.VirtualSize)//判断位置
{
return dwRva - pSectionHeader[i].VirtualAddress + pSectionHeader[i].PointerToRawData;//返回偏移量
}
}
}
bool CheckPEFormat(char* szBuffer) {
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)szBuffer;
if (pDos->e_magic != IMAGE_DOS_SIGNATURE) {
return false; // Not a valid PE file
}
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(szBuffer + pDos->e_lfanew);
return pNt->Signature == IMAGE_NT_SIGNATURE; // Check PE signature
}
void ReadFileHeader(char* szBuffer) {
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)szBuffer;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(szBuffer + pDos->e_lfanew);
PIMAGE_FILE_HEADER pFileHeader = &pNt->FileHeader;

printf("Machine: %04X\n", pFileHeader->Machine);
printf("Number of Sections: %04X\n", pFileHeader->NumberOfSections);
printf("Time Date Stamp: %08X\n", pFileHeader->TimeDateStamp);
printf("Characteristics: %04X\n", pFileHeader->Characteristics);
}
void ReadOptionalHeader(char* szBuffer) {
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)szBuffer;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(szBuffer + pDos->e_lfanew);
PIMAGE_OPTIONAL_HEADER pOptHeader = &pNt->OptionalHeader;

printf("Magic: %04X\n", pOptHeader->Magic);
printf("Image Base: %08X\n", pOptHeader->ImageBase);
printf("Section Alignment: %08X\n", pOptHeader->SectionAlignment);
//printf("Size of Optional Header: %04X\n", pFileHeader->SizeOfOptionalHeader);
}
void ReadDataDirectory(char* szBuffer) {
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)szBuffer;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(szBuffer + pDos->e_lfanew);

for (int i = 0; i < IMAGE_NUMBEROF_DIRECTORY_ENTRIES; i++) {
PIMAGE_DATA_DIRECTORY pDir = &pNt->OptionalHeader.DataDirectory[i];
printf("Data Directory %d: VA = %08X, Size = %08X\n", i, pDir->VirtualAddress, pDir->Size);
}
}
void ReadSectionTable(char* szBuffer) {
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)szBuffer;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(szBuffer + pDos->e_lfanew);
PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(pNt);

for (int i = 0; i < pNt->FileHeader.NumberOfSections; i++) {
printf("Section Name: %.8s\n", pSectionHeader[i].Name);
printf("Virtual Size: %08X\n", pSectionHeader[i].Misc.VirtualSize);
printf("Virtual Address: %08X\n", pSectionHeader[i].VirtualAddress);
printf("Size of Raw Data: %08X\n", pSectionHeader[i].SizeOfRawData);
pSectionHeader++;
}
}


//解析导入表
void ImportTable(char* szBuffer)
{
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)szBuffer;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(szBuffer + pDos->e_lfanew);
//定位导入表
PIMAGE_OPTIONAL_HEADER pOptionHeader = &pNt->OptionalHeader;
PIMAGE_DATA_DIRECTORY pImportDir = pOptionHeader->DataDirectory + IMAGE_DIRECTORY_ENTRY_IMPORT;
//填入导出表
PIMAGE_IMPORT_DESCRIPTOR pImport = (PIMAGE_IMPORT_DESCRIPTOR)(RvaToFoa(pImportDir->VirtualAddress, szBuffer) + szBuffer);//Rvatofoa计算导入表的头到文件头的距离
while(pImport->Name!= NULL)
{
char* szModuleName = (char*)(RvaToFoa(pImport->Name, szBuffer) + szBuffer);
printf("DLL名称:%s\r\n", szModuleName);
printf("时间日期标志\r\n", pImport->TimeDateStamp);
printf("ForwarderChain%08X\r\n", pImport->ForwarderChain);
printf("NameRva;%08X\r\n", pImport->Name);
printf("OriginalFirstThunk;%08X\r\n", pImport->OriginalFirstThunk);
printf("FirstThunk;%08X\r\n", pImport->FirstThunk);
//指向导入地址表的RVA
PIMAGE_THUNK_DATA pIAT = (PIMAGE_THUNK_DATA)(RvaToFoa(pImport->FirstThunk, szBuffer) + szBuffer);//IAT导入地址表
DWORD dwIndex = 0;
DWORD dwImportOffset = 0;
//被导入函数序号
while (pIAT->u1.Ordinal)
{
printf("ThunkRVA:08X\r\n", pImport->OriginalFirstThunk + dwIndex);
dwImportOffset = RvaToFoa(pImport->OriginalFirstThunk, szBuffer);
printf("ThunkFOA:08X\r\n", dwImportOffset + dwIndex);
dwIndex += 4;
if ((pIAT->u1.Ordinal && 0x80000000) != 1)
{
PIMAGE_IMPORT_BY_NAME pName = (PIMAGE_IMPORT_BY_NAME)(RvaToFoa(RvaToFoa(pIAT->u1.AddressOfData, szBuffer), szBuffer));
printf("API name:%s\n", pName->Name);
printf("Hint:%04X\n", pName->Hint);
//被导入函数的地址
printf("ThunkValue:%08X\n",pIAT->u1.Function);
}
pIAT++;
}
pImport++;
}
}
void TSLTable(char* szBuffer)
{
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)szBuffer;
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(szBuffer + pDos->e_lfanew);
//定位TSL表
PIMAGE_DATA_DIRECTORY pTSLDir = (pNt->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_TLS);
//填充TLS结构
PIMAGE_TLS_DIRECTORY pTLS = (PIMAGE_TLS_DIRECTORY)(RvaToFoa(pTSLDir->VirtualAddress, szBuffer) + szBuffer);
printf("数据块开始VA:%08X\n",pTLS->StartAddressOfRawData);
printf("数据块结束VA:%08X\n",pTLS->EndAddressOfRawData);
printf("索引变量VA:%08X\n", pTLS->AddressOfIndex);
printf("特征值:%08X\n", pTLS->Characteristics);
}
int main()
{
char* szBuffer = LoadFile("C:\\Users\\helse\\Desktop\\artifact2.exe");
if (CheckPEFormat(szBuffer))
{
ReadFileHeader(szBuffer);
ReadOptionalHeader(szBuffer);
ReadDataDirectory(szBuffer);
ReadSectionTable(szBuffer);
ImportTable(szBuffer);
TSLTable(szBuffer);
}
else {
printf("Not a valid PE file.\n");
}
system("pause");
return 0;
}



结果对比