使用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
相关产品推荐
相关产品推荐

