逆向TesSafe.sys有感:鹅厂是如何定位随机化的PTE_BASE
2019-09-06 14:14:10 Author: bbs.pediy.com(查看原文) 阅读量:267 收藏

[原创]逆向TesSafe.sys有感:鹅厂是如何定位随机化的PTE_BASE

2天前 1102

[原创]逆向TesSafe.sys有感:鹅厂是如何定位随机化的PTE_BASE

众所周知windows 10 14316开始实现了页表随机化,之前WRK里给出的#define PTE_BASE 0xFFFFF68000000000h 不再有效

那么各路神仙是怎么定位新的PTE_BASE的呢?

著名国际友人tandasat是通过特征码定位的:

  // Get PTE_BASE from MmGetVirtualForPhysical
  const auto p_MmGetVirtualForPhysical =
      UtilGetSystemProcAddress(L"MmGetVirtualForPhysical");
  if (!p_MmGetVirtualForPhysical) {
    return STATUS_PROCEDURE_NOT_FOUND;
  }

  static const UCHAR kPatternWin10x64[] = {
      0x48, 0x8b, 0x04, 0xd0,  // mov     rax, [rax+rdx*8]
      0x48, 0xc1, 0xe0, 0x19,  // shl     rax, 19h
      0x48, 0xba,              // mov     rdx, ????????`????????  ; PTE_BASE
  };
  auto found = reinterpret_cast<ULONG_PTR>(
      UtilMemMem(p_MmGetVirtualForPhysical, 0x30, kPatternWin10x64,
                 sizeof(kPatternWin10x64)));
  if (!found) {
    return STATUS_PROCEDURE_NOT_FOUND;
  }

  found += sizeof(kPatternWin10x64);
  HYPERPLATFORM_LOG_DEBUG("Found a hard coded PTE_BASE at %016Ix", found);

  const auto pte_base = *reinterpret_cast<ULONG_PTR *>(found);
  const auto index = (pte_base >> kUtilpPxiShift) & kUtilpPxiMask;
  const auto pde_base = pte_base | (index << kUtilpPpiShift);
  const auto ppe_base = pde_base | (index << kUtilpPdiShift);
  const auto pxe_base = ppe_base | (index << kUtilpPtiShift);

  g_utilp_pxe_base = static_cast<ULONG_PTR>(pxe_base);
  g_utilp_ppe_base = static_cast<ULONG_PTR>(ppe_base);
  g_utilp_pde_base = static_cast<ULONG_PTR>(pde_base);
  g_utilp_pte_base = static_cast<ULONG_PTR>(pte_base);

  g_utilp_pxi_shift = kUtilpPxiShift;
  g_utilp_ppi_shift = kUtilpPpiShift;
  g_utilp_pdi_shift = kUtilpPdiShift;
  g_utilp_pti_shift = kUtilpPtiShift;

  g_utilp_pxi_mask = kUtilpPxiMask;
  g_utilp_ppi_mask = kUtilpPpiMask;
  g_utilp_pdi_mask = kUtilpPdiMask;
  g_utilp_pti_mask = kUtilpPtiMask;
  return status;

那么鹅厂是如何定位的呢?

国际惯例直接上伪代码

char __fastcall InitializePteBase(char a1)
{
  char v1; // bl
  PHYSICAL_ADDRESS pml4t; // rdi
  __int64 *pml4t_va; // r11
  int slot; // edx
  __int64 index; // rcx
  __int64 v6; // r8

  v1 = 0;
  if ( a1 )
  {
    pml4t.QuadPart = __readcr3();
    pml4t_va = (__int64 *)MmMapIoSpace(pml4t, 0x1000ui64, MmCached);
    if ( pml4t_va )
    {
      slot = 0;
      index = 0i64;
      while ( (pml4t_va[index] & 0xFFFFFFFFF000i64) != pml4t.QuadPart )
      {
        ++index;
        ++slot;
        if ( index >= 512 )
          goto LABEL_8;
      }
      v1 = 1;
      v6 = (slot + 0x1FFFE00i64) << 39;
      g_pte_base = (slot + 0x1FFFE00i64) << 39;
      g_pxe_selfmapping_index = slot;
      g_pde_base = v6 + ((__int64)slot << 30);
      g_ppe_base = v6 + ((__int64)slot << 30) + ((__int64)slot << 21);
      g_pxe_base = (void *)(g_ppe_base + ((__int64)slot << 12));
      g_pxe_end = (__int64)g_pxe_base + 4096;
      g_pte_end = v6 + 0x8000000000i64;
LABEL_8:
      MmUnmapIoSpace(pml4t_va, 0x1000ui64);
    }
  }
  else
  {
    g_pxe_selfmapping_index = 493i64;
    v1 = 1;
    g_pte_base = 0xFFFFF68000000000i64;
    g_pde_base = 0xFFFFF6FB40000000i64;
    g_ppe_base = 0xFFFFF6FB7DA00000i64;
    g_pxe_base = (void *)0xFFFFF6FB7DBED000i64;
    g_pxe_end = 0xFFFFF6FB7DBEE000i64;
    g_pte_end = 0xFFFFF70000000000i64;
  }
  return v1;
}

先以当前cr3为物理地址映射成虚拟地址,相当于访问cr3所指向的pml4t表

然后在这个[512]的表(数组)中找到一项指向当前cr3的

也就是所谓的页表自映射,这一项PML4E所指向的物理页page_frame_number代表它自己这个表

满足这样的映射关系的PML4E所代表的虚拟地址空间也就相当于页表本身,一共0x8000000000/8 (512/8=64GB)个PTE

我们先从无随机化的情况论证:

大家都知道x64的线性地址是分成9① 9② 9③ 9④ 12⑤ 五部分进行地址翻译的:①代表pml4e索引,②代表pdpte索引,③代表pde索引,④代表pte索引,⑤代表页内偏移

无随机化的情况下第[493]项pml4e指向cr3,第[493]项pml4e是111101101(9位),左移39位让其变为第39~47位代表pml4e索引并补齐48~63位FFFF000000000000(保留位)

就是这样

刚好就是PTE_BASE

那么只要找到 指向当前cr3的pml4e index,将其左移39位并补上FFFF000000000000就得到了PTE_BASE

也就是代码中的 g_pte_base = (slot + 0x1FFFE00i64) << 39;(注意) 0x1FFFE00i64左移39位就是 FFFF000000000000

计算PDE_BASE也只需要将这个能够自映射的index左移30位让其变为30~38位代表pdpte索引,再加上PTE_BASE就等于PDE_BASE了

也就是

g_pde_base = g_pte_base + ((__int64)slot << 30);

PPE_BASE和PXE_BASE同理

注意有一点:win10 1803之后MmMapIoSpace不再允许映射页表相关的物理地址,因此只能用MmGetVirtualForPhysical代替,或者自己实现映射物理地址(方法很多)

[公告]看雪.纽盾 KCTF 2019晋级赛Q3攻击方规则,9月10日开赛,华为P30 Pro、iPad、kindle等你来拿!

最后于 2天前 被hzqst编辑 ,原因:


文章来源: https://bbs.pediy.com/thread-254276.htm
如有侵权请联系:admin#unsafe.sh