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

相关文章

Debezium 与 Apache Kafka 的集成方式步骤详解

《Debezium与ApacheKafka的集成方式步骤详解》本文详细介绍了如何将Debezium与ApacheKafka集成,包括集成概述、步骤、注意事项等,通过KafkaConnect,D... 目录一、集成概述二、集成步骤1. 准备 Kafka 环境2. 配置 Kafka Connect3. 安装 D

C#中读取XML文件的四种常用方法

《C#中读取XML文件的四种常用方法》Xml是Internet环境中跨平台的,依赖于内容的技术,是当前处理结构化文档信息的有力工具,下面我们就来看看C#中读取XML文件的方法都有哪些吧... 目录XML简介格式C#读取XML文件方法使用XmlDocument使用XmlTextReader/XmlTextWr

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

Springboot中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录

SQL 中多表查询的常见连接方式详解

《SQL中多表查询的常见连接方式详解》本文介绍SQL中多表查询的常见连接方式,包括内连接(INNERJOIN)、左连接(LEFTJOIN)、右连接(RIGHTJOIN)、全外连接(FULLOUTER... 目录一、连接类型图表(ASCII 形式)二、前置代码(创建示例表)三、连接方式代码示例1. 内连接(I

Android里面的Service种类以及启动方式

《Android里面的Service种类以及启动方式》Android中的Service分为前台服务和后台服务,前台服务需要亮身份牌并显示通知,后台服务则有启动方式选择,包括startService和b... 目录一句话总结:一、Service 的两种类型:1. 前台服务(必须亮身份牌)2. 后台服务(偷偷干

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

最长公共子序列问题的深度分析与Java实现方式

《最长公共子序列问题的深度分析与Java实现方式》本文详细介绍了最长公共子序列(LCS)问题,包括其概念、暴力解法、动态规划解法,并提供了Java代码实现,暴力解法虽然简单,但在大数据处理中效率较低,... 目录最长公共子序列问题概述问题理解与示例分析暴力解法思路与示例代码动态规划解法DP 表的构建与意义动

JS 实现复制到剪贴板的几种方式小结

《JS实现复制到剪贴板的几种方式小结》本文主要介绍了JS实现复制到剪贴板的几种方式小结,包括ClipboardAPI和document.execCommand这两种方法,具有一定的参考价值,感兴趣的... 目录一、Clipboard API相关属性方法二、document.execCommand优点:缺点: