解析PE文件

最近在自学解析PE文件,根据小辣椒(CFF Explorer)以及各论坛上大佬的帖子,做了个黑屏打印PE文件的,历时7天完成,在此想跟有相关需要的同学们分享下思路,有不足之处也希望大家不吝赐教,指点出来。谢谢。

 

PE文件主要有DOS头,NT头(包含PE头,可选PE头,可选PE头中又包含着一个数据目录,里面包含了其他表单的RVA和大小),节表

表单主要有导出表,导入表,重定位表,资源表。除了资源表外,其他表单的各项数据都用代码实现了打印。

 

首先通过CreateFile打开文件,获得文件句柄,随后调用CreateFileMapping生成内存映射对象,再调用MapViewOfFile获得文件映射基址。

DOS头RVA=(IMAGE_DOS_HEADER)映射基址;

NT头RVA=(IMAGE_NT_HEADERS)DOS头+DOS头->e_lfanew的值;

PE头RVA=&((IMAGE_FILE_HEADER)NT头->FileHeader);

可选PE头RVA=&((IMAGE_OPTIONAL_HEADER)NT头->OptionalHeader);

节表RVA=IMAGE_FIRST_SECTION(NT头);//节表的位置=DOS头大小(确定)+标准PE头大小(确定)+可选PE头大小(标准PE头的成员.SizeOfOptionalHeader记录了可选PE头大小)

节表数量:PE头->NumberOfSections;

导入表、导出表、重定位表的RVA和长度分别在可选PE头的->DataDirectory的第三、第一、第六个数组当中的VirtualAddress和size成员中(注意数组索引从0开始)

 

注意,在解析前,先要判断当前文件是否为PE文件:以下2个条件缺一不可。

1.dos头的值,即dos->e_magic是否等于0x5A4D(MZ)

2.NT头,即DOS头(RVA)+DOS->e_lfnew的值 得到的即为NT头的RVA    查看NT头的值,即NT->signatrue的值是否为0x00004550(PE)

下面po上代码:

  1 #include<Windows.h>
  2 #include<iostream>
  3 #include<time.h>
  4 using namespace std;
  5 
  6 typedef struct _MApFileH_STRUCT
  7 {
  8 HANDLE hFile; //文件句柄
  9 HANDLE hFileMap; //映射文件句柄
 10 LPVOID pMapImageBase; //映像基址
 11 } MApFileH_STRUCT, *PMApFileH_STRUCT;
 12 /*一、加载要打开的文件
 13 采用内存映射的方式将文件加载到内存中,内存映射函数包括:CreateFileMapping, OpenFileMapping, MapViewOfFile, UnmapViewOfFile和FlushViewOfFile。
 14 定义一个函数,如果是一个PE文件则返回true, 否则返回false:*/
 15 BOOL LoadFile(LPTSTR pFileName, PMApFileH_STRUCT pFileStruct)//加载文件 并将文件句柄、
 16 {
 17 if (pFileName == nullptr)
 18 {
 19 return false;
 20 }
 21 HANDLE hFile = NULL;
 22 HANDLE hFileMap = NULL;
 23 LPVOID pMapImageBase = NULL;
 24 memset(pFileStruct, 0, sizeof(MApFileH_STRUCT));
 25 //1、只读方式打开文件,返回文件句柄
 26 hFile = CreateFile(pFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
 27 if (!hFile)
 28 {
 29 return false;
 30 }
 31 //2、创建内存映射文件对象
 32 hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, 0);
 33 if (!hFileMap)
 34 {
 35 CloseHandle(hFile);
 36 return false;
 37 }
 38 //3、创建内存映射文件的视图
 39 pMapImageBase = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);
 40 if (!pMapImageBase)
 41 {
 42 CloseHandle(hFile);
 43 CloseHandle(hFileMap);
 44 return false;
 45 }
 46 pFileStruct->hFile = hFile;
 47 pFileStruct->hFileMap = hFileMap;
 48 pFileStruct->pMapImageBase = pMapImageBase;
 49 return true;
 50 }
 51 //二、程序执行完毕后,回收资源
 52 void UnLoadFile(PMApFileH_STRUCT pFileStruct)
 53 {
 54 if (pFileStruct->hFile)
 55 {
 56 CloseHandle(pFileStruct->hFile);
 57 }
 58 
 59 if (pFileStruct->hFileMap)
 60 {
 61 CloseHandle(pFileStruct->hFileMap);
 62 }
 63 
 64 if (pFileStruct->pMapImageBase)
 65 {
 66 //撤销映射并使用CloseHandle函数关闭内存映射文件对象句柄
 67 UnmapViewOfFile(pFileStruct->pMapImageBase);
 68 }
 69 }
 70 //三、文件格式检测
 71 bool IsPEFile(LPVOID pMapImageBase)//如果1.基地址为0 2.DOC头的e_magic不为5A4D(MZ) 3.NT头的Signature不为00004550(PE) 任一成立,则该文件不是PE文件
 72 {
 73 PIMAGE_DOS_HEADER pDosH = nullptr;//指向空指针
 74 PIMAGE_NT_HEADERS32 pNtH = nullptr;
 75 if (!pMapImageBase)
 76 {
 77 return false;
 78 }
 79 
 80 pDosH = (PIMAGE_DOS_HEADER)pMapImageBase;
 81 if (pDosH->e_magic != IMAGE_DOS_SIGNATURE) // 5A4D "MZ" 
 82 {
 83 return false;
 84 }
 85 // pDH->e_lfanew保存PIMAGE_NT_HEADERS32的偏移地址,加上基址pDH即为MAGE_NT_HEADERS的地址
 86 pNtH = (PIMAGE_NT_HEADERS32)((DWORD)pDosH + pDosH->e_lfanew); //基地址+DOS头的最后一个元素取得的值 即为下一个NT头的地址
 87 if (pNtH->Signature != IMAGE_NT_SIGNATURE) //"PE"
 88 {
 89 return false;
 90 }
 91 return true;
 92 }
 93 
 94 //四、读取FileHeader和OptionalHeader的内容
 95 PIMAGE_DOS_HEADER GetDOSHeaders(LPVOID pMapImageBase)//1、获取指向IMAGE_DOS_HEADERS结构的指针
 96 {
 97 PIMAGE_DOS_HEADER pDosH = nullptr;
 98 if (!IsPEFile(pMapImageBase))
 99 {
100 return nullptr;
101 }
102 pDosH = (PIMAGE_DOS_HEADER)pMapImageBase;
103 return pDosH;
104 }
105 PIMAGE_NT_HEADERS GetNtHeader(LPVOID pMapImageBase)//2、获取指向IMAGE_NT_HEADERS结构的指针
106 {
107 PIMAGE_DOS_HEADER pDosH = nullptr;
108 PIMAGE_NT_HEADERS pNtH = nullptr;
109 if (!IsPEFile(pMapImageBase))
110 {
111 return nullptr;
112 }
113 
114 pDosH = (PIMAGE_DOS_HEADER)pMapImageBase;
115 pNtH = (PIMAGE_NT_HEADERS)((DWORD)pDosH + pDosH->e_lfanew);
116 return pNtH;
117 }
118 PIMAGE_FILE_HEADER GetFileHeader(LPVOID pMapImageBase)//3、获取指向IMAGE_FILE_HEADER结构的指针
119 {
120 PIMAGE_NT_HEADERS pNtH = GetNtHeader(pMapImageBase);
121 if (!pNtH)
122 {
123 return nullptr;
124 }
125 return &(pNtH->FileHeader);
126 }
127 PIMAGE_OPTIONAL_HEADER GetOptionalHeader(LPVOID pMapImageBase)//4、获取指向IMAGE_OPTIONAL_HEADER结构的指针
128 {
129 PIMAGE_NT_HEADERS pNtH = GetNtHeader(pMapImageBase);
130 if (!pNtH)
131 {
132 return nullptr;
133 }
134 return &(pNtH->OptionalHeader);
135 }
136 
137 PIMAGE_SECTION_HEADER GetSectionone(LPVOID pMapImageBase)//5、获取指向第一个节头的指针
138 {
139 PIMAGE_NT_HEADERS pNtH = GetNtHeader(pMapImageBase);//得到NT头
140 if (!pNtH)
141 {
142 return nullptr;
143 }
144 //节表的位置=DOS头大小(确定)+标准PE头大小(确定)+可选PE头大小(标准PE头的成员.SizeOfOptionalHeader记录了可选PE头大小)
145 return IMAGE_FIRST_SECTION(pNtH);//通过公式计算 得到SECTION的首地址
146 }
147 
148 short GetdwSectionCount(LPVOID pMapImageBase)//6、节头的数量 通过File Hander的NumberOfSections得出
149 {
150 PIMAGE_FILE_HEADER pFileHeader = GetFileHeader(pMapImageBase);
151 if (pFileHeader != NULL)
152 {
153 return pFileHeader->NumberOfSections;
154 }
155 }
156 //五、地址转换
157 //1.相对虚拟地址转文件地址(文件地址=映射基址+文件偏移地址(文件偏移地址=RVA-范围内的内存偏移(起点)+范围内的文件偏移(起点)))
158 DWORD RVAtoFA(LPVOID pMapImageBase, DWORD dwRVA)//接受一个当前文件的映射基址和需转换的RVA
159 {
160 PIMAGE_SECTION_HEADER pSectionHH = GetSectionone(pMapImageBase);//指向节表
161 PIMAGE_FILE_HEADER pFileH = GetFileHeader(pMapImageBase);//指向PE头
162 int dwSectionCount = pFileH->NumberOfSections;//获得节表数量
163 for (int iNum = 0; iNum < dwSectionCount; iNum++)
164 {
165 if (dwRVA >= pSectionHH->VirtualAddress && dwRVA < (pSectionHH->VirtualAddress + pSectionHH->Misc.VirtualSize))//如果RVA的值落在当前节点的范围内
166 {
167 return (DWORD)pMapImageBase + dwRVA - pSectionHH->VirtualAddress + pSectionHH->PointerToRawData;
168 /*则文件地址=映射基址 + 文件偏移地址( RVA- VirtualAddress + RawAddress)
169 = 映射基址 + RVA - VirtualAddress + RawAddress*/
170 }
171 pSectionHH++;//指向下一个节表
172 }
173 return 0;
174 }
175 //2.相对虚拟地址转文件偏移地址(文件偏移地址=RVA-范围内的内存偏移(起点)+范围内的文件偏移(起点)))
176 DWORD RVAtoFVA(LPVOID pMapImageBase, DWORD dwRVA)//接受一个当前文件的映射基址和需转换的RVA
177 {
178 PIMAGE_SECTION_HEADER pSectionH = GetSectionone(pMapImageBase);//指向节表
179 PIMAGE_FILE_HEADER pFileH = GetFileHeader(pMapImageBase);//指向PE头
180 int dwSectionCount = pFileH->NumberOfSections;//获得节表数量
181 for (int iNum = 0; iNum < dwSectionCount; iNum++)
182 {
183 if (dwRVA >= pSectionH->VirtualAddress && dwRVA < (pSectionH->VirtualAddress + pSectionH->Misc.VirtualSize))//如果RVA的值落在当前节点的范围内
184 {
185 return dwRVA - pSectionH->VirtualAddress + pSectionH->PointerToRawData;
186 }
187 pSectionH++;//指向下一个节表
188 }
189 return 0;
190 }
191 //3.虚拟地址转文件偏移地址
192 DWORD VAtoFVA(LPVOID pMapImageBase, DWORD dwVA)//接受一个当前文件的映射基址和需转换的VA
193 {
194 PIMAGE_NT_HEADERS pNtH = GetNtHeader(pMapImageBase);//获得NT头
195 DWORD dwIB = pNtH->OptionalHeader.ImageBase;//获得基地址
196 DWORD dwRVA = dwVA - dwIB;//将要转换的VA先转换为RVA
197 PIMAGE_SECTION_HEADER pSectionH = GetSectionone(pMapImageBase);//指向节表
198 PIMAGE_FILE_HEADER pFileH = GetFileHeader(pMapImageBase);//指向PE头
199 int dwSectionCount = pFileH->NumberOfSections;//获得节表数量
200 for (int iNum = 0; iNum < dwSectionCount; iNum++)
201 {
202 if (dwRVA>pSectionH->VirtualAddress && dwRVA < (pSectionH->VirtualAddress + pSectionH->Misc.VirtualSize))//如果RVA的值落在当前节点的范围内
203 {
204 return dwRVA - pSectionH->VirtualAddress + pSectionH->PointerToRawData;//则文件偏移地址=RVA - VirtualAddress + RawAddress
205 }
206 pSectionH++;//指向下一个节表
207 }
208 return 0;
209 }
210 //4.相对虚拟地址转虚拟地址
211 DWORD RVAtoVA(LPVOID pMapImageBase, DWORD dwRVA)//接受一个当前文件的映射基址和需转换的RVA
212 {
213 PIMAGE_NT_HEADERS pNtH = GetNtHeader(pMapImageBase);//获得NT头
214 DWORD dwIB = pNtH->OptionalHeader.ImageBase;//获得基地址
215 return dwRVA + dwIB;
216 }
217 //5.虚拟地址转相对虚拟地址
218 DWORD VAtoRVA(LPVOID pMapImageBase, DWORD dwVA)//接受一个当前文件的映射基址和需转换的VA
219 {
220 PIMAGE_NT_HEADERS pNtH = GetNtHeader(pMapImageBase);//获得NT头
221 DWORD dwIB = pNtH->OptionalHeader.ImageBase;//获得基地址
222 return dwVA - dwIB;
223 }
224 //六、输出文件头信息
225 void ShowFileHeaderInfo(PMApFileH_STRUCT pFileStruct)
226 {
227 char szOutText[1024] = { 0 };
228 PIMAGE_DOS_HEADER pDosH = nullptr;//DOS头
229 pDosH = GetDOSHeaders(pFileStruct->pMapImageBase);
230 if (!pDosH)
231 {
232 printf("获取DOS头失败\n");
233 return;
234 }
235 printf("Dos Header:\n");
236 printf("e_magic: %04lX\n", pDosH->e_magic);
237 /*省略若干个信息*/
238 printf("\n");
239 PIMAGE_NT_HEADERS pNtH = nullptr; //NT头
240 pNtH = GetNtHeader(pFileStruct->pMapImageBase);
241 if (!pNtH)
242 {
243 printf("获取文件头失败\n");
244 return;
245 }
246 printf("NT Header:\n");
247 printf("Signature: %08x\n", pNtH->Signature);
248 PIMAGE_FILE_HEADER pFileH = nullptr;//PE头
249 //将信息按十六进制格式化
250 pFileH = GetFileHeader(pFileStruct->pMapImageBase);//此时返回出来的是NT头的File结构的首地址
251 char *szFileHeaderFormat = "\nIMAGE_FILE_HEADER:\n\
252 Machine: %04lX\n\
253 NumberOfSections: %04lX\n\
254 TimeDateStamp: %04lX\n\
255 PointerToSymbolTable:%08X\n\
256 NumberOfSymbols: %08lX\n\
257 SizeOfOptionalHeader:%04lX\n\
258 Characteristics: %04lX\n\n";
259 sprintf(szOutText, szFileHeaderFormat, pFileH->Machine, pFileH->NumberOfSections, pFileH->TimeDateStamp, pFileH->PointerToSymbolTable, pFileH->NumberOfSymbols,
260 pFileH->SizeOfOptionalHeader, pFileH->Characteristics);
261 printf("%s", szOutText);
262 memset(szOutText, 0, sizeof(szOutText));
263 
264 PIMAGE_OPTIONAL_HEADER pOFileH = nullptr; //可选PE头
265 char *szFileOptHeaderFormat = "\nIMAGE_OPTIONAL_HEADER:\n\
266 Entry Point: %08lX\n\
267 Image Base: %08lX\n\
268 Code Base: %08lX\n\
269 Data Base: %08lX\n\
270 Image Size: %08lX\n\
271 Headers Size: %08lX\n\
272 Section Alignment:%08lX\n\
273 File Alignment: %08lX\n\
274 Subsystem: %08lX\n\
275 Check Sum: %04lX\n\
276 Dll Flags: %04lX\n\n";
277 pOFileH = GetOptionalHeader(pFileStruct->pMapImageBase);//此时返回出来的是NT头的optional结构的首地址
278 if (!pOFileH)
279 {
280 printf("Get File Optional Header failed!\n");
281 return;
282 }
283 sprintf(szOutText, szFileOptHeaderFormat, pOFileH->AddressOfEntryPoint, pOFileH->ImageBase, pOFileH->BaseOfCode, pOFileH->BaseOfData,
284 pOFileH->SizeOfImage, pOFileH->SizeOfHeaders, pOFileH->SectionAlignment, pOFileH->FileAlignment, pOFileH->Subsystem, pOFileH->CheckSum, pOFileH->DllCharacteristics);
285 printf("%s", szOutText);
286 
287 
288 PIMAGE_SECTION_HEADER pSectionH = nullptr; //节表
289 pSectionH = GetSectionone(pFileStruct->pMapImageBase);//通过基址得到NT头 再通过公式算出第一个节的地址
290 DWORD dwSectionCount = pFileH->NumberOfSections;//通过PE头的NumberOfSections成员 得到节的数量
291 printf("name:\tVirtual Size\tVirtual Address\tRaw Size\tRaw Address\t\n");
292 for (DWORD dwNum = 0; dwNum < dwSectionCount; dwNum++)
293 {
294 /*printf("name:%s,Virtual Size:%08x,Virtual Address:%08x,Raw Size:%08x,Raw Address:%08x\n",
295 pSectionH->Name, pSectionH->Misc.VirtualSize, pSectionH->VirtualAddress, pSectionH->SizeOfRawData, pSectionH->PointerToRawData);*/
296 printf("%s:\t%08x\t%08x\t%08x\t%08x\n", pSectionH->Name, pSectionH->Misc.VirtualSize, pSectionH->VirtualAddress, pSectionH->SizeOfRawData, pSectionH->PointerToRawData);
297 pSectionH++;
298 }
299 printf("\n");
300 
301 /*通过可选PE头的DataDirectory数组(该数组为IMAGE_DATA_DIRECTORY结构 里面是各表的RVA和大小)*/
302 pOFileH = GetOptionalHeader(pFileStruct->pMapImageBase);//此时返回出来的是NT头的optional结构的首地址
303 printf("导入表的相对偏移地址为:0x%08x\n", pOFileH->DataDirectory[1].VirtualAddress);
304 printf("导入表的大小为:0x%08x\n", pOFileH->DataDirectory[1].Size);//已对比 取值正确
305 int iDllCount;//dll的数量
306 int iFuncCount;//dll中函数的数量
307 
308 long lImportRVA = pOFileH->DataDirectory[1].VirtualAddress;//导入表的位置 
309 //得到第一个导入表的地址
310 PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((UCHAR*)RVAtoFA(pFileStruct->pMapImageBase, lImportRVA));
311 /*上下2条同理,都是通过所需地址的RVA求得在文件中的VA,上面那条求得文件的FVA后,要手动加上映像基地址;下面那条直接返回FRVA(文件地址)
312 VA=虚拟地址 RVA=相对虚拟地址 IB基地址 FVA=文件地址 FRVA=文件偏移地址(相对开头)FIB=映射基地址(即pFileStruct->pMapImageBase)*/
313 //PIMAGE_IMPORT_DESCRIPTOR pImportH2 = (PIMAGE_IMPORT_DESCRIPTOR)ImageRvaToVa(d_nt, d_nIB, CCC->DataDirectory[1].VirtualAddress, NULL);
314 for (iDllCount = 0; pImportTable->FirstThunk != NULL; iDllCount++)
315 {
316 char* pszDllName = (char*)RVAtoFA(pFileStruct->pMapImageBase, pImportTable->Name);//将存放DLL名称的地址转换成字符指针
317 printf("DLL Name:%s\t INT:0x%08x,Name RVA:0x%08x,IAT:0x%08x\n",
318 pszDllName, pImportTable->OriginalFirstThunk, pImportTable->Name, pImportTable->FirstThunk);
319 
320 //获得导入的DLL中的数据
321 PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)((UCHAR*)RVAtoFA(pFileStruct->pMapImageBase, pImportTable->OriginalFirstThunk));
322 for (iFuncCount = 0; pThunk->u1.Function != 0; iFuncCount++)
323 {
324 if (pThunk->u1.AddressOfData & 0x80000000) //如果导入表的输入名称表(INT)的最高位为1,则为按序号导入 
325 {
326 printf("\t[%d]\t IAT:%04x\t 导入序号:%04x\n", iFuncCount, pThunk->u1.AddressOfData, pThunk->u1.AddressOfData & 0xffff);
327 } //这里的pThunk->u1.AddressOfData &0xffff是为了让32位的二进制最后以16位显示出来 
328 else //不为1,则函数名称以字符串形式 此时变为指向PIMAGE_IMPORT_BY_NAME结构的RVA 
329 {
330 PIMAGE_IMPORT_BY_NAME pImportName = (PIMAGE_IMPORT_BY_NAME)RVAtoFA(pFileStruct->pMapImageBase, pThunk->u1.AddressOfData);
331 printf("\t[%d]\t Hint:%04x\t Name:%s\n", iFuncCount, pImportName->Hint, pImportName->Name);
332 }
333 pThunk++;
334 }
335 printf("-----------------------------------------------------------------------------------------\n");
336 pImportTable++;
337 }
338 printf("该PE文件导入表共有%d个DLL\n\n", iDllCount);
339 //PIMAGE_IMPORT_DESCRIPTOR pImportTable; //导入表由这样的结构的数组构成,最后以一个该结构全为0作为结束标志
340 //PIMAGE_THUNK_DATA pThunkNum;//上述结构中,有2个成员导入名称表(INT)和导入地址表(IAT)是该类型的结构 为1时直接输出
341 //PIMAGE_IMPORT_BY_NAME pImportName;//当ITD的最高位不为1时,IAT的值变成了指向这样一个结构的RVA
342 
343 /*导出表*/
344 //PIMAGE_OPTIONAL_HEADER pOFileH = GetOptionalHeader(pFileStruct->pMapImageBase);
345 
346 DWORD dwExportRVA = pOFileH->DataDirectory[0].VirtualAddress;//导出表的相对偏移地址
347 if (dwExportRVA == 0)
348 goto Relocation;
349 printf("导出表的相对偏移地址:0x%08x\n", pOFileH->DataDirectory[0].VirtualAddress);
350 printf("导出表长度:0x%08x\n", pOFileH->DataDirectory[0].Size);
351 PIMAGE_EXPORT_DIRECTORY dwExportTable = (PIMAGE_EXPORT_DIRECTORY)((UCHAR*)RVAtoFA(pFileStruct->pMapImageBase, dwExportRVA));//求得导出表的文件地址
352 printf("--------------------------------------------------------------------------------\n");
353 printf("Member\t\t\t Offset\t Size\t\t Value\n");
354 printf("--------------------------------------------------------------------------------\n");
355 printf("Characteristics\t\t %08x\t Dword\t\t %08x\n", dwExportRVA, dwExportTable->Characteristics);
356 printf("TimeDateStamp\t\t %08x\t Dword\t\t %08x\n", dwExportRVA + sizeof(DWORD), dwExportTable->TimeDateStamp);
357 printf("MajorVersion\t\t %08x\t Word\t\t %04x\n", dwExportRVA + sizeof(DWORD)* 2, dwExportTable->MajorVersion);
358 printf("MinorVersion\t\t %08x\t Word\t\t %04x\n", dwExportRVA + sizeof(DWORD)* 2 + sizeof(WORD), dwExportTable->MinorVersion);
359 printf("Name\t\t\t %08x\t Dword\t\t %08x\n", dwExportRVA + sizeof(DWORD)* 2 + sizeof(WORD)* 2, dwExportTable->Name);
360 printf("Base\t\t\t %08x\t Dword\t\t %08x\n", dwExportRVA + sizeof(DWORD)* 3 + sizeof(WORD)* 2, dwExportTable->Base);
361 printf("NumberOfFunctions\t %08x\t Dword\t\t %08x\n", dwExportRVA + sizeof(DWORD)* 4 + sizeof(WORD)* 2, dwExportTable->NumberOfFunctions);
362 printf("NumberOfNames\t\t %08x\t Dword\t\t %08x\n", dwExportRVA + sizeof(DWORD)* 5 + sizeof(WORD)* 2, dwExportTable->NumberOfNames);
363 printf("AddressOfFunctions\t %08x\t Dword\t\t %08x\n", dwExportRVA + sizeof(DWORD)* 6 + sizeof(WORD)* 2, dwExportTable->AddressOfFunctions);
364 printf("AddressOfNames\t\t %08x\t Dword\t\t %08x\n", dwExportRVA + sizeof(DWORD)* 7 + sizeof(WORD)* 2, dwExportTable->AddressOfNames);
365 printf("AddressOfNameOrdinals\t %08x\t Dword\t\t %08x\n", dwExportRVA + sizeof(DWORD)* 8 + sizeof(WORD)* 2, dwExportTable->AddressOfNameOrdinals);
366 
367 char* szFuncName = NULL;//用于输出函数名字
368 for (DWORD dwCount = 0; dwCount < dwExportTable->NumberOfFunctions; dwCount++) //导出序号不大于导出函数的值
369 {
370 DWORD dwExportNum = dwExportTable->Base + (dwCount * 1);//导出序号
371 DWORD dwFuncAddrRVA = dwExportTable->AddressOfFunctions + (dwCount*sizeof(DWORD));//导出函数地址的rva
372 DWORD dwFuncNameAddrRVA = dwExportTable->AddressOfNames + (dwCount*sizeof(DWORD));//存放着导出函数名字地址的地址
373 DWORD dwFuncNameNumRVA = dwExportTable->AddressOfNameOrdinals + (dwCount*sizeof(WORD));//导出函数名字的序号的rva
374 DWORD dwFuncNameRVA = *(DWORD*)RVAtoFA(pFileStruct->pMapImageBase, dwFuncNameAddrRVA);//将存放着函数名字RVA的RVA转换成文件地址 解引用获得函数名字RVA
375 szFuncName = (char*)(RVAtoFA(pFileStruct->pMapImageBase, dwFuncNameRVA));//再将函数名字RVA转换成文件地址,取得文件名字
376 printf("Ordinals:%08x Functions RVA:%08x Name Ordinal:%08x Name RVA:%08X Name:%s\n\n", dwExportNum, dwFuncAddrRVA, dwFuncNameNumRVA, dwFuncNameAddrRVA, szFuncName);
377 }
378 Relocation:
379 /*重定位表*/
380 DWORD dwRelocationRVA = pOFileH->DataDirectory[5].VirtualAddress;//重定位表rva
381 DWORD dwdwRelocationSize = pOFileH->DataDirectory[5].Size;//重定位表的大小
382 printf("\n重定位表的相对偏移地址:0x%08x\n", pOFileH->DataDirectory[5].VirtualAddress);
383 printf("重定位表的大小:0x%08x\n", pOFileH->DataDirectory[5].Size);
384 if (dwRelocationRVA == 0)
385 {
386 printf("该文件没有重定位表\n");
387 return;
388 }
389 PIMAGE_BASE_RELOCATION pRelocationTable;//重定位表的文件地址
390 //当前位置的RVA不能大于重定位表最后一块数据的RVA(起始位置+大小)
391 while (dwRelocationRVA<(pOFileH->DataDirectory[5].VirtualAddress + pOFileH->DataDirectory[5].Size))
392 {
393 pRelocationTable = (PIMAGE_BASE_RELOCATION)(RVAtoFA(pFileStruct->pMapImageBase, dwRelocationRVA));//取得当前块文件地址
394 int dwdwRelocationSize = (pRelocationTable->SizeOfBlock - 8) / 2;//这个块中需要重定位的值的个数 (该块前8位分别是起始地址和长度)
395 printf("VirtualAddress:%08x\t SizeOfBlock:%08x\t ltems:%d\n", pRelocationTable->VirtualAddress, pRelocationTable->SizeOfBlock, dwdwRelocationSize);
396 for (int iNum = 0; iNum < dwdwRelocationSize; iNum++)
397 {
398 WORD pRelocationTable_Chunk = *(WORD*)RVAtoFA(pFileStruct->pMapImageBase, dwRelocationRVA + 8 + sizeof(WORD)*iNum);
399 if (pRelocationTable_Chunk != 0)
400 {
401 printf("ltem:%04x\t 需要重定位的数据的RVA:%08x\n", pRelocationTable_Chunk, (pRelocationTable_Chunk ^ 0x3000) + pRelocationTable->VirtualAddress);
402 /*重定位地址=当前块内的偏移(当前2字节的值(需要通过文件地址取出来)^ 0x3000)+当前块的基址*/
403 }
404 }
405 printf("--------------------------------------------------------------\n");
406 dwRelocationRVA = dwRelocationRVA + pRelocationTable->SizeOfBlock;
407 }
408 }
409 
410 
411 MApFileH_STRUCT pFileStruct = { nullptr, nullptr, nullptr };
412 int main()
413 {
414 LPTSTR pFilePath = TEXT("E:\\学习\\C++\\静态解析PE文件测试\\解析PE文件测试\\kkk.dll");
415 if (!LoadFile(pFilePath, &pFileStruct))//载入文件内容
416 {
417 return -1;
418 }
419 
420 if (!IsPEFile(pFileStruct.pMapImageBase))//检查文件是否为PE文件
421 {
422 UnLoadFile(&pFileStruct);
423 return -1;
424 }
425 ShowFileHeaderInfo(&pFileStruct);
426 UnLoadFile(&pFileStruct);
427 system("pause");
428 }
View Code

 

 

 

 

posted @ 2019-11-02 00:15  __阿阿阿怪  阅读(780)  评论(0编辑  收藏  举报