查看原文
其他

简单看一下 微软新出的内核页表隔离补丁

2018-01-09 hzqst 看雪学院


文 | hzqst

看雪论坛



相比以往的内核多了一套用户层专用的页表,里面没有内核内存的映射(除了几个r3进r0的入口)


KiEnableKvaShadowing由KiInitializeBootStructures和KxInitializeProcessorState调用,应该只初始化一次


signed __int64 __fastcall KiEnableKvaShadowing(__int64 a1, __int64 a2)

{

  __int64 v3; // rdx@3

  __int64 v4; // rcx@3

  __int64 v5; // r8@3

  char v6; // al@4

  signed __int64 v7; // r9@8

  signed __int64 v8; // r10@8

  signed __int64 v9; // rdx@8

  signed __int64 v10; // rcx@10

  unsigned __int64 v15; // rax@14

  __int64 v16; // rdx@15

  signed __int64 result; // rax@21

 

  _RBX = a1;

  if ( !(unsigned __int8)KiIsKvaShadowDisabled() )

  {

    if ( (unsigned __int8)KiIsKvaLeakSimulated() )

    {

      v6 = 1;

      KiKvaLeakageSimulate = 1;

    }

    else

    {

      v6 = KiKvaLeakageSimulate;

    }

    if ( !KiKvaLeakage && !v6 )

      return 1i64;

    *(_QWORD *)(v4 + 28288) = __readcr3();

    v7 = v3 + 4132;

    v8 = 7i64;

    *(_QWORD *)(v3 + 4216) = *(_QWORD *)(v3 + 4100);

    *(_QWORD *)(v3 + 4100) = v3 + 16896;

    v9 = v3 + 17376;

    do

    {

      if ( *(_QWORD *)v7 )

      {

        v10 = *(_QWORD *)v7 - 32i64;

        *(_QWORD *)v9 = _RBX - 384;

        *(_QWORD *)(v9 + 8) = v10;

        *(_QWORD *)v10 = v9;

        *(_QWORD *)v7 = v9;

      }

      v9 += 512i64;

      v7 += 8i64;

      --v8;

    }

    while ( v8 );

    if ( *(_DWORD *)(_RBX + 36) )

    {

      result = KiShadowProcessorAllocation(_RBX, v5);

      if ( !(_DWORD)result )

        return result;

      *(_DWORD *)(_RBX + 28312) |= 2u;

      goto LABEL_23;

    }

    KiInitializeIdt(v5, 1);

    *(_BYTE *)(*(_QWORD *)(*MK_FP(__GS__, 392i64) + 184i64) + 703i64) = 1;

    byte_1403BFBFF = 1;

    KiSetAddressPolicy(_CF, _ZF, _SF, _OF, 1);

    if ( *(_QWORD *)(_RBX + 25192) & 0x40000000000i64 )

    {

      v15 = __readcr4() & 0xFFFFFFFFFFFFFF7Fui64;

      _bittestandset(&v15, 0x11u);

      __writecr4(v15);

      __writecr3(__readcr3() | 2);

      KiFlushPcid = 1;

    }

    HvlRescindEnlightenments(0x40000000000i64, -129i64);

    KiKvaShadow = 1;

    if ( KiFlushPcid )

    {

      if ( *(_BYTE *)(_RBX + 1597) != 1 )

      {

LABEL_20:

        KiKvaShadowMode = 1;

LABEL_23:

        if ( KiFlushPcid )

          __asm { lock bts qword ptr [rbx+6E80h], 3Fh }

        return 1i64;

      }

    }

    else if ( *(_BYTE *)(_RBX + 1597) != 1 )

    {

      KiKvaShadowMode = 2;

      return 1i64;

    }

    __writecr4(v16 & __readcr4());

    goto LABEL_20;

  }

  KiIsKvaShadowConfigDisabled = 1;

  return 1i64;

}


关键代码


signed __int64 __fastcall KiShadowProcessorAllocation(__int64 a1, __int64 a2)

{

  __int64 v2; // rsi@1

  __int64 v3; // rdi@1

  signed int v5; // ebx@4

  __int64 v6; // rax@6

  __int64 v7; // rax@6

  unsigned int v8; // edx@6

 

  v2 = a2;

  v3 = a1;

  if ( !KiKvaShadow )

    return 1i64;

  if ( MmCreateShadowMapping(a2, 20480i64) )

  {

    v5 = 0;

    if ( MmCreateShadowMapping(v3 + 28288, 4096i64) )

    {

      v5 = 1;

      if ( *(_DWORD *)(v3 + 36) )

        return 1i64;

      LODWORD(v6) = RtlImageNtHeader(0x140000000i64);

      LODWORD(v7) = RtlSectionTableFromVirtualAddress(

                      v6,

                      0x140000000i64,

                      (unsigned int)KiDivideErrorFaultShadow - 0x40000000);

      v8 = *(_DWORD *)(v7 + 16);

      if ( *(_DWORD *)(v7 + 8) > v8 )

        v8 = *(_DWORD *)(v7 + 8);

      if ( MmCreateShadowMapping(*(_DWORD *)(v7 + 12) + 0x140000000i64, (v8 + 4095) & 0xFFFFF000) )

        return 1i64;

    }

    MmDeleteShadowMapping(v2, 20480i64);

    if ( v5 )

      MmDeleteShadowMapping(v3 + 28288, 4096i64);

  }

  return 0i64;

}


似乎是把 KiDivideErrorFaultShadow 所在的section这几块内核内存单独拿出来创建了映射。


刚好这一块都是idt和 syscall/sysenter 的入口,在KVASCODE 这个section里



当然IDT要用shadow的那份

KiInitializeIdt(v5, TRUE);


第二个参数应该就是是否使用shadow idt


然后根据cpu是否有flushpcid功能(KiFlushPcid)选择shadow的方式 KiKvaShadowMode = 1 or 2?


然后看一下 sysenter 的入口,内核页表应该是被放在了 gs:7000h 里面,太简单了不做分析,jmp后的流程和以前老 Systemcall 是一样的



随便挑了个中断分析了下



然后是 AttachProcess 的时候也加了一些特技(以前就一句writecr3)



void __fastcall KiLoadDirectoryTableBase(PEPROCESS ProcessObj, unsigned __int64 DirTableBase)

{

  unsigned __int64 NewCr3; // rbx@1

  unsigned __int64 v3; // rax@2

  bool v4; // zf@2

  bool v5; // sf@2

  unsigned __int64 v6; // rcx@10

  unsigned __int64 v7; // rax@11

 

  NewCr3 = DirTableBase;

  if ( KiKvaShadow )

  {

    v3 = DirTableBase;

    v4 = (DirTableBase & 2) == 0;

    v5 = (DirTableBase & 2 & 0x80u) != 0i64;

    if ( DirTableBase & 2 )

    {

      v3 = DirTableBase | 0x8000000000000000ui64;

      v4 = (DirTableBase | 0x8000000000000000ui64) == 0;

      v5 = ((DirTableBase | 0x8000000000000000ui64) & 0x8000000000000000ui64) != 0i64;

    }

    *MK_FP(__GS__, 28672i64) = v3;

    KiSetAddressPolicy(0, v4, v5, 0, ProcessObj->Pcb.AddressPolicy);

  }

  if ( HvlEnlightenments & 1 )

    HvlSwitchVirtualAddressSpace(NewCr3);

  else

    __writecr3(NewCr3);

  if ( KiKvaShadow && !KiFlushPcid )

  {

    v6 = __readcr4();

    if ( v6 & 0x20080 )

    {

      v7 = v6;

      _bittestandcomplement(&v7, 7u);

      __writecr4(v7);

       49 30703 49 15231 0 0 3733 0 0:00:08 0:00:04 0:00:04 3734__writecr4(v6);

    }

    else

    {

      __writecr3(__readcr3());

    }

  }

}


页表还是 EPROCESS 里那个页表,只不过看起来是把 KvaShadow 暂时禁用了(不然一切换cr3  内核地址空间全没了 直接炸穿)


Detach 的时候也是直接调用的 KiLoadDriectoryTableBase,被内联了



暂时就先看这么多,如果还有新的欢迎补充


原文链接:https://bbs.pediy.com/thread-223805.htm



更多详情可参考看雪论坛专题帖:

详情戳左下角“阅读原文”


热门阅读


点击阅读原文/read,

更多干货等着你~


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存