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

【讨论】:

感谢您的提示。我阅读了 GetSystemInfo() 和 GetNativeSystemInfo() 的区别。但是在我的操作系统中,两种尺寸都是一样的。 RegionSize 通常包含多个页面。但你是对的,一次处理更大的块是有意义的。

以上是关于ReadProcessMemory 在某些页面上失败 (GetLastError()=299)的主要内容,如果未能解决你的问题,请参考以下文章

ReadProcessMemory 总是读取 0

我可以使用 ReadProcessMemory 在 Windows 中读取进程的程序内存吗?

ReadProcessMemory WriteProcessMemory iOS

使用 ReadProcessMemory 获取字符串值的访问冲突

如何使用 ReadProcessMemory 获取托盘按钮文本

确定外部进程的主线程 ID(ReadProcessMemory - 错误 299)