C/C++编程解析PE文件结构
一、PE文件介绍
1. PE文件定义
PE的意思就是Portable Executable(可移植、可执行)
2. PE文件结构
如下为PE文件结构图:
PS:现在大多数PE文件都是加壳或者经过处理的,此程序只适用于最原始的PE文件
关于PE文件的解析问题,可以参考我的另一篇博文《用Winhex软件解析PE文件》
二、代码
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>//函数计算导出/导入表相对内存的偏移量
DWORD RVA2Offset(PIMAGE_NT_HEADERS pNTHeader, DWORD dwExpotRVA)
{PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)((DWORD)pNTHeader + sizeof(IMAGE_NT_HEADERS));for(int i = 0; i < pNTHeader->FileHeader.NumberOfSections; i++){if(dwExpotRVA >= pSection[i].VirtualAddress && dwExpotRVA < (pSection[i].VirtualAddress + pSection[i].SizeOfRawData)){return pSection[i].PointerToRawData + (dwExpotRVA - pSection[i].VirtualAddress);}}return 0;
}int main(int argc, char* argv[])
{char szExePath[MAX_PATH]; //声明文件名//"C:\\Program Files (x86)\\Application Verifier\\vrfauto.dll"printf("Please input the execution file path:\n");scanf("%s",szExePath);HANDLE hFile = CreateFile(szExePath, GENERIC_ALL, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL); //获得PE文件句柄HANDLE hMapping = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL); //创建一个新的文件映射内核对象//将一个文件映射对象映射到内存,得到指向映射到内存的第一个字节的指针pbFilePVOID pbFile = MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);if(INVALID_HANDLE_VALUE == hFile || NULL == hMapping || NULL == pbFile){printf("\n\t---------- The File Inexistence! ----------\n");if(NULL != pbFile){UnmapViewOfFile(pbFile);}if(NULL != hMapping){CloseHandle(hMapping);}if (INVALID_HANDLE_VALUE != hFile){CloseHandle(hFile);}return 0;}printf("<------------------------PE Header----------------------->\n");PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pbFile;//pDosHeader指向DOS头起始位置printf("PE Header e_lfanew:0x%x\n",pDosHeader->e_lfanew);PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pbFile + pDosHeader->e_lfanew);//计算PE头位置if(0x00004550 != pNTHeader->Signature){printf("\n\t---------- Lawless PE File! ----------\n");if(NULL != pbFile){UnmapViewOfFile(pbFile);}if(NULL != hMapping){CloseHandle(hMapping);}if (INVALID_HANDLE_VALUE != hFile){CloseHandle(hFile);}return 0;}printf("<----------------------FileHeader---------------------->\n");WORD wNumberOfSections = (WORD)pNTHeader->FileHeader.NumberOfSections;//找到存放节数的项,并打印printf("NumberOfSections: %d\n",wNumberOfSections);WORD wSizeOfOptionalHeader= (WORD)pNTHeader->FileHeader.SizeOfOptionalHeader;//找到可选头长度,并打印printf("SizeOfOptionalHeader: %d\n",wSizeOfOptionalHeader);//可选映像头printf("<--------------------Optional Header-------------------->\n");DWORD dwSizeOfCode = (DWORD)pNTHeader->OptionalHeader.SizeOfCode;printf("SizeOfCode: 0x%08X\n",dwSizeOfCode);DWORD dwAddressOfEntryPoint = (DWORD)pNTHeader->OptionalHeader.AddressOfEntryPoint;printf("AddressOfEntryPoint: 0x%08X\n",dwAddressOfEntryPoint);DWORD dwImageBase = (DWORD)pNTHeader->OptionalHeader.ImageBase;printf("ImageBase: 0x%08X\n",dwImageBase);DWORD dwSectionAlignment = (DWORD)pNTHeader->OptionalHeader.SectionAlignment;printf("SectionAlignment: 0x%08X\n",dwSectionAlignment);DWORD dwFileAlignment = (DWORD)pNTHeader->OptionalHeader.FileAlignment;printf("FileAlignment: 0x%08X\n",dwFileAlignment);DWORD dwSizeOfImage = (DWORD)pNTHeader->OptionalHeader.SizeOfImage;printf("SizeOfImage: 0x%08X\n",dwSizeOfImage);DWORD dwNumberOfRvaAndSize = (DWORD)pNTHeader->OptionalHeader.NumberOfRvaAndSizes;printf("NumberOfRvaAndSizes: 0x%08X\n",dwNumberOfRvaAndSize);DWORD dwSectionHeaderOffset = (DWORD)pNTHeader+24+(DWORD)wSizeOfOptionalHeader;//计算节表的位置printf("<----------------------SectionTable---------------------->\n");int NumOfSec = 0;for(NumOfSec;NumOfSec<wNumberOfSections;NumOfSec++){PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)(dwSectionHeaderOffset + 40*NumOfSec);printf("%d 's Name):%s\n",NumOfSec+1,pSectionHeader->Name);DWORD dwVirtualAddress = (DWORD)pSectionHeader->VirtualAddress;printf("VirtualAddress: 0x%08X\n",dwVirtualAddress);DWORD dwSizeOfRawData = (DWORD)pSectionHeader->SizeOfRawData;printf("SizeOfRawData: 0x%08X\n",dwSizeOfRawData);DWORD dwPointerToRawData = (DWORD)pSectionHeader->PointerToRawData;printf("PointerToRawData: 0x%08X\n",dwPointerToRawData);}printf("<--------------------Export Table-------------------->\n");DWORD dwExportOffset = RVA2Offset(pNTHeader, pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pbFile + dwExportOffset);DWORD dwFunctionNameOffset = (DWORD)pbFile + RVA2Offset(pNTHeader, pExport->Name);DWORD* pdwNamesAddress = (DWORD*)((DWORD)pbFile + RVA2Offset(pNTHeader, pExport->AddressOfNames));DWORD* pdwFunctionAddress = (DWORD*)((DWORD)pbFile + RVA2Offset(pNTHeader, pExport->AddressOfFunctions));WORD* pwOrdinals = (WORD*)((DWORD)pbFile + RVA2Offset(pNTHeader, pExport->AddressOfNameOrdinals));printf("AddressOfNameOrdinals: 0x%08X\n",RVA2Offset(pNTHeader, pExport->AddressOfNameOrdinals));printf("AddressOfFunctions: 0x%08X\n",RVA2Offset(pNTHeader, pExport->AddressOfFunctions));printf("AddressOfNames: 0x%08X\n",RVA2Offset(pNTHeader, pExport->AddressOfNames));if(0 == pExport->NumberOfFunctions){printf("\n\t---------- No Export Tabel! ----------\n");if(NULL != pbFile){UnmapViewOfFile(pbFile);}if(NULL != hMapping){CloseHandle(hMapping);}if (INVALID_HANDLE_VALUE != hFile){CloseHandle(hFile);}return 0;}printf("FileName: %s\n", dwFunctionNameOffset);printf("NumberOfFunctions: %d\n", pExport->NumberOfFunctions);printf("NumberOfNames: %d\n\n", pExport->NumberOfNames);printf("============NameExport:\n\n");int IsFound[1000]={0};int k;for(k=0;k<pExport->NumberOfFunctions;k++){IsFound[k]=0;//printf("%d ",IsFound[k]);}int i;for(i = 0; i < pExport->NumberOfNames; i++){DWORD dwFunctionAddress = pdwFunctionAddress[pwOrdinals[i]];DWORD pdwFunNameOffset = (DWORD)pbFile + RVA2Offset(pNTHeader, pdwNamesAddress[i]);IsFound[pwOrdinals[i]]=1;printf("[ExportNum]: %-4d [Name]: %-30s [RVA]: 0x%08X\n", pExport->Base + pwOrdinals[i], pdwFunNameOffset,dwFunctionAddress);}printf("\n============NumberExport:\n");int m;for(m = 0; m < pExport->NumberOfFunctions; m++){if(IsFound[m]!=1){DWORD dwFunctionAddress = pdwFunctionAddress[m];printf("[ExportNum]: %-4d [RVA]: 0x%08X\n", pExport->Base + m, dwFunctionAddress);}}printf("\n");printf("<--------------------Inport Table-------------------->\n");int cont = 0;do{DWORD dwInportOffset = RVA2Offset(pNTHeader, pNTHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);dwInportOffset = dwInportOffset + cont;PIMAGE_IMPORT_DESCRIPTOR pInport = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pbFile + dwInportOffset);if(pInport->OriginalFirstThunk == 0&& pInport->TimeDateStamp == 0&& pInport->ForwarderChain== 0 && pInport->Name== 0 && pInport->FirstThunk== 0 )break;DWORD dwOriginalFirstThunk = (DWORD)pbFile + RVA2Offset(pNTHeader, pInport->OriginalFirstThunk);//VA to IATDWORD dwFirstThunk = (DWORD)pbFile + RVA2Offset(pNTHeader, pInport->FirstThunk);//VA to IATDWORD dwName = (DWORD)pbFile + RVA2Offset(pNTHeader, pInport->Name);printf("\n---------Inport File Name: %s\n\n",dwName);if(dwOriginalFirstThunk == 0x00000000){dwOriginalFirstThunk = dwFirstThunk;}DWORD* pdwTrunkData= (DWORD*)dwOriginalFirstThunk;int n=0,x=0;while(pdwTrunkData[n] != 0){DWORD TrunkData = pdwTrunkData[n];if(TrunkData < IMAGE_ORDINAL_FLAG32)//名字导入{PIMAGE_IMPORT_BY_NAME pInportByName = (PIMAGE_IMPORT_BY_NAME)((DWORD)pbFile + RVA2Offset(pNTHeader, TrunkData));printf("-----------InportByName: %s\n",pInportByName->Name);}else//序号导入{DWORD FunNumber = (DWORD)(TrunkData - IMAGE_ORDINAL_FLAG32);printf("-----------InportByNumber: %-4d ",FunNumber);}if(x != 0 && x%3==0) printf("\n");n++;x++;}cont=cont+40;}while(true);if(NULL != pbFile){UnmapViewOfFile(pbFile);}if(NULL != hMapping){CloseHandle(hMapping);}if (INVALID_HANDLE_VALUE != hFile){CloseHandle(hFile);}return 0;
}
三、运行结果
1.PE头、文件头、选择头…
2.导出表部分:
…
3.导入表部分:
…
C/C++编程解析PE文件结构相关推荐
- 用Winhex软件解析PE文件
一.概念 1. PE文件 PE(Portable Executable)文件,被称为可移植的可执行的文件,常见的EXE.DLL.OCX.SYS.COM都是PE文件,PE文件是微软Windows操作系统 ...
- 《C++ 黑客编程揭秘与防范(第2版)》——6.2 详解PE文件结构
本节书摘来自异步社区出版社<C++ 黑客编程揭秘与防范(第2版)>一书中的第6章,第6.2节,作者:冀云,更多章节内容可以访问云栖社区"异步社区"公众号查看. 6.2 ...
- 《C++ 黑客编程揭秘与防范(第2版)》—第6章6.2节详解PE文件结构
本节书摘来自异步社区<C++ 黑客编程揭秘与防范(第2版)>一书中的第6章6.2节详解PE文件结构,作者冀云,更多章节内容可以访问云栖社区"异步社区"公众号查看. 6. ...
- [系统安全] 四十一.APT系列(6)Python解析PE文件并获取时间戳判断来源区域
您可能之前看到过我写的类似文章,为什么还要重复撰写呢?只是想更好地帮助初学者了解病毒逆向分析和系统安全,更加成体系且不破坏之前的系列.因此,我重新开设了这个专栏,准备系统整理和深入学习系统安全.逆向分 ...
- 病毒木马查杀实战第024篇:MBR病毒之编程解析引导区
前言 通过之前的学习,相信大家已经对磁盘的引导区有了充分的认识.但是我们之前的学习都是利用现成的工具来对引导区进行解析的,而对于一名反病毒工程师而言,不单单需要有扎实的逆向分析功底,同时也需要有很强的 ...
- PE文件结构详解(二)可执行文件头
by evil.eagle 转载请注明出处. http://blog.csdn.net/evileagle/article/details/11903197 在PE文件结构详解(一)基本概念里,解释了 ...
- 【转】PE文件结构详解--(完整版)
(一)基本概念 PE(Portable Execute)文件是Windows下可执行文件的总称,常见的有DLL,EXE,OCX,SYS等,事实上,一个文件是否是PE文件与其扩展名无关,PE文件可以是任 ...
- PE文件结构与程序装载
PE文件结构与程序装载是掌握Windows逆向.加壳.免杀等技术的基础,本文详细记录了PE文件的基本结构,用编辑器对文件结构进行分析,并介绍程序装载的相关概念和基本过程. 参考书籍:<逆向工程核 ...
- PE文件结构详解 --(完整版)
From:https://blog.csdn.net/adam001521/article/details/84658708 PE结构详解:https://www.cnblogs.com/zheh/p ...
最新文章
- python爬虫案例-Python爬取租房数据实例,据说可以入门爬虫的小案例!
- SPRING事务传播特性
- jQuery源码学习
- Web Deployment Projects with Visual Studio 2005
- Castle.ActiveRecord的ProxyFactory配置
- IDEA JRebel热部署插件免费使用方法
- 关于一道数据库例题的解析。为什么σ age22 (πS_ID,SCORE (SC) ) 选项是错的?
- 新浪uc2010官方下载
- jemter在linux上怎么安装_【JMeter之一】在linux系统下安装jmeter
- 区块链未来发展三大关键词,华为云如何见招拆招?
- 【转】什么是磁珠(Ferrite Bead 即 FB)
- Ansible详解(十二)——Ansible Roles详解
- 影视.20190507
- 数学之美番外篇--贝叶斯方法
- .Net中数据绑定控件应用小技巧
- stm32与绝对式编码器的ssi接口进行通信
- 思路初探:采用c#实现pdf转ofd
- 用计算机思维认识摩斯密码(摩斯密码速记)
- Denoise Autoencoder
- w7电脑蓝屏怎么解决_Win7电脑突然蓝屏怎么办?