ReadProcessMemory无法一次性读取大量字节问题求助
我用C语言开发一款内存读取软件,通过ReadProcessMemory函数访问目标进程内存。该函数读取少量字节时运行正常,但尝试读取大量字节时程序出现卡顿/冻结现象。以下是我的代码(我认为代码写法并非问题所在),代码中的bufferval是通过MODULEENTRY32对象(me32)获取me32.modBaseSize后手动填入的:
#include <stdio.h> #include <windows.h> #include <tlhelp32.h> #include <tchar.h> #include <string.h> BOOL GetProcessList() { char targetname[] = "increment.exe"; int bufferval = 2064300; SIZE_T noBsize = bufferval - 1; HANDLE hProcessSnap; HANDLE hProcess; PROCESSENTRY32 pe32; DWORD dwPriorityClass; hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hProcessSnap == INVALID_HANDLE_VALUE) { printf("cth32ss"); return 1; } pe32.dwSize = sizeof(PROCESSENTRY32); if (!Process32First(hProcessSnap, &pe32)) { printf("Process32First"); CloseHandle(hProcessSnap); return 1; } do { if (strcmp(pe32.szExeFile, targetname) == 0) { DWORD procID = pe32.th32ProcessID; printf("%lu", procID); printf("%s", pe32.szExeFile); HANDLE CGFProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, procID); if (CGFProcess == NULL) { printf("OPEN PROCESS FAIL"); CloseHandle(hProcessSnap); return 1; } printf("success\n"); HANDLE hModuleSnap = INVALID_HANDLE_VALUE; MODULEENTRY32 me32; hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, procID); if (hModuleSnap == INVALID_HANDLE_VALUE) { printf("module snap error"); CloseHandle(hModuleSnap); return 1; } me32.dwSize = sizeof(MODULEENTRY32); if (!Module32First(hModuleSnap, &me32)) { printf("Module32First"); CloseHandle(hModuleSnap); return 1; } LPCVOID baseaddr = NULL; do { if (strcmp(me32.szModule, targetname)) { printf("MODULE FOUND"); baseaddr = (const void*)me32.modBaseAddr; printf("MODSIZE:%i\n", me32.modBaseSize); break; } } while (Module32Next(hModuleSnap, &me32)); CloseHandle(hModuleSnap); int buffer[bufferval]; int* bufferp = (int*)&buffer; BOOL ReadMem = ReadProcessMemory(CGFProcess, baseaddr, bufferp, noBsize, NULL); printf("READ MEM:%i\n", ReadMem); FILE* FILE_PTR; FILE_PTR = fopen("procmem2.txt", "w"); int k; for (k = 0; k < noBsize; k++) { printf("%i", buffer[k]); printf(" "); fprintf(FILE_PTR, "%i", buffer[k]); fprintf(FILE_PTR, " "); } fclose(FILE_PTR); CloseHandle(CGFProcess); break; } } while (Process32Next(hProcessSnap, &pe32)); CloseHandle(hProcessSnap); return 0; } int main(void) { GetProcessList(); return 0; }
问题分析与修复方案
1. 栈溢出风险
你在栈上分配了int buffer[bufferval];,当bufferval=2064300时,数组大小约为8MB(每个int占4字节),而Windows默认栈大小通常只有1-8MB,这会直接导致栈溢出,引发未定义行为(包括卡顿、崩溃)。
修复:改用堆内存分配:
// 替换栈数组为堆分配 int* buffer = (int*)malloc(bufferval * sizeof(int)); if (!buffer) { printf("malloc failed\n"); CloseHandle(CGFProcess); return 1; } // 使用完后释放 free(buffer);
2. 模块匹配逻辑错误
if (strcmp(me32.szModule, targetname))的逻辑反了——strcmp返回0时表示字符串相等,当前代码会在不相等时进入分支,导致可能找不到正确的模块基地址,读取错误内存区域。
修复:
if (strcmp(me32.szModule, targetname) == 0) { // ... 后续逻辑 }
3. 一次性读取大量内存的阻塞问题
ReadProcessMemory一次性读取大内存块时,会占用大量系统资源,且如果目标内存中存在不可访问的页,会直接读取失败。分块读取可以降低阻塞风险,同时提高兼容性。
修复示例(分块读取):
#define BLOCK_SIZE 4096 // 4KB块大小 SIZE_T totalRead = 0; SIZE_T bytesRead; while (totalRead < noBsize) { SIZE_T readSize = min(BLOCK_SIZE, noBsize - totalRead); if (!ReadProcessMemory(CGFProcess, (BYTE*)baseaddr + totalRead, (BYTE*)buffer + totalRead, readSize, &bytesRead)) { printf("Read failed at offset %zu\n", totalRead); break; } totalRead += bytesRead; if (bytesRead < readSize) break; // 提前结束(比如内存区域结束) }
4. 控制台输出导致的卡顿
循环中频繁调用printf输出大量数据是非常慢的操作,会严重拖慢程序速度。建议只输出关键状态信息,所有内存数据直接写入文件即可。
修复:移除循环中的printf语句,只保留读取成功/失败的状态输出。
5. 变量类型不匹配
me32.modBaseSize是DWORD类型,你用int类型的bufferval接收,可能会因类型溢出导致错误。建议统一使用SIZE_T或DWORD定义内存大小相关变量。
修复:
DWORD bufferval = 2064300; SIZE_T noBsize = bufferval - 1;
修正后的完整代码示例
#include <stdio.h> #include <windows.h> #include <tlhelp32.h> #include <tchar.h> #include <string.h> #include <stdlib.h> #include <limits.h> BOOL GetProcessList() { char targetname[] = "increment.exe"; DWORD bufferval = 2064300; SIZE_T noBsize = bufferval - 1; HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hProcessSnap == INVALID_HANDLE_VALUE) { printf("CreateToolhelp32Snapshot failed\n"); return 1; } PROCESSENTRY32 pe32; pe32.dwSize = sizeof(PROCESSENTRY32); if (!Process32First(hProcessSnap, &pe32)) { printf("Process32First failed\n"); CloseHandle(hProcessSnap); return 1; } BOOL found = FALSE; do { if (strcmp(pe32.szExeFile, targetname) == 0) { DWORD procID = pe32.th32ProcessID; printf("Found process: ID=%lu, Name=%s\n", procID, pe32.szExeFile); HANDLE CGFProcess = OpenProcess(PROCESS_VM_READ, FALSE, procID); if (CGFProcess == NULL) { printf("OpenProcess failed, Error=%lu\n", GetLastError()); CloseHandle(hProcessSnap); return 1; } printf("Opened process successfully\n"); HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, procID); if (hModuleSnap == INVALID_HANDLE_VALUE) { printf("CreateToolhelp32Snapshot (module) failed, Error=%lu\n", GetLastError()); CloseHandle(CGFProcess); CloseHandle(hProcessSnap); return 1; } MODULEENTRY32 me32; me32.dwSize = sizeof(MODULEENTRY32); if (!Module32First(hModuleSnap, &me32)) { printf("Module32First failed, Error=%lu\n", GetLastError()); CloseHandle(hModuleSnap); CloseHandle(CGFProcess); CloseHandle(hProcessSnap); return 1; } LPCVOID baseaddr = NULL; do { if (strcmp(me32.szModule, targetname) == 0) { printf("Found target module\n"); baseaddr = (const void*)me32.modBaseAddr; printf("Module Base Address: %p, Size: %lu\n", baseaddr, me32.modBaseSize); bufferval = me32.modBaseSize; noBsize = bufferval; // 直接使用模块实际大小 found = TRUE; break; } } while (Module32Next(hModuleSnap, &me32)); CloseHandle(hModuleSnap); if (!found) { printf("Target module not found\n"); CloseHandle(CGFProcess); CloseHandle(hProcessSnap); return 1; } // 堆分配内存 BYTE* buffer = (BYTE*)malloc(bufferval); if (!buffer) { printf("malloc failed, Error=%lu\n", GetLastError()); CloseHandle(CGFProcess); CloseHandle(hProcessSnap); return 1; } // 分块读取内存 SIZE_T totalRead = 0; SIZE_T bytesRead; const SIZE_T BLOCK_SIZE = 4096; while (totalRead < noBsize) { SIZE_T readSize = (noBsize - totalRead) < BLOCK_SIZE ? (noBsize - totalRead) : BLOCK_SIZE; if (!ReadProcessMemory(CGFProcess, (BYTE*)baseaddr + totalRead, buffer + totalRead, readSize, &bytesRead)) { printf("ReadProcessMemory failed at offset %zu, Error=%lu\n", totalRead, GetLastError()); break; } totalRead += bytesRead; if (bytesRead < readSize) { printf("Partial read at offset %zu, read %zu bytes\n", totalRead - bytesRead, bytesRead); break; } } printf("Total bytes read: %zu\n", totalRead); // 写入文件 FILE* FILE_PTR = fopen("procmem2.txt", "wb"); if (FILE_PTR) { // 如果要写入文本格式,需要转换,但二进制写入更高效 // 这里保留文本格式示例,若要二进制可直接fwrite(buffer, 1, totalRead, FILE_PTR); for (SIZE_T k = 0; k < totalRead / sizeof(int); k++) { fprintf(FILE_PTR, "%d ", ((int*)buffer)[k]); } fclose(FILE_PTR); printf("Data written to procmem2.txt\n"); } else { printf("Failed to open file for writing\n"); } free(buffer); CloseHandle(CGFProcess); break; } } while (Process32Next(hProcessSnap, &pe32)); CloseHandle(hProcessSnap); return 0; } int main(void) { GetProcessList(); return 0; }
内容的提问来源于stack exchange,提问作者AerospaceMilitary420

