物理内存读写

2024/05/16 Tools 共 1833 字,约 6 分钟

不调用系统api读写物理内存

原理

利用内核模式下的内存映射技术,将一个物理地址映射到一个虚拟地址上,然后通过操作这个虚拟地址来读取物理内存的内容。具体步骤包括:

  1. 分配一个虚拟地址空间 Mapper,确保不会分配出大页,以保证后续操作的灵活性。
  2. 获取 Mapper 对应的页表条目,并保存原始的页帧号。
  3. 修改页表条目,将 Mapper 映射到目标物理地址上,然后刷新 TLB 以确保页表修改生效。
  4. 关闭中断,避免并发问题。
  5. 使用汇编指令将物理内存数据复制到目标缓冲区。
  6. 恢复原始的页表条目,以确保不会影响其他操作。
  7. 恢复中断状态,保证系统的正常运行。

实现代码

PVOID Mapper = 0;
PTE_64* MapperPte = 0;
u64 MapperPteOrigPfn = 0;
 
VOID Init()
{
    Mapper = MmAllocateIndependentPages(0x1000, -1);
    memset(Mapper, 0, 0x1000);
    MapperPte = MiGetPteAddress(Mapper);
    MapperPteOrigPfn = MapperPte->PageFrameNumber;
}
 
VOID ReadPhysicalMemoryInPage(PVOID Buffer, u64 Phys, u64 Size)
{
    _disable();
 
    MapperPte->PageFrameNumber = Phys >> PAGE_SHIFT;
    __invlpg(Mapper);
 
    __movsb((PUCHAR)Buffer, (PUCHAR)Mapper + (Phys & 0xFFF), Size);
 
    MapperPte->PageFrameNumber = MapperPteOrigPfn;
 
    _enable();
}

原理:

先分配一个Mapper,用MmAllocateIndependentPages的原因是不会分配出大页,用ExAllocatePool等api是有概率分配出大页Pde内存的哦

获取Mapper的Pte并保存原始Pfn,用于后续还原

改Pte->Pfn使得Mapper映射我们的物理地址,刷tlb,拷贝内存,还原Pfn

关中断的原因是invlpg之后切到其他核心会导致在其他核心的tlb没被刷新从而拷贝到错误的数据,其实提升irql就行了

注意事项:

多核情况要注意分配多个Mapper给每个核心使用

仅拷贝一个页面,跨页要自己处理

未修复内存缓存属性,读物理内存没什么问题,写入物理内存就要注意了,可以通过pte.pat实现

总结:

非常简单的手动映射物理内存读写

其实类似的代码在各个开源的VT上多少有出现过

UC上的人还在用MmCopy…

GPT优化版

#define PAGE_SIZE 4096

typedef struct _PTE_64 {
    ULONG64 PageFrameNumber;
} PTE_64;

ULONG_PTR Mapper = 0;
PTE_64* MapperPte = NULL;
ULONG_PTR MapperPteOrigPfn = 0;

VOID Init()
{
    // 分配 4KB 内存,并清零
    Mapper = ExAllocatePoolWithTag(NonPagedPool, PAGE_SIZE, 'MPCM');
    if (Mapper == 0) {
        // 处理内存分配失败的情况
        return;
    }

    // 获取 Mapper 对应的页表条目
    MapperPte = (PTE_64*)((ULONG_PTR)Mapper & ~(PAGE_SIZE - 1));
    MapperPteOrigPfn = MapperPte->PageFrameNumber;
}

VOID ReadPhysicalMemoryInPage(PVOID Buffer, ULONG_PTR Phys, SIZE_T Size)
{
    // 关闭中断
    ULONG_PTR oldIrql;
    _disable();

    // 修改页表条目,映射物理地址到 Mapper
    MapperPte->PageFrameNumber = Phys >> 12;
    __invlpg(Mapper);

    // 拷贝内存数据到目标缓冲区
    __movsb((PUCHAR)Buffer, (PUCHAR)Mapper + (Phys & (PAGE_SIZE - 1)), Size);

    // 恢复原始页表条目
    MapperPte->PageFrameNumber = MapperPteOrigPfn;

    // 恢复中断
    _enable();
}

文档信息

Search

    Table of Contents