在进入Windows之后,Runtime Service 和 ACPI Table之类的仍然存在于内存中,但是Windows没有提供标准的方法进行访问。用户能获得的只有经过包装之后 ACPI Table (实际上是来自注册表) ,或者根本就没有提供(比如:Runtime Service Table)。如果想再次获得这样的信息,只能通过扫描系统保留内存来完成。但是 Windows 没有提供类似 E820 的机制告知用户(应该是因为这种信息对于用户来说是根本无需了解的)。经过一段时间的研究,找到了在 Windows下获得系统保留内存的方法:在注册表 HKLM\HARDWARE\RESOURCEMAP\System Resources\Loader Reserved的位置。以我目前使用的笔记本电脑为例,打开这个位置之后可以看到
此外,Device Manager中也会给出硬件占用的内存地址,但是这个和系统的保留内存是没有任何关系的。
CM_RESOURCE_LIST structure 【参考1】
typedef struct _CM_RESOURCE_LIST { ULONG Count; CM_FULL_RESOURCE_DESCRIPTOR List[1]; } CM_RESOURCE_LIST, *PCM_RESOURCE_LIST;
接下来是下面这个结构体【参考2】
typedef struct _CM_FULL_RESOURCE_DESCRIPTOR { INTERFACE_TYPE InterfaceType; // unused for WDM == 0 ULONG BusNumber; // unused for WDM CM_PARTIAL_RESOURCE_LIST PartialResourceList; } CM_FULL_RESOURCE_DESCRIPTOR, *PCM_FULL_RESOURCE_DESCRIPTOR;
继续解析【参考3】。其中 ULONG 占用 4BYTES;USHORT占用2BYTES;UCHAR占用1BYTES.
typedef struct _CM_PARTIAL_RESOURCE_LIST { USHORT Version; == 00 USHORT Revision; ULONG Count; CM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptors[1]; } CM_PARTIAL_RESOURCE_LIST, *PCM_PARTIAL_RESOURCE_LIST;
接下来就是每一个保留的内存情况
CM_PARTIAL_RESOURCE_DESCRIPTOR structure【参考4】 typedef struct _CM_PARTIAL_RESOURCE_DESCRIPTOR { UCHAR Type; UCHAR ShareDisposition; USHORT Flags; union { ……………… ……………… ……………… }
根据上面的方法即可获得当前系统中 Loader 通知系统的 Reserved Memory
对于 type的定义在【参考5】
Identifies the resource type. The constant value specified for Type indicates which structure within the u union is valid, as indicated in the following table. (These flags are used within both CM_PARTIAL_RESOURCE_DESCRIPTORand IO_RESOURCE_DESCRIPTOR structures, except where noted.)
有了上面的基础就可以编写代码:
// ConsoleApplication3.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <Windows.h> #include <strsafe.h> #include <tchar.h> HKEY m_hKey; #define OffSet(type, field) ((size_t)&(((type*)0)->field)) #pragma pack(1) // // Physical address. // typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS; typedef struct _CM_PARTIAL_RESOURCE_DESCRIPTOR { UCHAR Type; UCHAR ShareDisposition; USHORT Flags; union { // // Range of resources, inclusive. These are physical, bus relative. // It is known that Port and Memory below have the exact same layout // as Generic. // struct { PHYSICAL_ADDRESS Start; ULONG Length; } Generic; // // struct { PHYSICAL_ADDRESS Start; ULONG Length; } Port; // // struct { #if defined(NT_PROCESSOR_GROUPS) USHORT Level; USHORT Group; #else ULONG Level; #endif ULONG Vector; KAFFINITY Affinity; } Interrupt; // // Values for message signaled interrupts are distinct in the // raw and translated cases. // struct { union { struct { #if defined(NT_PROCESSOR_GROUPS) USHORT Group; #else USHORT Reserved; #endif USHORT MessageCount; ULONG Vector; KAFFINITY Affinity; } Raw; struct { #if defined(NT_PROCESSOR_GROUPS) USHORT Level; USHORT Group; #else ULONG Level; #endif ULONG Vector; KAFFINITY Affinity; } Translated; } DUMMYUNIONNAME; } MessageInterrupt; // // Range of memory addresses, inclusive. These are physical, bus // relative. The value should be the same as the one passed to // HalTranslateBusAddress(). // struct { PHYSICAL_ADDRESS Start; // 64 bit physical addresses. ULONG Length; } Memory; // // Physical DMA channel. // struct { ULONG Channel; ULONG Port; ULONG Reserved1; } Dma; struct { ULONG Channel; ULONG RequestLine; UCHAR TransferWidth; UCHAR Reserved1; UCHAR Reserved2; UCHAR Reserved3; } DmaV3; // // Device driver private data, usually used to help it figure // what the resource assignments decisions that were made. // struct { ULONG Data[3]; } DevicePrivate; // // Bus Number information. // struct { ULONG Start; ULONG Length; ULONG Reserved; } BusNumber; // // Device Specific information defined by the driver. // The DataSize field indicates the size of the data in bytes. The // data is located immediately after the DeviceSpecificData field in // the structure. // struct { ULONG DataSize; ULONG Reserved1; ULONG Reserved2; } DeviceSpecificData; // The following structures provide support for memory-mapped // IO resources greater than MAXULONG struct { PHYSICAL_ADDRESS Start; ULONG Length40; } Memory40; struct { PHYSICAL_ADDRESS Start; ULONG Length48; } Memory48; struct { PHYSICAL_ADDRESS Start; ULONG Length64; } Memory64; struct { UCHAR Class; UCHAR Type; UCHAR Reserved1; UCHAR Reserved2; ULONG IdLowPart; ULONG IdHighPart; } Connection; } u; } CM_PARTIAL_RESOURCE_DESCRIPTOR, *PCM_PARTIAL_RESOURCE_DESCRIPTOR; // // A Partial Resource List is what can be found in the ARC firmware // or will be generated by ntdetect.com. // The configuration manager will transform this structure into a Full // resource descriptor when it is about to store it in the regsitry. // // Note: There must a be a convention to the order of fields of same type, // (defined on a device by device basis) so that the fields can make sense // to a driver (i.e. when multiple memory ranges are necessary). // typedef struct _CM_PARTIAL_RESOURCE_LIST { USHORT Version; USHORT Revision; ULONG Count; CM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptors[1]; } CM_PARTIAL_RESOURCE_LIST, *PCM_PARTIAL_RESOURCE_LIST; // // Define the I/O bus interface types. // typedef enum _INTERFACE_TYPE { InterfaceTypeUndefined = -1, Internal, Isa, Eisa, MicroChannel, TurboChannel, PCIBus, VMEBus, NuBus, PCMCIABus, CBus, MPIBus, MPSABus, ProcessorInternal, InternalPowerBus, PNPISABus, PNPBus, Vmcs, ACPIBus, MaximumInterfaceType }INTERFACE_TYPE, *PINTERFACE_TYPE; // // A Full Resource Descriptor is what can be found in the registry. // This is what will be returned to a driver when it queries the registry // to get device information; it will be stored under a key in the hardware // description tree. // // Note: There must a be a convention to the order of fields of same type, // (defined on a device by device basis) so that the fields can make sense // to a driver (i.e. when multiple memory ranges are necessary). // typedef struct _CM_FULL_RESOURCE_DESCRIPTOR { INTERFACE_TYPE InterfaceType; // unused for WDM ULONG BusNumber; // unused for WDM CM_PARTIAL_RESOURCE_LIST PartialResourceList; } CM_FULL_RESOURCE_DESCRIPTOR, *PCM_FULL_RESOURCE_DESCRIPTOR; // // The Resource list is what will be stored by the drivers into the // resource map via the IO API. // typedef struct _CM_RESOURCE_LIST { ULONG Count; CM_FULL_RESOURCE_DESCRIPTOR List[1]; } CM_RESOURCE_LIST, *PCM_RESOURCE_LIST; int main() { DWORD Index; BYTE *v; if (RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("HARDWARE\\RESOURCEMAP\\System Resources\\Loader Reserved"), 0, KEY_READ, &m_hKey) != ERROR_SUCCESS) { printf("RegOpenKeyEx fail \n"); getchar(); exit(0); } BOOL bRet = FALSE; LPSTR lpstrValue; DWORD dwType = REG_SZ; DWORD lpcbData; DWORD r; r = RegQueryValueEx(m_hKey, TEXT(".Raw"), NULL, &dwType, NULL, &lpcbData); if (r != ERROR_SUCCESS) { printf("Can't get data from registry\n"); getchar(); exit(0); } bRet = FALSE; lpstrValue = (LPSTR)malloc(lpcbData); r = RegQueryValueEx(m_hKey, TEXT(".Raw"), NULL, &dwType, (BYTE*)(LPCTSTR)lpstrValue, &lpcbData); if (r != ERROR_SUCCESS) { printf("fail\n"); getchar(); exit(0); } for (Index = 0; Index < lpcbData; Index++) { if (Index % 16 == 0) { printf("\n"); } v = (BYTE*)lpstrValue; printf("%02X ", *(v + Index)); } PCM_RESOURCE_LIST res= (PCM_RESOURCE_LIST)lpstrValue; PCM_PARTIAL_RESOURCE_LIST Partial; DWORD Index1; for (Index = 0; Index < res->Count; Index++) { printf("\nInteface type: %d\n Bus Number: %d\n", res->List[Index].InterfaceType, res->List[Index].BusNumber); Partial = (PCM_PARTIAL_RESOURCE_LIST) &res->List[Index].PartialResourceList; printf(" Version: %d\n Revision: %d\n Counter: %x\n", Partial->Version, Partial->Revision, Partial->Count); for (Index1 = 0; Index1 < Partial->Count; Index1++) { //printf("%d\n", Partial->PartialDescriptors[Index1].Type); if (Partial->PartialDescriptors[Index1].Type == 3) { printf("Start:%016I64x Length:%x \n", Partial->PartialDescriptors[Index1].u.Memory.Start, Partial->PartialDescriptors[Index1].u.Memory.Length ); } } } free(lpstrValue); getchar(); return 0; }
运行结果如下(运行的机器和之前做分析的不同,所以 Counter数量不同)
参考:
1. https://msdn.microsoft.com/en-us/library/windows/hardware/ff541994(v=vs.85).aspx
2. https://msdn.microsoft.com/en-us/library/windows/hardware/ff541954(v=vs.85).aspx
3. https://msdn.microsoft.com/en-us/library/windows/hardware/ff541981(v=vs.85).aspx
4. https://msdn.microsoft.com/en-us/library/windows/hardware/ff541977(v=vs.85).aspx
5. https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/wdm/ns-wdm-_cm_partial_resource_descriptor
是这样...
System Reserved Memory Range :
---------------------------------------------------------------------------------------------------------------------
Index Start Address Limit Address Size About Flag
1 0x0000000000000000 0x0000000000100000 0x0000000000100000 1024.00KB 0x00000103
2 0x0000000000103000 0x00000000001A7000 0x00000000000A4000 656.00KB 0x00000103
3 0x00000000002E1000 0x00000000002F1000 0x0000000000010000 64.00KB 0x00000103
4 0x000000008DDAB000 0x000000008F40E000 0x0000000001663000 22.39MB 0x00000103
5 0x000000008F40F000 0x0000000090000000 0x0000000000BF1000 11.94MB 0x00000103
6 0x00000000E0000000 0x00000000F0000000 0x0000000010000000 256.00MB 0x00000103
7 0x00000000FE000000 0x00000000FE011000 0x0000000000011000 68.00KB 0x00000103
8 0x00000000FEC00000 0x00000000FEC01000 0x0000000000001000 4.00KB 0x00000103
9 0x00000000FED00000 0x00000000FED04000 0x0000000000004000 16.00KB 0x00000103
10 0x00000000FEE00000 0x00000000FEE01000 0x0000000000001000 4.00KB 0x00000103
11 0x00000000FF000000 0x0000000100000000 0x0000000001000000 16.00MB 0x00000103
---------------------------------------------------------------------------------------------------------------------
Total 0x000000001341F000 308.12MB
這篇分析很不錯, 剛好要把自己的工具增加相關的功能, 感謝!
有研究过每一段后面的Flag如何解析?
例如我解析到的Flag 0x00000103, 0x02000107具体代表什么?
貌似没有查到具体代表的意义是什么...
Physical Memory Range :
------------------------------------------------------------------------------------------------------------------
Index Start Address Limit Address Size About Flag
1 0x0000000000001000 0x000000000009F000 0x000000000009E000 632.00KB 0x00000103
2 0x0000000000100000 0x00000000791C3000 0x00000000790C3000 1.89GB 0x00000103
3 0x000000007A2A9000 0x000000007A392000 0x00000000000E9000 932.00KB 0x00000103
4 0x000000007AC0E000 0x000000007AC0F000 0x0000000000001000 4.00KB 0x00000103
5 0x0000000100000000 0x000000047E000000 0x000000037E000000 13.97GB 0x02000107
------------------------------------------------------------------------------------------------------------------
Total 0x00000003F724B000 15.8616GB
uefi 怎么 reseve memory呀,楼主?
楼主,uefi 怎么reserve memory呀?
gBS -> AllocatePool 函数可以分配设置的,你可以参考 https://www.lab-z.com/shellwin/
谢谢站长的回复。
我的问题是这样子的,要想在内核中reserve 一块特定物理内存,不被系统分配使用,而是只由自己使用。
gBS -> AllocatePool 函数只能指定memory type,无法指定具体的地址值吧?
比如, 以内存位置0x22000000为起始地址,reserve 1KB的内存,使得这1KB的内存不被OS使用。
是的,这个函数无法指定内存位置。如果有这样的需求你只能去研究一下扫描 PCI 设备空间分配的内存的函数看看了。具体我没做过,只是猜测。