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 ...
最新文章
- mysql查询嵌套where_MySQL-10(where /from 嵌套查询)
- Storm【实践系列-如何写一个爬虫】 - ParserBolt
- windows tcp ping 端口工具及使用方法
- lammps后处理:Python调用Ovito模块配置方法
- python 移动平均法_移动平均法详解
- JavaScript 弹窗事件
- 怎样在Excel顶部单元格操作锁定,技巧干货!Excel如何冻结首行单元格?
- VS Code Material Icon Theme插件设置自定义文件夹图标关联
- 计算机网线接口灯怎样是正常,电脑网线接口灯不亮是什么原因
- 网易乐得数据库及运维分享会
- python 网格交易源码_网格交易策略(难度:中级)
- Android 蓝牙连接
- 图机器学习-图神经网络
- Python读取键盘输入到一维列表及二维列表
- 了解一下winsock
- 程序员需知的11个在线教程网站,建议收藏!
- ISE14.7 Spartan3e 呼吸灯
- python开发和大数据开发工程师_大数据开发工程师的岗位职责
- 【解决方案】解决ImportError: Library “GLU“ not found.问题
- php微信狗,PHP 微信狗小程序 搜鱼CMS商业正版V3.1 一键生成小程序