能否将内存中PE文件写入磁盘?C语言实现遇内存访问错误求助
问题分析与修复方案
你遇到的「invalid access to memory」错误,核心原因是混淆了PE文件在内存和磁盘中的结构差异——你的代码直接用磁盘文件的字段来访问内存中的PE数据,同时错误地将内存布局直接写入磁盘,导致生成的PE结构无效。
关键错误点
- 错误使用
PointerToRawData访问内存区段:PointerToRawData是PE文件在磁盘中的区段偏移,运行时PE加载到内存后,这个字段已失去意义。内存中的区段起始地址应该用VirtualAddress计算:(BYTE*)hModule + sectionHeader[i].VirtualAddress。 - 头部复制长度错误:你用
dosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS)作为头部复制长度,会遗漏区段表(Section Table)内容。正确的头部总长度应该用optionalHeader->SizeOfHeaders,这个字段定义了磁盘文件中所有头部(DOS头、NT头、区段表)的总字节数。 - 直接按内存布局写入磁盘:内存中的PE按虚拟地址(VirtualAddress)排列区段,而磁盘中的PE按文件偏移(PointerToRawData)排列,直接写入内存布局会导致PE加载器无法正确解析区段。
修正后的代码实现
下面是调整后的代码,兼顾内存PE读取和磁盘PE结构的正确生成:
// 获取当前模块的PE头 IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)hModule; IMAGE_NT_HEADERS* ntHeaders = (IMAGE_NT_HEADERS*)((BYTE*)hModule + dosHeader->e_lfanew); IMAGE_FILE_HEADER* fileHeader = &ntHeaders->FileHeader; IMAGE_OPTIONAL_HEADER* optionalHeader = &ntHeaders->OptionalHeader; // 计算磁盘PE文件的总大小:头部大小 + 所有区段的磁盘大小之和 DWORD fileSize = optionalHeader->SizeOfHeaders; IMAGE_SECTION_HEADER* sectionHeader = (IMAGE_SECTION_HEADER*)(ntHeaders + 1); for (int i = 0; i < fileHeader->NumberOfSections; i++) { fileSize += sectionHeader[i].SizeOfRawData; } // 分配磁盘文件缓冲区 unsigned char* buffer = (unsigned char*)malloc(fileSize); if (!buffer) { // 处理内存分配失败逻辑 return; } memset(buffer, 0, fileSize); // 1. 复制完整头部(包含区段表) memcpy(buffer, dosHeader, optionalHeader->SizeOfHeaders); // 2. 复制每个区段到磁盘文件对应的偏移位置 IMAGE_SECTION_HEADER* destSectionHeader = (IMAGE_SECTION_HEADER*)((BYTE*)buffer + dosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS)); for (int i = 0; i < fileHeader->NumberOfSections; i++) { // 内存中的区段起始地址 BYTE* srcSection = (BYTE*)hModule + sectionHeader[i].VirtualAddress; // 磁盘文件中的区段起始偏移 BYTE* destSection = buffer + destSectionHeader[i].PointerToRawData; // 复制内容:取内存实际大小和磁盘大小的较小值,剩余部分补0(已memset过,可省略但保留严谨性) DWORD copySize = min(sectionHeader[i].Misc.VirtualSize, sectionHeader[i].SizeOfRawData); memcpy(destSection, srcSection, copySize); if (sectionHeader[i].SizeOfRawData > copySize) { memset(destSection + copySize, 0, sectionHeader[i].SizeOfRawData - copySize); } } // 3. 写入磁盘文件 HANDLE hFile = CreateFileA("dump.exe", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { DWORD bytesWritten; WriteFile(hFile, buffer, fileSize, &bytesWritten, NULL); CloseHandle(hFile); } free(buffer);
额外注意事项
- 如果目标PE启用了ASLR,dump后的文件运行时可能需要重定位;但如果是dump当前进程自身的模块,重定位已完成,运行dump文件基本正常。
- 部分区段(如.tls、.reloc)在运行时可能被系统修改,dump后的文件会和原始磁盘文件存在差异,但核心功能不受影响。
内容的提问来源于stack exchange,提问作者MasterYii
相关产品推荐
相关产品推荐

