为何出现std::bad_alloc异常?C++缓冲区分配问题求助
解决std::bad_alloc异常的内存扫描优化方案
这个std::bad_alloc异常说白了就是内存分配撑爆了——你代码里尝试一次性申请整个内存区域的大小,而Windows进程里有些内存区域可能大到几个GB,堆内存根本扛不住这么大的单次分配,直接就抛出异常了。咱们从核心问题入手,一步步修复:
核心问题分析
你原来的代码里char* chunk = new char[info.RegionSize];这行是罪魁祸首:VirtualQueryEx返回的RegionSize可能是非常大的数值(比如系统预留的大内存块),一次性在堆上申请这么多内存,直接超出了堆的可用空间,触发内存分配失败。
具体修复步骤
1. 改用小缓冲区分批读取
不要一次性读取整个内存区域,用固定大小的小缓冲区(比如4KB、64KB)分批次读取,这样不管区域多大,都不会瞬间占用大量内存。比如定义一个4KB的缓冲区:
const size_t BUFFER_SIZE = 4096; // 可根据实际情况调整大小 std::vector<char> chunk(BUFFER_SIZE); // 用vector更安全,自动管理内存
然后在读取时,循环处理每个缓冲区,每次读取的字节数取「剩余区域大小」和「缓冲区大小」的最小值。
2. 过滤不可读的内存区域
不是所有内存区域都能读取,提前过滤掉无效区域,避免无效读取和资源浪费:
- 跳过未提交的区域:
info.State != MEM_COMMIT - 跳过无访问权限的区域:
(info.Protect & (PAGE_NOACCESS | PAGE_GUARD)) != 0 - 只保留可读的区域:
(info.Protect & (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY)) != 0
3. 修复循环逻辑的小bug
你原来代码里的p = (char*)info.BaseAddress;是多余的,VirtualQueryEx已经把当前区域的基地址存在info.BaseAddress里,循环里的p += info.RegionSize已经能正确移动到下一个区域,这行代码会导致重复处理同一区域。
修改后的完整代码示例
#include <iostream> #include <vector> #include <windows.h> #include <cstring> using namespace std; int main() { DWORD systemPid = 123; // 替换成目标进程的PID // 只申请需要的权限,PROCESS_ALL_ACCESS太容易失败 HANDLE systemH = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, systemPid); if (systemH && systemPid > 4) { SYSTEM_INFO si; GetSystemInfo(&si); MEMORY_BASIC_INFORMATION info; char* p = reinterpret_cast<char*>(si.lpMinimumApplicationAddress); // 从最小应用地址开始 const size_t BUFFER_SIZE = 4096; std::vector<char> chunk(BUFFER_SIZE); while (p < reinterpret_cast<char*>(si.lpMaximumApplicationAddress)) { if (VirtualQueryEx(systemH, p, &info, sizeof(info)) == sizeof(info)) { // 过滤不可读的内存区域 if (info.State == MEM_COMMIT && !(info.Protect & (PAGE_NOACCESS | PAGE_GUARD)) && (info.Protect & (PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY))) { char* regionBase = reinterpret_cast<char*>(info.BaseAddress); size_t remainingSize = info.RegionSize; // 分批读取整个区域 while (remainingSize > 0) { size_t readSize = min(remainingSize, BUFFER_SIZE); SIZE_T bytesRead; if (ReadProcessMemory(systemH, regionBase, chunk.data(), readSize, &bytesRead)) { // 扫描缓冲区中的"exe"字符串 for (size_t i = 0; i < (bytesRead - 2); ++i) { // 注意是bytesRead-2,因为要匹配3个字符 if (memcmp("exe", &chunk[i], 3) == 0) { cout << "Found \"exe\" at address: " << reinterpret_cast<void*>(regionBase + i) << endl; } } } regionBase += bytesRead; remainingSize -= bytesRead; } } p += info.RegionSize; // 移动到下一个内存区域 } else { // VirtualQueryEx失败,移动一页继续扫描 p += si.dwPageSize; } } cout << "Finish" << endl; CloseHandle(systemH); // 记得关闭进程句柄,避免资源泄漏 } else { cout << "Failed to open process" << endl; } return 0; }
额外注意事项
- 尽量用
std::vector代替裸指针的new/delete,避免手动管理内存时出现泄漏或者释放错误 - 读取内存后一定要检查
bytesRead的实际值,避免越界访问缓冲区 - 申请进程权限时,只申请需要的权限(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ),过高的权限不仅容易申请失败,还存在安全风险
内容的提问来源于stack exchange,提问作者DeathZ_
相关产品推荐
相关产品推荐

