本文主要是介绍C++通过读取二进制流的方式来解析PE(静态文件读取法),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
步骤解读
- 先选择文件
- 读取文件二进制流
- 从二进制流读取
DOS头(DOS_HEADER)
,长度64字节 - 读取
DOS壳(DOS_STUB)
,DOS头开始,长度至到dosHeader->e_lfanew偏移量 - 读取
PE标识(Signature)
,e_lfanew偏移量开始,长度4字节 - 读取
PE文件头(FILE_HEADER)
,PE标识开始,长度20字节 - 读取
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(静态文件读取法)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!