ReadProcessMemory 在某些页面上失败 (GetLastError()=299)
Posted
技术标签:
【中文标题】ReadProcessMemory 在某些页面上失败 (GetLastError()=299)【英文标题】:ReadProcessMemory fails on some Pages (GetLastError()=299) 【发布时间】:2012-08-20 18:44:15 【问题描述】:我尝试读取进程的所有已提交页面 (Win7-64)。在大多数页面上它都可以工作,但在少数页面上却失败了。我无法解释为什么。这是我的测试程序(x32编译,Win7-64测试):
#include <windows.h>
void main()
HANDLE hProc = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,FALSE,GetCurrentProcessId());
SYSTEM_INFO si;
ZeroMemory(&si,sizeof(SYSTEM_INFO));
GetSystemInfo(&si);
char* buf = new char[si.dwPageSize];
for (unsigned i = 0; i < 0x7fff0; i++)
void* baseOffs = (void*) (i * si.dwPageSize);
MEMORY_BASIC_INFORMATION mbi;
ZeroMemory(&mbi,sizeof(MEMORY_BASIC_INFORMATION));
if (VirtualQueryEx(hProc, baseOffs, &mbi, sizeof(MEMORY_BASIC_INFORMATION)) == 0)
MessageBox(NULL, TEXT("VirtualQueryEx failed"),TEXT(""),MB_OK);
if (mbi.State == MEM_COMMIT)
SIZE_T numByteWritten = 0;
if(ReadProcessMemory(hProc, baseOffs,buf,si.dwPageSize,&numByteWritten) == FALSE)
OutputDebugString(TEXT("bad\n")); //GetLastError()==ERROR_PARTIALLY_READ; numByteWritten == 0;
else
OutputDebugString(TEXT("good\n"));
delete[] buf;
我厌倦了查看失败页面的 MEMORY_BASIC_INFORMATION,但我没有发现任何奇怪的地方。此外,失败页面的数量因运行而异(平均约为 5)。是什么阻止我阅读这些页面?我需要调整进程令牌中的一些权限吗?
【问题讨论】:
我不知道,但是如果目标进程在 ReadProcessMemory 试图复制页面时取消映射页面会发生什么?收到此错误后,页面是否仍然提交? 是的,如果我在 ReadProcessMemory 失败后直接运行 VirtualQuery,它仍然会为该页面返回 State==MEM_COMMIT。我还在一个循环中对整个程序进行了两次测试,并且在两次运行中相同的页面都失败了。但是,当我多次运行该进程时,我会得到不同的页面(也有不同的编号)。 这是 64 位操作系统上的常见错误代码。不知道为什么,我怀疑 wow64 模拟器与它有关。 如果您通过VirtualProtect
将页面显式设置为可读会发生什么?查看您的程序如何运行,您可能正在访问不驻留在内存中的页面,并且ReadProcessMemory
不会触发它们被分页,请尝试检查失败页面的内存信息类型。
仅供参考:“ERROR_PARTIAL_COPY 299 (0x12B) 只有部分 ReadProcessMemory 或 WriteProcessMemory 请求已完成”
【参考方案1】:
一点点调试和一些有趣的事情被确定:所有失败的页面都设置了保护位PAGE_GUARD
(参见MSDN doc)。在我解释文档时,您无法使用ReadProcessMemory
阅读这些页面是设计使然。
if(ReadProcessMemory(hProc, baseOffs,buf,si.dwPageSize,&numByteWritten) == FALSE)
assert(mbi.Protect & 0x100);
OutputDebugString(TEXT("bad\n")); //GetLastError()==ERROR_PARTIALLY_READ; numByteWritten == 0;
else
assert(!(mbi.Protect & 0x100));
OutputDebugString(TEXT("good\n"));
【讨论】:
然而我想,我已经仔细检查过了,你是完全正确的。我添加了 VirtualProtectEx 以暂时删除标志,一切都很好。 你是如何调用 VirtualProtectEx 的?我也尝试为另一个进程覆盖它,但它不会更改标志。它说,旧标志始终为 0x1 (PAGE_NOACCESS)。【参考方案2】:32 位 Windows 上的页面大小与 64 位 Windows 上的页面大小不同。因此,页面大小是每个进程的值。您的进程的页面大小不一定与您正在读取的进程的页面大小相同。请改用MEMORY_BASIC_INFORMATION
的RegionSize
成员。这是受影响区域的实际大小。
【讨论】:
感谢您的提示。我阅读了 GetSystemInfo() 和 GetNativeSystemInfo() 的区别。但是在我的操作系统中,两种尺寸都是一样的。 RegionSize 通常包含多个页面。但你是对的,一次处理更大的块是有意义的。以上是关于ReadProcessMemory 在某些页面上失败 (GetLastError()=299)的主要内容,如果未能解决你的问题,请参考以下文章
我可以使用 ReadProcessMemory 在 Windows 中读取进程的程序内存吗?
ReadProcessMemory WriteProcessMemory iOS
使用 ReadProcessMemory 获取字符串值的访问冲突