• 1720阅读
  • 2回复

Blue Pill源代码分析(3) [复制链接]

上一主题 下一主题
离线天道酬勤
 

只看楼主 倒序阅读 使用道具 楼主  发表于: 2016-02-02
— 本帖被 天道酬勤 从 驱动保护 移动到本区(2016-05-19) —
首先看一下IA32页表映射关系如图

函数MmSavePage分配如下结构体保存物理地址、宿主机虚拟地址和客户机虚拟地址的对应关系
typedef struct _ALLOCATED_PAGE
{

LIST_ENTRY le;  //链表头 链接到g_PageTableList

ULONG Flags;  //标志

PAGE_ALLOCATION_TYPE AllocationType; //分配类型
ULONG uNumberOfPages;         // for PAT_CONTIGUOUS only 分配内存页数

PHYSICAL_ADDRESS PhysicalAddress; //物理地址
PVOID HostAddress; //宿主机虚拟地址
PVOID GuestAddress; //客户机虚拟地址

} ALLOCATED_PAGE,  *PALLOCATED_PAGE;

//更新页表,创建物理地址PhysicalAddress和宿主机虚拟地址VirtualAddress的映射关系
NTSTATUS NTAPI MmCreateMapping (
  PHYSICAL_ADDRESS PhysicalAddress,
  PVOID VirtualAddress,
  BOOLEAN bLargePage
)
{
  PALLOCATED_PAGE Pml4Page;
  NTSTATUS Status;

  //通过第四级页表的物理地址,查找分配结构ALLOCATED_PAGE
  Status = MmFindPageByPA (g_PageMapBasePhysicalAddress, &Pml4Page);//根据物理地址查找所属的分配结构体
  if (!NT_SUCCESS (Status)) {
    return STATUS_UNSUCCESSFUL;
  }

  //页对齐
  PhysicalAddress.QuadPart = PhysicalAddress.QuadPart & 0x000ffffffffff000;
  VirtualAddress = (PVOID) ((ULONG64) VirtualAddress & 0xfffffffffffff000);

  //从第四级页表开始更新
  return MmUpdatePageTable (Pml4Page->GuestAddress, 4, VirtualAddress, PhysicalAddress, bLargePage);
}


//PageTable是客户机虚拟地址,且内存页已经存在
//PageTableLevel页级  1-4
//VirtualAddress宿主机虚拟地址
//PhysicalAddress物理地址
//bLargePage
//首先判断是不是已经更新页表到了最后一级页表,一般是第一级PTE,对于Large Page是第二级PDE,是就更新页表项并返回
//否则获取通过当前页表PageTable获取下一级页表的物理地址LowerPageTablePA,通过自映射关系获取宿主机虚拟地址LowerPageTableHostVA
//如果LowerPageTablePA为NULL,页表项为空,则通过宿主机虚拟地址查找分配结构ALLOCATED_PAGE,此时若没找到,说明还没分配该页表,分配内存并初始化
// 初始化页表项,调用MmCreateMapping更新映射关系
//LowerPageTablePA不为NULL,则通过物理地址查找分配结构ALLOCATED_PAGE
//最后递归更新下一级页表
static NTSTATUS NTAPI MmUpdatePageTable (
  PVOID PageTable,
  UCHAR PageTableLevel,
  PVOID VirtualAddress,
  PHYSICAL_ADDRESS PhysicalAddress,
  BOOLEAN bLargePage
)
{
  ULONG64 PageTableOffset, GlobalOffset;
  ULONG64 GlobalOffset1, GlobalOffset2, GlobalOffset3, GlobalOffset4;
  PVOID LowerPageTableHostVA, LowerPageTableGuestVA;
  PALLOCATED_PAGE LowerPageTable;
  PHYSICAL_ADDRESS LowerPageTablePA;
  NTSTATUS Status;
  PHYSICAL_ADDRESS PagePA, l1, l2, l3;

  PALLOCATED_PAGE Pml4e, Pdpe, Pde, Pte;
  // get the offset in the specified page table level
  // 通过虚拟地址VirtualAddress计算偏移页表项在第PageTableLevel级页表的偏移
  PageTableOffset = (((ULONG64) VirtualAddress & (((ULONG64) 1) << (12 + PageTableLevel * 9))
                      - 1) >> (12 + ((ULONG64) PageTableLevel - 1) * 9));

  if ((PageTableLevel == 1) || (bLargePage && (PageTableLevel == 2))) {
//递归返回条件,到达第一级页表或者第二级页表且为大页模式
   //设置页表项,每个页表项占8个字节,低12位或上页属性
#ifdef SET_PCD_BIT
    ((PULONG64) PageTable)[PageTableOffset] = PhysicalAddress.QuadPart |        /*P_GLOBAL | */
      P_WRITABLE | P_PRESENT | P_CACHE_DISABLED;
#else
    ((PULONG64) PageTable)[PageTableOffset] = PhysicalAddress.QuadPart | /*P_GLOBAL | */ P_WRITABLE | P_PRESENT;
#endif
    if (bLargePage)
      ((PULONG64) PageTable)[PageTableOffset] |= P_LARGE;//大页模式
    return STATUS_SUCCESS;
  }

  //计算下一级页表偏移,因为自映射是连续的,所以这里的GlobalOffset计算使用的是全偏移从第4级开始算,不是PageTableLevel
  GlobalOffset =
    (((ULONG64) VirtualAddress & (((ULONG64) 1) << (12 + 4 * 9)) - 1) >> (12 + ((ULONG64) PageTableLevel - 2) * 9));
  LowerPageTablePA.QuadPart = ((PULONG64) PageTable)[PageTableOffset] & 0x000ffffffffff000;//物理地址
  LowerPageTableHostVA = GlobalOffset * 8 + g_PageTableBases[PageTableLevel - 2];//宿主机虚拟地址
//static PUCHAR g_PageTableBases[4] = {
//    (PUCHAR) PT_BASE,
//   (PUCHAR) PD_BASE,
//    (PUCHAR) PDP_BASE,
//    (PUCHAR) PML4_BASE
// };

  if (!LowerPageTablePA.QuadPart) {  
//下一级页表物理地址不在当前级页表项中
//通过宿主机虚拟地址查找,该内存页是否已经在内存中,这可能是因为其它页映射的时候已经分配好了
    Status = MmFindPageByHostVA (LowerPageTableHostVA, &LowerPageTable);  
    if (!NT_SUCCESS (Status)) {  //没有找到该内存页,页表还么有分配

      LowerPageTableGuestVA = ExAllocatePoolWithTag (NonPagedPool, PAGE_SIZE, ITL_TAG);//分配
      if (!LowerPageTableGuestVA)
        return STATUS_INSUFFICIENT_RESOURCES;
      RtlZeroMemory (LowerPageTableGuestVA, PAGE_SIZE);

      LowerPageTablePA = MmGetPhysicalAddress (LowerPageTableGuestVA);//获取物理地址
   //保存该内存页的映射关系,物理地址/宿主机虚拟地址/客户机虚拟地址
      Status =
        MmSavePage (LowerPageTablePA, LowerPageTableHostVA,
                    LowerPageTableGuestVA, PAT_POOL, 1, AP_PAGETABLE | (1 << (PageTableLevel - 1)));
      if (!NT_SUCCESS (Status)) {
        DbgPrint
          ("MmUpdatePageTable(): Failed to store page table level %d, MmSavePage() returned status 0x%08X/n",
           PageTableLevel - 1, Status);
        return Status;
      }
    } else {  //内存页已经存在
      LowerPageTablePA.QuadPart = LowerPageTable->PhysicalAddress.QuadPart; //物理地址
      LowerPageTableGuestVA = LowerPageTable->GuestAddress; //客户机虚拟地址
    }

//设置该页表项
#ifdef SET_PCD_BIT
    ((PULONG64) PageTable)[PageTableOffset] = LowerPageTablePA.QuadPart |       /*P_GLOBAL | */
      P_WRITABLE | P_PRESENT | P_CACHE_DISABLED;
#else
    ((PULONG64) PageTable)[PageTableOffset] = LowerPageTablePA.QuadPart | /*P_GLOBAL | */ P_WRITABLE | P_PRESENT;
#endif

//建立虚拟地址LowerPageTableHostVA和物理地址LowerPageTablePA的映射关系,这导致本函数被间接递归调用
    Status = MmCreateMapping (LowerPageTablePA, LowerPageTableHostVA, FALSE);
    if (!NT_SUCCESS (Status)) {
      DbgPrint
        ("MmUpdatePageTable(): MmCreateMapping() failed to map PA 0x%p with status 0x%08X/n",
         LowerPageTablePA.QuadPart, Status);
      return Status;
    }

  } else {  //页表项存在物理地址
//通过物理地址LowerPageTablePA,查找该内存页
    Status = MmFindPageByPA (LowerPageTablePA, &LowerPageTable);
    if (!NT_SUCCESS (Status)) { //失败,
  //读取页表项,重新审查是否该内存页是大页模式
      LowerPageTablePA.QuadPart = ((PULONG64) PageTable)[PageTableOffset];
      if ((PageTableLevel == 2) && (LowerPageTablePA.QuadPart & P_LARGE)) {  
//是大页模式,这可能是已经映射,但是重新映射的时候没有指定大页模式
        DbgPrint ("MmUpdatePageTable(): Found large PDE, data 0x%p/n", LowerPageTablePA.QuadPart);
        return STATUS_SUCCESS;

      } else {  //否则 错误
        DbgPrint
          ("MmUpdatePageTable(): Failed to find lower page table (pl%d) guest VA, data 0x%p, status 0x%08X/n",
           PageTableLevel - 1, LowerPageTablePA.QuadPart, Status);
        return Status;
      }
    }
//下一级页表客户机虚拟地址
    LowerPageTableGuestVA = LowerPageTable->GuestAddress;
  }

  //递归更新下一级页表
  return MmUpdatePageTable (LowerPageTableGuestVA, PageTableLevel - 1, VirtualAddress, PhysicalAddress, bLargePage);
}


函数MmInitIdentityPageTable建立两个恒等映射页表在客户机关闭分页机制的时候使用。g_IdentityPageTableBasePhysicalAddress在64GB虚拟地址空间使用,

g_IdentityPageTableBasePhysicalAddress_Legacy在4GB虚拟地址空间使用。

//映射指定的客户机虚拟内存块,映射到宿主机相同的虚拟地址
NTSTATUS NTAPI MmMapGuestPages (
  PVOID FirstPage, //内存块首地址
  ULONG uNumberOfPages  //内存页数
);


//遍历客户机的指定Level的页表
NTSTATUS NTAPI MmWalkGuestPageTable (
  PULONG64 PageTable,  //客户机页表
  UCHAR bLevel
)
{
  ULONG64 i;
  PVOID VirtualAddress;
  PUCHAR ShortPageVA;
  PHYSICAL_ADDRESS PhysicalAddress;
  PULONG64 LowerPageTable; //低一级页表

  if (!MmIsAddressValid (PageTable))
    return STATUS_SUCCESS;

  for (i = 0; i < 0x200; i++)  //每一个页表有0x200=512项 页表项
   if (PageTable & P_PRESENT) { //该页存在
      if (((bLevel == 2) && (PageTable & P_LARGE)) || (bLevel == 1)) {//到了最低页表
    //根据自映射关系,计算虚拟地址
        if (bLevel == 1)
          VirtualAddress = (PVOID) (((LONGLONG) (&PageTable) - PT_BASE) << 9);
        else
          VirtualAddress = (PVOID) (((LONGLONG) (&PageTable) - PD_BASE) << 18);

  //高16位符号位扩展
        if ((LONGLONG) VirtualAddress & 0x0000800000000000)
          VirtualAddress = (PVOID) ((LONGLONG) VirtualAddress | 0xffff000000000000);

        PhysicalAddress.QuadPart = PageTable & 0x000ffffffffff000;//物理地址
        if ((ULONGLONG) VirtualAddress >= PT_BASE && (ULONGLONG) VirtualAddress < PML4_BASE + 0x1000)
          // guest pagetable stuff here - so don't map it
          continue; //客户机页表自映射,宿主机不映射

        if (bLevel == 2) { //Large Page分开映射成标准4k页
          for (ShortPageVA = (PUCHAR) VirtualAddress + 0x0 * PAGE_SIZE;
               ShortPageVA < (PUCHAR) VirtualAddress + 0x200 * PAGE_SIZE;
               ShortPageVA += PAGE_SIZE, PhysicalAddress.QuadPart += PAGE_SIZE)
            MmCreateMapping (PhysicalAddress, ShortPageVA, FALSE);
        } else
          MmCreateMapping (PhysicalAddress, VirtualAddress, FALSE);
      }

      if ((bLevel > 1) && !((bLevel == 2) && (PageTable & P_LARGE))) {
        LowerPageTable = (PULONG64) (g_PageTableBases[bLevel - 2] + 8 * (i << (9 * (5 - bLevel))));
        MmWalkGuestPageTable (LowerPageTable, bLevel - 1); //遍历下一级页表
      }
    }

  return STATUS_SUCCESS;
}


//映射客户机核心态内存页
NTSTATUS NTAPI MmMapGuestKernelPages (
)
{
  PULONG64 Pml4e = (PULONG64) PML4_BASE;
  PULONG64 Pdpe;
  PULONG64 Pde;
  ULONG uPml4eIndex, uPdpeIndex, uPdeIndex;

  for (uPml4eIndex = 0x100; uPml4eIndex < 0x200; uPml4eIndex++) //只映射核心态内存,所用从第0x100开始,0-0xff为用户态内存
    if (Pml4e[uPml4eIndex] & P_PRESENT) {
//自映射 连续映射 所以可以通过上一级偏移量左移9位得到该级页表虚拟地址
      Pdpe = (PULONG64) PDP_BASE + (uPml4eIndex << 9);  
      MmWalkGuestPageTable (Pdpe, 3); //遍历第三级页表
    }

  return STATUS_SUCCESS;
}

理解了以上几个函数paging.c里面的其他函数就不难理解了。虚拟内存部分基本扫清了,待续.....
离线v2680267313

只看该作者 沙发  发表于: 2016-04-30
用户被禁言,该主题自动屏蔽!
离线a168747

只看该作者 板凳  发表于: 2017-06-21
ffffffffffffffffffffff
快速回复
限100 字节
如果您在写长篇帖子又不马上发表,建议存为草稿
 
上一个 下一个