【问题标题】:Read REG_RESOURCE_LIST memory values - Incorrect value读取 REG_RESOURCE_LIST 内存值 - 值不正确
【发布时间】:2018-07-07 05:02:44
【问题描述】:

我正在尝试使用以下代码读取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

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

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

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

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

【问题讨论】:

    标签: c++ windows winapi registry regedit


    【解决方案1】:

    如果值类型为REG_RESOURCE_LIST,则值数据为CM_RESOURCE_LIST 结构。需要使用它来代替*(DWORD*)(lpData + 16);lpData + 24。无论如何,如果Count != 1,您的代码不正确。您尝试打印的是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
    

    你说:

    注册表编辑器显示 0x41f000000 而不是 0x41f0000

    0x41f000000 移动了 8 位 0x41f0000。基于这个明显的,你真的在​​这里有CmResourceTypeMemoryLargeCM_RESOURCE_MEMORY_40 标志。

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

    40 位长度的高 32 位,以字节为单位,范围为 分配的内存地址。最低 8 位被视为零。

    所以转储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 个调用之间更改(一些更改值),并且您可能无法在第二次调用 RegQueryValueExtoo 时获取数据。我们也可以在第一次调用时分配合理的内存空间,但前提是它还不够——在下一次调用时重新分配。所以最好在循环中调用它,直到我们得到 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;
    }
    

    【讨论】:

    • 非常感谢您的回答。我是否正确只需要移动长度值而不需要移动起始地址?
    • @Thomas - 是的,只有长度,起始地址已经是 64 位值了
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-23
    • 1970-01-01
    • 2014-08-28
    • 1970-01-01
    相关资源
    最近更新 更多