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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 11:39:50