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

相关文章

SpringBoot中@Value注入静态变量方式

《SpringBoot中@Value注入静态变量方式》SpringBoot中静态变量无法直接用@Value注入,需通过setter方法,@Value(${})从属性文件获取值,@Value(#{})用... 目录项目场景解决方案注解说明1、@Value("${}")使用示例2、@Value("#{}"php

SpringBoot分段处理List集合多线程批量插入数据方式

《SpringBoot分段处理List集合多线程批量插入数据方式》文章介绍如何处理大数据量List批量插入数据库的优化方案:通过拆分List并分配独立线程处理,结合Spring线程池与异步方法提升效率... 目录项目场景解决方案1.实体类2.Mapper3.spring容器注入线程池bejsan对象4.创建

线上Java OOM问题定位与解决方案超详细解析

《线上JavaOOM问题定位与解决方案超详细解析》OOM是JVM抛出的错误,表示内存分配失败,:本文主要介绍线上JavaOOM问题定位与解决方案的相关资料,文中通过代码介绍的非常详细,需要的朋... 目录一、OOM问题核心认知1.1 OOM定义与技术定位1.2 OOM常见类型及技术特征二、OOM问题定位工具

C++右移运算符的一个小坑及解决

《C++右移运算符的一个小坑及解决》文章指出右移运算符处理负数时左侧补1导致死循环,与除法行为不同,强调需注意补码机制以正确统计二进制1的个数... 目录我遇到了这么一个www.chinasem.cn函数由此可以看到也很好理解总结我遇到了这么一个函数template<typename T>unsigned

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

深度解析Python中递归下降解析器的原理与实现

《深度解析Python中递归下降解析器的原理与实现》在编译器设计、配置文件处理和数据转换领域,递归下降解析器是最常用且最直观的解析技术,本文将详细介绍递归下降解析器的原理与实现,感兴趣的小伙伴可以跟随... 目录引言:解析器的核心价值一、递归下降解析器基础1.1 核心概念解析1.2 基本架构二、简单算术表达

HTTP 与 SpringBoot 参数提交与接收协议方式

《HTTP与SpringBoot参数提交与接收协议方式》HTTP参数提交方式包括URL查询、表单、JSON/XML、路径变量、头部、Cookie、GraphQL、WebSocket和SSE,依据... 目录HTTP 协议支持多种参数提交方式,主要取决于请求方法(Method)和内容类型(Content-Ty

深度解析Java @Serial 注解及常见错误案例

《深度解析Java@Serial注解及常见错误案例》Java14引入@Serial注解,用于编译时校验序列化成员,替代传统方式解决运行时错误,适用于Serializable类的方法/字段,需注意签... 目录Java @Serial 注解深度解析1. 注解本质2. 核心作用(1) 主要用途(2) 适用位置3

Java MCP 的鉴权深度解析

《JavaMCP的鉴权深度解析》文章介绍JavaMCP鉴权的实现方式,指出客户端可通过queryString、header或env传递鉴权信息,服务器端支持工具单独鉴权、过滤器集中鉴权及启动时鉴权... 目录一、MCP Client 侧(负责传递,比较简单)(1)常见的 mcpServers json 配置

从原理到实战解析Java Stream 的并行流性能优化

《从原理到实战解析JavaStream的并行流性能优化》本文给大家介绍JavaStream的并行流性能优化:从原理到实战的全攻略,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的... 目录一、并行流的核心原理与适用场景二、性能优化的核心策略1. 合理设置并行度:打破默认阈值2. 避免装箱