内核模式下物理地址有效性验证的Kernel API查询
内核模式下物理地址有效性验证方案
首先纠正你的代码误区:不能直接将物理地址作为指针传入memcpy,内核虚拟地址空间中无法直接访问物理地址,必须先通过MmMapIoSpace或MmGetVirtualForPhysical将其映射为虚拟地址后才能访问,否则必然触发蓝屏。
回到你的核心需求:在访问物理地址前验证其有效性,以下是几种无需依赖MmCopyMemory的可行方案:
方案一:通过系统物理内存范围列表验证
使用MmGetPhysicalMemoryRanges函数获取系统中所有已分配的物理内存块,遍历这些块检查目标物理地址是否落在有效范围内。这个方法适合批量地址验证,或者需要快速判断地址是否属于系统可用物理内存的场景。
代码示例
BOOL IsPhysicalAddressValid(PHYSICAL_ADDRESS targetPa, SIZE_T size) { PPHYSICAL_MEMORY_RANGE ranges = MmGetPhysicalMemoryRanges(); if (!ranges) return FALSE; PPHYSICAL_MEMORY_RANGE current = ranges; while (current->BaseAddress.QuadPart != 0 || current->NumberOfBytes.QuadPart != 0) { // 检查目标地址范围是否完全落在当前物理内存块内 if (targetPa.QuadPart >= current->BaseAddress.QuadPart && (targetPa.QuadPart + size) <= (current->BaseAddress.QuadPart + current->NumberOfBytes.QuadPart)) { ExFreePool(ranges); return TRUE; } current++; } ExFreePool(ranges); return FALSE; } // 修正后的ReadPhysicalAddress函数 BOOL ReadPhysicalAddress(PHYSICAL_ADDRESS TargetPhysicalAddress, PVOID Buffer, SIZE_T Size) { if (!IsPhysicalAddressValid(TargetPhysicalAddress, Size)) return FALSE; // 映射物理地址到虚拟地址 PVOID virtualAddr = MmMapIoSpace(TargetPhysicalAddress, Size, MmNonCached); if (!virtualAddr) return FALSE; memcpy(Buffer, virtualAddr, Size); MmUnmapIoSpace(virtualAddr, Size); return TRUE; }
方案二:通过页表查询验证(精准单地址验证)
如果只需要验证单个物理地址的有效性,可以通过遍历页表来判断该地址是否存在对应的有效页表项。这种方法更精准,但实现相对复杂,需要处理不同CPU架构(x86/x64)的页表结构差异。
x64架构下的简化示例
#include <ntddk.h> #include <intrin.h> BOOL IsPhysicalPageValid(PHYSICAL_ADDRESS pa) { // 获取当前进程的页目录基址(CR3) ULONG64 cr3 = __readcr3(); // 页目录项(PML4)索引 ULONG64 pml4Index = (pa.QuadPart >> 39) & 0x1FF; // 页目录指针项(PDPT)索引 ULONG64 pdptIndex = (pa.QuadPart >> 30) & 0x1FF; // 页目录项(PD)索引 ULONG64 pdIndex = (pa.QuadPart >> 21) & 0x1FF; // 页表项(PT)索引 ULONG64 ptIndex = (pa.QuadPart >> 12) & 0x1FF; // 映射PML4表到虚拟地址 PULONG64 pml4 = (PULONG64)MmMapIoSpace({cr3}, PAGE_SIZE, MmNonCached); if (!pml4) return FALSE; ULONG64 pml4Entry = pml4[pml4Index]; MmUnmapIoSpace(pml4, PAGE_SIZE); // 检查PML4项是否有效(存在且可访问) if (!(pml4Entry & 0x1)) // Present位 return FALSE; // 映射PDPT表 PHYSICAL_ADDRESS pdptPa = {pml4Entry & ~0xFFF}; PULONG64 pdpt = (PULONG64)MmMapIoSpace(pdptPa, PAGE_SIZE, MmNonCached); if (!pdpt) return FALSE; ULONG64 pdptEntry = pdpt[pdptIndex]; MmUnmapIoSpace(pdpt, PAGE_SIZE); if (!(pdptEntry & 0x1)) return FALSE; // 映射PD表 PHYSICAL_ADDRESS pdPa = {pdptEntry & ~0xFFF}; PULONG64 pd = (PULONG64)MmMapIoSpace(pdPa, PAGE_SIZE, MmNonCached); if (!pd) return FALSE; ULONG64 pdEntry = pd[pdIndex]; MmUnmapIoSpace(pd, PAGE_SIZE); // 检查是否是大页(2MB) if (pdEntry & 0x80) // PS位 return TRUE; // 映射PT表 PHYSICAL_ADDRESS ptPa = {pdEntry & ~0xFFF}; PULONG64 pt = (PULONG64)MmMapIoSpace(ptPa, PAGE_SIZE, MmNonCached); if (!pt) return FALSE; ULONG64 ptEntry = pt[ptIndex]; MmUnmapIoSpace(pt, PAGE_SIZE); return (ptEntry & 0x1) != 0; }
关于你尝试的MmIsIoSpaceActive的说明
这个函数仅用于验证IO端口空间的地址有效性,而系统模块的物理地址属于系统物理内存空间,不属于IO空间,所以调用该函数必然返回0,这是预期行为,不能用于物理内存地址的验证。
另外,不存在类似MmIsAddressValid的导出API直接验证物理地址,MmIsAddressValid仅针对虚拟地址生效。
内容的提问来源于stack exchange,提问作者YoAvrageBiTz
相关产品推荐
相关产品推荐

