You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否将内存中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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 02:25:07