【问题标题】:ReadProcessMemory fails on some Pages (GetLastError()=299)ReadProcessMemory 在某些页面上失败 (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 请求已完成”

标签: winapi windows-7 kernel32 readprocessmemory


【解决方案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_INFORMATIONRegionSize 成员。这是受影响区域的实际大小。

【讨论】:

  • 感谢您的提示。我阅读了 GetSystemInfo() 和 GetNativeSystemInfo() 的区别。但是在我的操作系统中,两种尺寸都是一样的。 RegionSize 通常包含多个页面。但你是对的,一次处理更大的块是有意义的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多