该结构体简单来说用于三环的寄存器保存,存储于零环,由操作系统维护,每个线程都有自己的 _Ktrap_frame 结构体(ethread+0x108处)。

  详细信息可以查看这篇文章:

 2. _ETHREAD 结构体

  该结构体保存了和线程相关的信息,位于0环(其并非三环的 TEB,线程环境块)

  在_ETHREAD结构体第一个成员是 _KTHRAD,可以看出其大小0x200,里面保存了线程中的一些信息  

  kd > dt _ethread
            ntdll!_ETHREAD
            + 0x000 Tcb              : _KTHREAD
            + 0x200 CreateTime : _LARGE_INTEGER
          ····

        kd > dt _kthread
            ntdll!_KTHREAD
            + 0x000 Header           : _DISPATCHER_HEADER
            ···
 +0x128 TrapFrame        : Ptr32 _KTRAP_FRAME 

3. KPCR结构体(kernel processor control region 内核线程控制区)

  有一篇文章,里面大体介绍了该结构体 --> [Windows内核分析]KPCR结构体介绍 (CPU控制区 Processor Control Region)

  简单来说,KPCR结构体中保存着 关于CPU 的信息,一个核有一个自己私有的KPCR结构体,八核则每个核有自己的单独的KPCR结构体

  我们需要用到最后一个成员中的中的CurrentThread来获取当前线程的_KTHREAD结构体。

   kd > dt _kpcr
            ntdll!_KPCR
            + 0x000 NtTib            : _NT_TIB
            + 0x000 Used_ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD
            ····
            +0x120 PrcbData         : _KPRCB

         kd > dt _KPRCB
            ntdll!_KPRCB
            + 0x000 MinorVersion     : Uint2B
            + 0x002 MajorVersion : Uint2B
            + 0x004 CurrentThread : Ptr32 _KTHREAD
            + 0x008 NextThread : Ptr32 _KTHREAD
            ···

 

二、分析 nt!KiSystemService 内核函数

  该函数位于ntkrnlpa.exe真正的内核中(并非ntdll.dll),一开始希望使用IDA反汇编查看其代码,但是无法显示。

  我们只能采取"曲线救国"的方法,通过int 2eh中断号查找GDT表,从表中获取中断描述符,将中断描述符拼接拆分,获取 函数的EIP 地址。

  之后,我们使用 Windbg 的U指令,来查看该其该函数在内存中的反汇编代码

 

  反汇编代码可能有点长,我们在正式分析之前需要明确几个注意事项。

  0)这些反汇编代码的目的就是将 _Ktrap_frame 结构体中的寄存器填满,然后将[KTHREAD+0x128]处旧的TRAPFRAME切换为这个新的KTRAPFRAME然后调用内核函数。

  1)函数开始时ESP位于_Ktrap_framec+0x070 ErrCode处。ErroCode这里没有,因此可以看待汇编代码中第一行使用 push 0。

  2)FS寄存器在0环时是指向自己的KPCR,而在3环时保存的是该线程的TEB结构体。(R3可以查看:利用C++实现模块隐藏)

    因此在反汇编代码 83e8cff6 、83e8cffb 是手动将FS寄存器存储KPCR。

    原理是以30h作为段选择子查找GDT这张表,找出段描述符,然后加载到fs段寄存器中。

    

  3) 0x83e8d007处 KPCR:[124] 为当前线程的ETHREAD结构体(看一中的表,KTHREAD也即ETHREAD的头部),将其存储到寄存器esi中,之后要经常使用。

  4)0x83e8d026处修改esp,将其指向 _Ktrap_frame 结构体第一个成员,0x83e8d036 处也将ebp修改为 _Ktrap_frame 结构体第一个成员。

  5)因为 esi 指向ETHREAD, [esi+128h] 则表示当前线程的_Ktrap_frame,在进入内核时,其保存了一个旧的frame。

    0x83e8d038将获取旧的_Ktrap_frame

    0x83e8d03e将旧的Frame放入新的Frame的edx寄存器中

    0x83e8d049将新的Frame挂靠在ETHREAD+0x128处,这样完成了新旧替换。

    下图三个箭头依次对应上面三个阶段,这样通过FS寄存器就可以查找到该FRAME了,回去只需要找到这里完成替换即可。

    

  6)Frame前部分成员和调试有关,因此0x83e8d045这里需要判断是否在调试状态,如果处于调试状态,则jmp进一个地址,将寄存器入栈再回来。

    硬件调试,其实就是通过这个部分。对于反调试,这里可以做些文章...

  7)还有部分涉及权限切换,0x83e8d030 比如有些API可以让3环也可以让0环权限调用,但有的不让(看保护模式调用门等知识),此时就会进行位运算判断"先前模式"的权限级别。

  8)当这些工作全部完成之后,会执行 83e8d06d e9dd000000      jmp     nt!KiFastCallEntry+0x8f (83e8d14f)

    对,你没看错,就是快速调用时的函数,相当于中断多走了一步,处理了上面的一些信息,而快速调用提前就可以处理好直接调用   nt!KiFastCallEntry+0x8f函数。

 

三、nt!KiSystemService函数的反汇编解读

复制代码
 1 nt!KiSystemService:  2