读取 REG_RESOURCE_LIST 个内存值 - 不正确的值

Read REG_RESOURCE_LIST memory values - Incorrect value

我正在尝试使用以下代码读取 Hardware\ResourceMap\System Resources\Physical Memory 中的物理内存值:

#include <iostream>
#include <conio.h>
#include <windows.h>
#include <string>
#include <stdlib.h>

using namespace std;

int main() 
{
    HKEY hKey = NULL;
    LPCTSTR pszSubKey = L"Hardware\ResourceMap\System Resources\Physical Memory";
    LPCTSTR pszValueName = L".Translated";

    if (! RegOpenKey(HKEY_LOCAL_MACHINE, pszSubKey, &hKey) == ERROR_SUCCESS)
    {
        cout << "RegOpenKey failed" << endl;
        return 0;
    }

    DWORD dwType = 0;
    LPBYTE lpData = NULL;
    DWORD dwLength = 0;

    if (! RegQueryValueEx(hKey, pszValueName, 0, &dwType, NULL, &dwLength) == ERROR_SUCCESS)
    {
        cout << "RegOpenKey failed" << endl;
        return 0;
    }

    lpData = new BYTE[dwLength];
    RegQueryValueEx(hKey, pszValueName, 0, &dwType, lpData, &dwLength);
    RegCloseKey(hKey);

    DWORD dwResourceCount = *(DWORD*)(lpData + 16);

    auto pmi = lpData + 24;

    for (int dwIndex = 0; dwIndex < dwResourceCount; dwIndex++)
    {
        auto start = *(uint64_t*)(pmi + 0);
        cout << "-> 0x" << hex << start;

        auto length = *(uint64_t*)(pmi + 8);
        cout << "\t + 0x" << hex << length;

        auto endaddr = start + length;
        cout << "\t0x" << hex << endaddr << endl;

        pmi += 20;
    }

    delete[]lpData;
}

示例输出:

-> 0x1000        + 0x57000      0x58000
-> 0x59000       + 0x46000      0x9f000
-> 0x100000      + 0xc855f000   0xc865f000
-> 0xc8666000    + 0xbf3000     0xc9259000
-> 0xc9759000    + 0x13779000   0xdced2000
-> 0xdd0d8000    + 0x3c000      0xdd114000
-> 0xddfff000    + 0x1000       0xde000000
-> 0x100000000   + 0x41f0000    0x1041f0000

问题是最后的长度值不正确。

而不是 0x41f0000,注册表编辑器显示 0x41f000000 是正确的值:

这几天我一直在研究这个问题,但无法弄清楚为什么我在这里得到一个错误的值。

任何有更多 Win32 使用经验的人都可以 API 帮助我吗?

如果值类型是 REG_RESOURCE_LIST 值数据是 CM_RESOURCE_LIST structure. need use it instead of *(DWORD*)(lpData + 16);, lpData + 24. anyway your code is incorrect in case Count != 1. what you try print is CM_PARTIAL_RESOURCE_DESCRIPTOR 结构。但是您没有检查 CM_PARTIAL_RESOURCE_DESCRIPTORType 成员。但它可能会有所不同。可以是 CmResourceTypeMemory 但也可以是 CmResourceTypeMemoryLarge - 你没有考虑到这一点。如果 CmResourceTypeMemoryLarge 需要为

检查 Flags
CM_RESOURCE_MEMORY_LARGE_40
CM_RESOURCE_MEMORY_LARGE_48
CM_RESOURCE_MEMORY_LARGE_64

你说:

Instead of 0x41f0000 the regeditor shows 0x41f000000

但是 0x41f000000 移动了 8 位 0x41f0000。基于这一点,你确实在这里 CmResourceTypeMemoryLargeCM_RESOURCE_MEMORY_40 标志。

在这种情况下需要使用 Length40 成员:

The high 32 bits of the 40-bit length, in bytes, of the range of allocated memory addresses. The lowest 8 bits are treated as zero.

因此转储 CM_RESOURCE_LIST 的代码必须是下一个:

BOOL Memory(PCM_RESOURCE_LIST pcrl, ULONG size)
{
    if (size < FIELD_OFFSET(CM_RESOURCE_LIST, List))
    {
        return FALSE;
    }

    size -= FIELD_OFFSET(CM_RESOURCE_LIST, List);

    if (ULONG Count = pcrl->Count)
    {
        PCM_FULL_RESOURCE_DESCRIPTOR List = pcrl->List;

        do 
        {
            if (size < FIELD_OFFSET(CM_FULL_RESOURCE_DESCRIPTOR, PartialResourceList.PartialDescriptors))
            {
                return FALSE;
            }

            size -= FIELD_OFFSET(CM_FULL_RESOURCE_DESCRIPTOR, PartialResourceList.PartialDescriptors);

            DbgPrint("InterfaceType=%x BusNumber=%u\n", List->InterfaceType, List->BusNumber);

            if (ULONG n = List->PartialResourceList.Count)
            {
                PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptors = List->PartialResourceList.PartialDescriptors;

                do 
                {
                    if (size < sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR))
                    {
                        return FALSE;
                    }

                    size -= sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);

                    ULONG64 Length = PartialDescriptors->u.Memory.Length;

                    switch (PartialDescriptors->Type)
                    {
                    case CmResourceTypeMemoryLarge:

                        switch (PartialDescriptors->Flags & (CM_RESOURCE_MEMORY_LARGE_40|
                            CM_RESOURCE_MEMORY_LARGE_48|CM_RESOURCE_MEMORY_LARGE_64))
                        {
                        case CM_RESOURCE_MEMORY_LARGE_40:
                            Length <<= 8;
                            break;
                        case CM_RESOURCE_MEMORY_LARGE_48:
                            Length <<= 16;
                            break;
                        case CM_RESOURCE_MEMORY_LARGE_64:
                            Length <<= 32;
                            break;
                        default:
                            DbgPrint("unknown mamory type\n");
                            continue;
                        }
                    case CmResourceTypeMemory:
                        DbgPrint("%016I64x %I64x\n", 
                            PartialDescriptors->u.Memory.Start.QuadPart, Length);
                        break;
                    }

                } while (PartialDescriptors++, --n);
            }

        } while (List++, --Count);
    }

    return size == 0;
}

同样,当我们获取数据时——即使出现错误也不必忘记关闭键句柄(当 RegQueryValueEx 失败时你不会这样做)并使用 RegOpenKeyExW 而不是 RegOpenKey 来指定能力所需的密钥访问权限。使用 2 次顺序调用 RegQueryValueEx(使用 0 个缓冲区并分配一次缓冲区)也不是最好的。因为理论上缓冲区大小可以在这 2 次调用之间更改(某些更改值),并且您也可能无法在第二次调用 RegQueryValueEx 中获取数据。我们也可以在第一次调用时分配合理的内存 space,并且只有当它不够时 - 在下次调用时重新分配。所以最好在循环中调用它,直到我们得到 ERROR_MORE_DATA 并且第一次调用已经不为空的缓冲区:

ULONG Memory()
{
    HKEY hKey;

    ULONG dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE, 
        L"Hardware\ResourceMap\System Resources\Physical Memory", 
        0, KEY_READ, &hKey);

    if (dwError == NOERROR)
    {
        ULONG cb = 0x100;

        do 
        {
            dwError = ERROR_NO_SYSTEM_RESOURCES;

            union {
                PVOID buf;
                PBYTE pb;
                PCM_RESOURCE_LIST pcrl;
            };

            if (buf = LocalAlloc(0, cb))
            {
                ULONG dwType;

                if ((dwError = RegQueryValueExW(hKey, L".Translated", 
                    0, &dwType, pb, &cb)) == NOERROR)
                {
                    if (dwType == REG_RESOURCE_LIST)
                    {
                        if (!Memory(pcrl, cb))
                        {
                            DbgPrint("error parsing resource list\n");
                        }
                    }
                    else
                    {
                        dwError = ERROR_INVALID_DATATYPE;
                    }
                }

                LocalFree(buf);
            }

        } while (dwError == ERROR_MORE_DATA);

        RegCloseKey(hKey);
    }

    return dwError;
}