我目前使用的是64位Windows7和Windows7。我正在使用一些PSAPI(process status api)函数来了解windows如何管理内存。
然而,我注意到QueryWorkingSet包含了我无法阅读的条目(例如,第0页,而您无法阅读0x00000000)。当在64位上尝试时,很明显的原因是:QueryWorkingSet在32位上被窃听,因为地址被截断(因此多个页面0条目)。
但是,64位QueryWorkingSet返回的某些条目也不可访问。为什么这个明显不可访问的内存显示为可访问?这是QueryWorkingSet中的另一个bug吗?此外,为什么没有任何加载的模块显示在结果中?它们不是工作组的一部分吗?他们是……
这是一个小的示例程序,它使用seh尝试读取页面,并在页面不可读时报告该页面(而QueryWorkingSet告诉我它是):

#include <windows.h>
#include <psapi.h>
#include <stdio.h>

char z;

int main(int argc, char **argv) {
    unsigned int counter;
    HANDLE thisProcess = GetCurrentProcess();
    SYSTEM_INFO si;
    PSAPI_WORKING_SET_INFORMATION wsi_1, *wsi;
    DWORD wsi_size;

    GetSystemInfo(&si);

    wsi_1.NumberOfEntries = 0;
    QueryWorkingSet(thisProcess, (LPVOID)&wsi_1, sizeof(wsi));

#if !defined(_WIN64)
    wsi_1.NumberOfEntries--;
#endif
    wsi_size = sizeof(PSAPI_WORKING_SET_INFORMATION)
             + sizeof(PSAPI_WORKING_SET_BLOCK) * wsi_1.NumberOfEntries;
    wsi = (PSAPI_WORKING_SET_INFORMATION*)HeapAlloc(GetProcessHeap(),
        HEAP_ZERO_MEMORY, wsi_size);

    if (!QueryWorkingSet(thisProcess, (LPVOID)wsi, wsi_size)) {
        printf("# Second QueryWorkingSet failed: %lu\n"
            , GetLastError());
        return 1;
    }

    for (counter = 0; counter < wsi->NumberOfEntries; counter++) {
        unsigned long long page = (unsigned long long)wsi->WorkingSetInfo[counter].VirtualPage;
        DWORD protection = (DWORD)wsi->WorkingSetInfo[counter].Protection;

        __try {
            if (*(char*)(page * si.dwPageSize) || TRUE) {
                z = (protection & 5) ? '-' : 'T';
            }
        } __except(GetExceptionCode() == STATUS_ACCESS_VIOLATION) {
            z = (protection & 5) ? 'F' : '-';
        }

        if (z == 'F') {
            printf("%p %p : %c%c%c%c%c %c\n"
                , (LPVOID)(page * si.dwPageSize)
                , (LPVOID)((page + 1) * si.dwPageSize)
                , protection & 16 ? 'G' : '-'
                , protection &  8 ? 'V' : '-'
                , protection &  4 ? 'w' : '-'
                , protection &  2 ? 'x' : '-'
                , protection &  1 ? 'r' : '-'
                , z
                );
        }
    }

    return 0;
}

32位版本提供以下输出:
7DBED000 7DBEE000 : --w-- F
7DBEE000 7DBEF000 : --w-- F
7DC00000 7DC01000 : --w-- F
80008000 80009000 : --w-- F
01080000 01081000 : --w-- F
01000000 01001000 : --w-- F
00000000 00001000 : --w-- F
7DA00000 7DA01000 : --w-- F
40001000 40002000 : --w-- F
003F7000 003F8000 : --w-- F
003FF000 00400000 : --w-- F
003BA000 003BB000 : --w-- F
00001000 00002000 : --w-- F
003B9000 003BA000 : --w-- F
40000000 40001000 : --w-- F
00006000 00007000 : --w-- F
00002000 00003000 : --w-- F
00000000 00001000 : --w-- F
0039A000 0039B000 : --w-- F
003A6000 003A7000 : --w-- F
003A8000 003A9000 : --w-- F
003AC000 003AD000 : --w-- F
00003000 00004000 : --w-- F

64位版本提供了:
FFFFF6FB7DBED000 FFFFF6FB7DBEE000 : --w-- F
FFFFF6FB7DBEE000 FFFFF6FB7DBEF000 : --w-- F
FFFFF6FB7DC00000 FFFFF6FB7DC01000 : --w-- F
FFFFF6FB80008000 FFFFF6FB80009000 : --w-- F
FFFFF70001080000 FFFFF70001081000 : --w-- F
FFFFF70000000000 FFFFF70000001000 : --w-- F
FFFFF70001000000 FFFFF70001001000 : --w-- F
FFFFF7000107F000 FFFFF70001080000 : --w-- F
FFFFF6FB7DA0F000 FFFFF6FB7DA10000 : --w-- F
FFFFF6FB41FFF000 FFFFF6FB42000000 : --w-- F
FFFFF683FFFFF000 FFFFF68400000000 : --w-- F
FFFFF6FB40001000 FFFFF6FB40002000 : --w-- F
FFFFF680003FF000 FFFFF68000400000 : --w-- F
FFFFF6FB7DA00000 FFFFF6FB7DA01000 : --w-- F
FFFFF6FB40005000 FFFFF6FB40006000 : --w-- F
FFFFF68000A00000 FFFFF68000A01000 : --w-- F
FFFFF6FB40000000 FFFFF6FB40001000 : --w-- F
FFFFF68000000000 FFFFF68000001000 : --w-- F
FFFFF680003B9000 FFFFF680003BA000 : --w-- F
FFFFF68000001000 FFFFF68000002000 : --w-- F
FFFFF680003B6000 FFFFF680003B7000 : --w-- F
FFFFF680003B7000 FFFFF680003B8000 : --w-- F
FFFFF683FF7FA000 FFFFF683FF7FB000 : --w-- F
FFFFF683FF7EC000 FFFFF683FF7ED000 : --w-- F
FFFFF6FB41FFB000 FFFFF6FB41FFC000 : --w-- F
FFFFF680003F7000 FFFFF680003F8000 : --w-- F
FFFFF680003BA000 FFFFF680003BB000 : --w-- F

最佳答案

我假设这是由于本机api中的缺陷造成的。K32QueryWorkingSet非常天真地使用了NtQueryVirtualMemory,假设它可以将结果原样传递给调用者。根据this article,有问题的结果是指页面表和工作集条目,它们在内核空间是完全可读的,但不能从用户空间直接访问。
一个快速的解决方法是清理结果,这样在MmSystemRangeStart下面找到的地址就被认为是有效的。此值只能在内核模式下读取,因此必须对用户模式应用程序进行“猜测”。对于Windows 7 64位,它是0xFFFF080000000000。对于32位版本来说有点棘手(由于4GB的优化),但我不确定这个bug是否真的发生在那里。

关于windows - QueryWorkingSet的结果中包含无效页面,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/8285506/

10-15 00:37