win10(或者说windows)读取物理内存方式有很多种,但是在DISPATCH_LEVEL下读取物理内存的方式就少的可怜了,原因是DISPATCH_LEVEL无法执行缺页中断,所以像MDL的方法就会直接导致蓝屏。这里一般的情况下,我会推荐使用MmMapIoSpace,将物理地址映射成虚拟地址。但是在win10 的1803版本之后,这个方法也不太适用了,那么有没有替代的方法甚至是更为通杀的方法呢?
肯定是有的,不然我就不会写这个文章了!
首先我们先来分析,分页页表等级分为9-9-9-9-12 (4 * 9 + 12 = 48),这个我应该没有记错,我们的页表存在于Cr3,然而现在MmMapIoSpace不让用了,所以,我们只能从另一个方向入手:PTE,我写了一个程序,源代码如下:
#include "stdio.h" int main() { unsigned char* pUserBuffer = (unsigned char *)"Hello World!\n"; printf("0x%p\n", pUserBuffer); getchar(); printf("%s\n", pUserBuffer); getchar(); }
这段代码是将一个字符串的虚拟地址打印出来,然后等待按键,再然后输入该字符串,编译后,放入虚拟机中,看看实际运行情况我们查看一下运行情况:
此时我们可以得到字符串的虚拟地址,0x00DD86EC,我们通过PTE读取这个虚拟地址的物理地址基址,公式是 物理地址基址 = *(ULONG_PTR *)(PTE_BASE + ( 0x00DD86EC >> 12) * 8) & 0xFFFFFFFFF000 ,那么这个PTE_BASE 是多少呢?论坛里面的hzqst 大神总结了几种方法,https://bbs.pediy.com/thread-254276.htm ,这里就不在赘述,我们采用的是tandasat的方法。
那么,现在的PTE_BASE就是 0xFFFFFB8000000000,我们带入公式中算一下,结果为 fffffb8000006ec0,目前为止我们需要挂靠进程,才能读到对应的内容:
通过公式我们可以出,物理地址的基址是 0x11cfe000,我们再加上原虚拟地址的低12位偏移0x6eC,查看一下物理内存:
可以看到我们能够正确的读取了。
讲到这里,应该已经能猜到我的方法了,但是我还是打算说出来,希望大家能表示惊讶一下,就是构造一段连续的物理地址,然后根据PTE_BASE找到自己对应的PTE,将自己的地址挂靠到对应的物理地址上面。
那么自己手工构造怎么构造呢?如果你想到了PLM4,那么恭喜你,你想的太深了,我们只需要申请一段NonPage内存就可以了,这样子,系统会自动替我们构造好,问题又来了,我们需要申请多大的呢?我们注意到一个PTE可以描述的是4KB大小的物理地址,所以,我们就申请4KB就好了。
笔者不太了解ExAllocateMemoryWithTag如果申请0x1000(4KB)个字节会是具体申请多少,在这里,笔者选用了MmAllocateContiguousMemory,根据文档,只要是4KB对齐的大小,申请出来的内存都会是4kb对齐的(就是低12位为0)。
现在看来思路清晰了,我们申请的内存挂靠到对应的物理地址的4kb对齐的头部地址,然后再加上物理地址的低12位偏移,就可以了。
结合实际情况就是:我们设我们申请出来的内存虚拟地址为X,则X的PTE描述地址为:p = (PTE_BASE + (x >> 12) * 8) ,将 p 地址中的 48 ~ 12位替换为要读取的物理地址的48 ~ 12位,然后我们就可以通过 p + offset 读取到内容了。(我们暂时不考虑内容会被交换到硬盘上)
代码如下:
#include "ntddk.h" void DriverUnload(PDRIVER_OBJECT pDriverObject) { } extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pUnicodeString) { pDriverObject->DriverUnload = DriverUnload; KIRQL kIrql = KeRaiseIrqlToDpcLevel(); ULONG_PTR PTE_BASE = 0xFFFFF68000000000; ULONG_PTR pReadPhysicalAddress = 0x708c6EC; PHYSICAL_ADDRESS dtPhysical; dtPhysical.QuadPart = -1; unsigned char* p = (unsigned char *)MmAllocateContiguousMemory(0x1000, dtPhysical); if (p) { KdPrint(("MmAllocateContiguousMemory Address : 0x%p\n", p)); ULONG_PTR* pPte = (ULONG_PTR *)(PTE_BASE + (((ULONG_PTR)p & 0xFFFFFFFFF000) >> 12) * 8); ULONG_PTR nOldValue = *pPte; //保存原来的值 *pPte = (nOldValue & ~(0xFFFFFFFFF000)) | (pReadPhysicalAddress & 0xFFFFFFFFF000); unsigned char* pReadStart = p + (pReadPhysicalAddress & 0xFFF); for (size_t i = 0; i < 16; i++) { KdPrint(("%c", pReadStart[i])); } KdPrint(("\n")); *pPte = nOldValue; //恢复原来的值 MmFreeContiguousMemory(p); } KeLowerIrql(kIrql); return STATUS_SUCCESS; }
2020安全开发者峰会(2020 SDC)议题征集 中国.北京 7月!
最后于 14小时前 被不对编辑 ,原因: