C++通过读取二进制流的方式来解析PE(静态文件读取法)

2024-05-25 15:36

本文主要是介绍C++通过读取二进制流的方式来解析PE(静态文件读取法),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

步骤解读

  1. 先选择文件
  2. 读取文件二进制流
  3. 从二进制流读取DOS头(DOS_HEADER),长度64字节
  4. 读取DOS壳(DOS_STUB),DOS头开始,长度至到dosHeader->e_lfanew偏移量
  5. 读取PE标识(Signature),e_lfanew偏移量开始,长度4字节
  6. 读取PE文件头(FILE_HEADER),PE标识开始,长度20字节
  7. 读取PE可选头(OPTIONAL_HEADER),PE文件头开始,长度peHeader->sizeOfOptionalHeader

直接上代码

代码中只解析了重要信息

#include<iostream>
#include<windows.h>using namespace std;BOOL selectFile(char* filePath) {OPENFILENAMEA ofn;char filename[MAX_PATH];ZeroMemory(&ofn, sizeof(ofn));ofn.lStructSize = sizeof(ofn);ofn.hwndOwner = NULL;ofn.lpstrFilter = "ALL Files\0*.*\0";ofn.lpstrFile = filename;ofn.nMaxFile = sizeof(filename);ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;ofn.lpstrFile[0] = '\0';if (GetOpenFileNameA(&ofn) == TRUE) {//MessageBoxA(NULL, filename, "提示", MB_OK);strcpy_s(filePath,MAX_PATH,filename);return TRUE;}return FALSE;
}int main() {char filePath[MAX_PATH];if (selectFile(filePath)) {//打开文件HANDLE hfile = CreateFileA(filePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);//根据文件句柄获取文件大小DWORD fileSize = GetFileSize(hfile,NULL);//文件流缓冲区char* fileBuffer = new char[fileSize];//实际接收字节数DWORD realRead = 0;//读取文件if (ReadFile(hfile, fileBuffer, fileSize, &realRead, NULL)) {WORD e_magic = *(WORD*)fileBuffer;	//MZ标识DWORD e_lfanew = *(DWORD*)(fileBuffer + 60);	//NT头偏移量printf("e_magic:%04X\n", e_magic);printf("e_lfanew:%08X\n", e_lfanew);printf("\n");DWORD Signatrue = *(DWORD*)(fileBuffer + e_lfanew);	//PE标识printf("Siganture:%08X\n", Signatrue);printf("\n");WORD Machine = *(WORD*)(fileBuffer + e_lfanew + 0x04);	//运行平台WORD NumberOfSections = *(WORD*)(fileBuffer + e_lfanew + 0x06);	//区段数量DWORD TimeDateStamp = *(DWORD*)(fileBuffer + e_lfanew + 0x08);	//区段数量WORD SizeOfOptionHeader = *(WORD*)(fileBuffer + e_lfanew + 0x14);	//可选头大小WORD Characteristics = *(WORD*)(fileBuffer + e_lfanew + 0x16);	//特征printf("Machine:%04X\n", Machine);printf("NumberOfSections:%04X\n", NumberOfSections);printf("TimeDateStamp:%04X\n", TimeDateStamp);printf("SizeOfOptionHeader:%04X\n", SizeOfOptionHeader);printf("Characteristics.EXECUTABLE_IMAGE:%d\n", Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE);	//是否是可执行文件printf("Characteristics.IMAGE_FILE_LINE_NUMS_STRIPPED:%d\n", Characteristics & IMAGE_FILE_LINE_NUMS_STRIPPED);	//文件中不包含行号信息。printf("Characteristics.IMAGE_FILE_LOCAL_SYMS_STRIPPED:%d\n", Characteristics & IMAGE_FILE_LOCAL_SYMS_STRIPPED);	//文件中不包含局部符号。printf("Characteristics.IMAGE_FILE_32BIT_MACHINE:%d\n", Characteristics & IMAGE_FILE_32BIT_MACHINE);	//目标平台是32位。printf("Characteristics.IMAGE_FILE_DEBUG_STRIPPED:%d\n", Characteristics & IMAGE_FILE_DEBUG_STRIPPED);	//调试信息被移除。printf("Characteristics.IMAGE_FILE_SYSTEM:%d\n", Characteristics & IMAGE_FILE_SYSTEM);	//文件是系统文件。printf("Characteristics.IMAGE_FILE_DLL:%d\n", Characteristics & IMAGE_FILE_DLL);	//文件是dll文件。printf("\n");WORD Magic = *(WORD*)(fileBuffer  + e_lfanew + 0x18);	//0x10B是32位,0x20B是64位DWORD AddressOfEntryPoint = *(DWORD*)(fileBuffer + e_lfanew + 0x28);	//OEP程序入口偏移量DWORD ImageBase = *(DWORD*)(fileBuffer + e_lfanew + 0x34);	//程序入口,固定值+偏移量DWORD SectionAlignment = *(DWORD*)(fileBuffer + e_lfanew + 0x38);	//内存对齐大小DWORD FileAlignment = *(DWORD*)(fileBuffer + e_lfanew + 0x3C);	//文件对齐大小DWORD SizeOfIamge = *(DWORD*)(fileBuffer + e_lfanew + 0x50);	//文件在内存中的大小,按SectionAlignment对齐后的大小DWORD SizeOfHeaders = *(DWORD*)(fileBuffer + e_lfanew + 0x54);	//DOS,NT,PE,可选PE+区段 各种头加一块,按照FileAlignment对齐后的大小DWORD NumberOfRvaAndSizes = *(DWORD*)(fileBuffer + e_lfanew + 0x74);	//数据目录表的个数printf("Magic:%04X\n", Magic);printf("AddressOfEntryPoint:%08X\n", AddressOfEntryPoint);printf("ImageBase:%08X\n", ImageBase);printf("SectionAlignment:%08X\n", SectionAlignment);printf("FileAlignment:%08X\n", FileAlignment);printf("SizeOfIamge:%08X\n", SizeOfIamge);printf("SizeOfHeaders:%08X\n", SizeOfHeaders);printf("NumberOfRvaAndSizes:%08X\n", NumberOfRvaAndSizes);CloseHandle(hfile);}else {int errcode = GetLastError();cout << "文件读取失败:"<< errcode << endl;}}else {cout << "文件选择失败。" << endl;}
}

这篇关于C++通过读取二进制流的方式来解析PE(静态文件读取法)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1001911

相关文章

gradle第三方Jar包依赖统一管理方式

《gradle第三方Jar包依赖统一管理方式》:本文主要介绍gradle第三方Jar包依赖统一管理方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录背景实现1.顶层模块build.gradle添加依赖管理插件2.顶层模块build.gradle添加所有管理依赖包

C#如何调用C++库

《C#如何调用C++库》:本文主要介绍C#如何调用C++库方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录方法一:使用P/Invoke1. 导出C++函数2. 定义P/Invoke签名3. 调用C++函数方法二:使用C++/CLI作为桥接1. 创建C++/CL

Linux之systemV共享内存方式

《Linux之systemV共享内存方式》:本文主要介绍Linux之systemV共享内存方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、工作原理二、系统调用接口1、申请共享内存(一)key的获取(二)共享内存的申请2、将共享内存段连接到进程地址空间3、将

Maven中引入 springboot 相关依赖的方式(最新推荐)

《Maven中引入springboot相关依赖的方式(最新推荐)》:本文主要介绍Maven中引入springboot相关依赖的方式(最新推荐),本文给大家介绍的非常详细,对大家的学习或工作具有... 目录Maven中引入 springboot 相关依赖的方式1. 不使用版本管理(不推荐)2、使用版本管理(推

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

C#使用StackExchange.Redis实现分布式锁的两种方式介绍

《C#使用StackExchange.Redis实现分布式锁的两种方式介绍》分布式锁在集群的架构中发挥着重要的作用,:本文主要介绍C#使用StackExchange.Redis实现分布式锁的... 目录自定义分布式锁获取锁释放锁自动续期StackExchange.Redis分布式锁获取锁释放锁自动续期分布式

Golang HashMap实现原理解析

《GolangHashMap实现原理解析》HashMap是一种基于哈希表实现的键值对存储结构,它通过哈希函数将键映射到数组的索引位置,支持高效的插入、查找和删除操作,:本文主要介绍GolangH... 目录HashMap是一种基于哈希表实现的键值对存储结构,它通过哈希函数将键映射到数组的索引位置,支持

Java对象转换的实现方式汇总

《Java对象转换的实现方式汇总》:本文主要介绍Java对象转换的多种实现方式,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Java对象转换的多种实现方式1. 手动映射(Manual Mapping)2. Builder模式3. 工具类辅助映

如何使用 Python 读取 Excel 数据

《如何使用Python读取Excel数据》:本文主要介绍使用Python读取Excel数据的详细教程,通过pandas和openpyxl,你可以轻松读取Excel文件,并进行各种数据处理操... 目录使用 python 读取 Excel 数据的详细教程1. 安装必要的依赖2. 读取 Excel 文件3. 读

Spring Boot读取配置文件的五种方式小结

《SpringBoot读取配置文件的五种方式小结》SpringBoot提供了灵活多样的方式来读取配置文件,这篇文章为大家介绍了5种常见的读取方式,文中的示例代码简洁易懂,大家可以根据自己的需要进... 目录1. 配置文件位置与加载顺序2. 读取配置文件的方式汇总方式一:使用 @Value 注解读取配置方式二