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

使用CreateProcess创建挂起进程后ReadProcessMemory报错299求助

问题分析与解决

你的问题根源出在地址不匹配,以及挂起进程的模块加载状态上,具体拆解如下:

1. 错误核心原因

  • 你通过GetModuleHandle("user32.dll")和GetProcAddress拿到的MessageBoxA地址,是当前进程内user32.dll导出函数的地址,并非目标进程的对应地址。Windows的ASLR(地址空间布局随机化)机制会让每个进程的DLL加载基址独立,跨进程直接复用当前进程的函数地址,本质是在访问目标进程中不存在或无权限的内存区域。
  • 你用CREATE_SUSPENDED创建进程,此时目标进程大概率还没加载user32.dll——这类非核心系统DLL通常是进程启动后按需加载的,挂起状态下系统不会自动触发加载流程。

错误码299的提示“仅完成部分读写请求”,正是因为你要读取的6字节内存,在目标进程中要么不存在,要么无法被正常访问。

2. 正确解决步骤

要读取目标进程内MessageBoxA的内存,必须先拿到该进程内该函数的真实地址:

步骤一:确保目标进程已加载user32.dll

挂起状态的进程不会自动加载user32.dll,需要手动触发:

HMODULE remoteLoadLibrary(HANDLE hProcess) {
    LPVOID loadLibAddr = (LPVOID)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");
    LPVOID dllNameAddr = VirtualAllocEx(hProcess, NULL, 12, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (!dllNameAddr) return NULL;

    WCHAR dllName[] = L"user32.dll";
    WriteProcessMemory(hProcess, dllNameAddr, dllName, sizeof(dllName), NULL);

    HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)loadLibAddr, dllNameAddr, 0, NULL);
    WaitForSingleObject(hThread, INFINITE);

    HMODULE hMod;
    GetExitCodeThread(hThread, (LPDWORD)&hMod);

    CloseHandle(hThread);
    VirtualFreeEx(hProcess, dllNameAddr, 0, MEM_RELEASE);
    return hMod;
}

步骤二:获取目标进程内MessageBoxA的真实地址

拿到目标进程中user32.dll的基址后,不能用当前进程的GetProcAddress,需要手动解析目标进程的PE导出表:

FARPROC getRemoteProcAddress(HANDLE hProcess, HMODULE hMod, LPCSTR procName) {
    // 读取DOS头
    IMAGE_DOS_HEADER dosHeader;
    ReadProcessMemory(hProcess, hMod, &dosHeader, sizeof(dosHeader), NULL);

    // 读取NT头
    IMAGE_NT_HEADERS ntHeaders;
    ReadProcessMemory(hProcess, (LPBYTE)hMod + dosHeader.e_lfanew, &ntHeaders, sizeof(ntHeaders), NULL);

    // 读取导出表
    IMAGE_EXPORT_DIRECTORY exportDir;
    ReadProcessMemory(hProcess, (LPBYTE)hMod + ntHeaders.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress, &exportDir, sizeof(exportDir), NULL);

    // 读取函数名表、地址表、序号表
    DWORD* funcNames = (DWORD*)VirtualAlloc(NULL, exportDir.NumberOfNames * sizeof(DWORD), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    ReadProcessMemory(hProcess, (LPBYTE)hMod + exportDir.AddressOfNames, funcNames, exportDir.NumberOfNames * sizeof(DWORD), NULL);

    DWORD* funcAddrs = (DWORD*)VirtualAlloc(NULL, exportDir.NumberOfFunctions * sizeof(DWORD), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    ReadProcessMemory(hProcess, (LPBYTE)hMod + exportDir.AddressOfFunctions, funcAddrs, exportDir.NumberOfFunctions * sizeof(DWORD), NULL);

    WORD* funcOrds = (WORD*)VirtualAlloc(NULL, exportDir.NumberOfNames * sizeof(WORD), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    ReadProcessMemory(hProcess, (LPBYTE)hMod + exportDir.AddressOfNameOrdinals, funcOrds, exportDir.NumberOfNames * sizeof(WORD), NULL);

    // 查找目标函数
    FARPROC result = NULL;
    for (DWORD i = 0; i < exportDir.NumberOfNames; i++) {
        char name[256];
        ReadProcessMemory(hProcess, (LPBYTE)hMod + funcNames[i], name, sizeof(name), NULL);
        if (strcmp(name, procName) == 0) {
            result = (FARPROC)((LPBYTE)hMod + funcAddrs[funcOrds[i]]);
            break;
        }
    }

    // 释放内存
    VirtualFree(funcNames, 0, MEM_RELEASE);
    VirtualFree(funcAddrs, 0, MEM_RELEASE);
    VirtualFree(funcOrds, 0, MEM_RELEASE);
    return result;
}

步骤三:正确读取目标进程内存

拿到真实地址后,再调用ReadProcessMemory即可。另外建议把read_mem的参数改成PROCESS_INFORMATION* pi(指针传递),避免结构体拷贝导致句柄失效。

额外注意点

  • 不要滥用PROCESS_ALL_ACCESS权限,仅请求所需权限即可:PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION | PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION,过高权限会增加请求失败的概率。
  • 操作完成后记得关闭所有打开的句柄,避免资源泄漏。

内容的提问来源于stack exchange,提问作者loaded_dypper

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 03:24:57