首页
社区
课程
招聘
[原创]逆向TesSafe.sys有感:鹅厂是如何定位随机化的PTE_BASE
发表于: 2019-9-4 12:02 25383

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

2019-9-4 12:02
25383
众所周知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代替,或者自己实现映射物理地址(方法很多)

  // 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代替,或者自己实现映射物理地址(方法很多)
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

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

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2019-9-4 12:02 被hzqst编辑 ,原因:
收藏
免费 19
支持
分享
打赏 + 5.00雪花
打赏次数 1 雪花 + 5.00
 
赞赏  毕达哥拉斯   +5.00 2019/09/05
最新回复 (47)
雪    币: 1641
活跃值: (3601)
能力值: (RANK:15 )
在线值:
发帖
回帖
粉丝
2
牛批嗷表哥
2019-9-4 12:04
0
雪    币: 9626
活跃值: (1838)
能力值: ( LV5,RANK:73 )
在线值:
发帖
回帖
粉丝
3
表哥牛批嗷
2019-9-4 12:05
0
雪    币: 711
活跃值: (253)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
继续安排他,表哥牛逼
2019-9-4 12:07
0
雪    币: 914
活跃值: (2468)
能力值: ( LV5,RANK:68 )
在线值:
发帖
回帖
粉丝
5
沙发
2019-9-4 12:14
0
雪    币: 12363
活跃值: (5884)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
6
如果从MiIsAddressValid搜也能搜到PTE_BASE吧?还有就是内核是在加载后修改MiIsAddressValid的立即数还是加载前就改好了呢?
2019-9-4 12:32
0
雪    币: 665
活跃值: (1051)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
2019-9-4 13:46
0
雪    币: 4709
活跃值: (1575)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
9
还得安排他,大表哥流批
2019-9-4 13:46
1
雪    币: 1564
活跃值: (3572)
能力值: ( LV13,RANK:420 )
在线值:
发帖
回帖
粉丝
10
试了下,可以用,牛批



最后于 2019-9-4 15:24 被xiaofu编辑 ,原因:
2019-9-4 13:53
0
雪    币: 46
活跃值: (1740)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
hhkqqs 如果从MiIsAddressValid搜也能搜到PTE_BASE吧?还有就是内核是在加载后修改MiIsAddressValid的立即数还是加载前就改好了呢?
我也表示你的好奇
2019-9-4 14:14
0
雪    币: 1108
活跃值: (3896)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
12
表哥牛批嗷
2019-9-4 15:26
0
雪    币: 12848
活跃值: (9147)
能力值: ( LV9,RANK:280 )
在线值:
发帖
回帖
粉丝
13
hhkqqs 如果从MiIsAddressValid搜也能搜到PTE_BASE吧?还有就是内核是在加载后修改MiIsAddressValid的立即数还是加载前就改好了呢?
特征码搜索的话万一微软的咖喱味程序员改一改代码 说不定就搜不到了
2019-9-4 15:58
0
雪    币: 262
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
厉害啊...
2019-9-4 16:58
0
雪    币: 12363
活跃值: (5884)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
15
hzqst 特征码搜索的话万一微软的咖喱味程序员改一改代码 说不定就搜不到了
不用硬编码确实是最好的选择,但是很多时候避免不了,不然那些驱动也不会每次出新系统就更新一次了
2019-9-4 17:03
0
雪    币: 2345
活跃值: (3064)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
16
大神啊
2019-9-4 17:38
0
雪    币: 222
活跃值: (185)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
17
表哥出手 鹅厂失眠
2019-9-4 18:08
0
雪    币: 5836
活跃值: (1918)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
18
表哥牛逼
2019-9-4 18:32
0
雪    币: 495
活跃值: (147)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
19
表哥牛逼   1803真蛋疼 好多神器都不能用了
最后于 2019-9-4 22:34 被养乐多A编辑 ,原因:
2019-9-4 22:32
0
雪    币: 3738
活跃值: (3872)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
20
其实不管PXE_BASE是多少,只需要按照9-9-9-9-12划分,高位的每9bit的值一样,满足这样要求的值就可以做PXE_BASE。PPE_BASE和PDE_BASE以此类推。
最后于 2019-9-4 23:06 被fengyunabc编辑 ,原因: 打错字目
2019-9-4 22:59
2
雪    币: 1176
活跃值: (1264)
能力值: ( LV12,RANK:380 )
在线值:
发帖
回帖
粉丝
21
支持支持
2019-9-5 00:06
0
雪    币: 890
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
不是一个导出函数直接可以获取么 KeCapturePersistentThreadState
2019-9-5 07:59
1
雪    币: 2065
活跃值: (500)
能力值: (RANK:10 )
在线值:
发帖
回帖
粉丝
23
牛批嗷表哥
2019-9-6 14:12
0
雪    币: 6124
活跃值: (4661)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
24
牛批牛批
2019-9-19 04:20
0
雪    币: 1686
活跃值: (183)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
安排上了
2019-9-19 09:07
0
游客
登录 | 注册 方可回帖
返回
//