Windows系统调用中的现场保存

 Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html

Windows系统调用中的现场保存

  我们之前介绍过三环进零环的步骤,通过中断或者快速调用来实现。

  但是我们是否考虑过CPU从三环进入零环时,其三环的寄存器该如何保存。

  这一篇文件就来介绍其系统调用中的(三环)现场保存的问题。

 

一、几个重要的结构体介绍

1. _Ktrap_frame

  Windows系统调用中的现场保存_第1张图片

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

  详细信息可以查看这篇文章:解析windows内核每日一讲 陷阱调度

  我们之前讲过进入0环时获取新的四个寄存器非常重要, SS\CS\EIP\ESP

  如下图,当进入零环时,操作系统会获取ESP值,该值指向_Ktrap_frame结构体,将旧的SS\CS\EIP\ESP\EFLAG依次压入(0x78-0x68处),之后ESP +0x070 ErrCode处。

  之后便进入获取的EIP来执行内核函数。

  Windows系统调用中的现场保存_第2张图片

 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段寄存器中。

    Windows系统调用中的现场保存_第3张图片

  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了,回去只需要找到这里完成替换即可。

    Windows系统调用中的现场保存_第4张图片

  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 // 压栈 按照 _Ktrap_frame 结构中寄存的值
 3 83e8cfee 6a00            push    0 // errorcode 填入零来进行对齐
 4 83e8cff0 55              push    ebp
 5 83e8cff1 53              push    ebx
 6 83e8cff2 56              push    esi
 7 83e8cff3 57              push    edi
 8 83e8cff4 0fa0            push    fs
 9 
10 // 根据30h这个值作为段选择子,查找gdt这张表,找出段描描述符,将其加载到fs段寄存器中
11 // 30h -> 0011 0000 -> index = 00110 | TI = 0 |00 -> TI=0查LDT表,查索引为6查出来的是 834093f7`ac003748 这个值。
12 // 834093f7`ac003748 段描述符,根据段描述符属性拼接起来的 83f7ac00
13 // 83f7ac00 指向的是 KPCR,FS在零环的时候,指向KPCR,不再是三环时线程TEB这个结构体
14 83e8cff6 bb30000000      mov     ebx,30h
15 83e8cffb 668ee3          mov     fs,bx
16 83e8cffe bb23000000      mov     ebx,23h
17 83e8d003 8edb            mov     ds,bx
18 83e8d005 8ec3            mov     es,bx
19 
20 
21 // 有些API 0环和三环都可以调用,通过下面操作可以查看原来调用的是0环还是3环,再进行调用API的权限验证 
22 // 验证完该线程,如果是0环,则bl为0;如果是3环,则bl=1
23 83e8d007 648b3524010000  mov     esi,dword ptr fs:[124h] // 将当前CPU正在跑的线程放到esi中
24 83e8d00e 64ff3500000000  push    dword ptr fs:[0] // 把老的EXCEPTION_LIST 存入 _Ktrap_frame 结构体中(fs上边那部分)
25 83e8d015 64c70500000000ffffffff mov dword ptr fs:[0],0FFFFFFFFh // 将现在EXCPTION_LIST放入异常链表
26 83e8d020 ffb63a010000    push    dword ptr [esi+13Ah] // 将老的"先前模式"也保存到栈中
27 83e8d026 83ec48          sub     esp,48h // 将当前ESP提升到 _Ktrap_frame 第一个成员
28 83e8d029 8b5c246c        mov     ebx,dword ptr [esp+6Ch] // 取出3环压入的参数CS _KTRAP_FRAME + 0x6c,指向三环原来的CS值
29 83e8d02d 83e301          and     ebx,1 // 将原来CS的值进行与运算, 0环最低为为0,3环最低为为1
30 83e8d030 889e3a010000    mov     byte ptr [esi+13Ah],bl // 新的"先前模式",原来是三环模式为1;原来是零环模式为0
31 83e8d036 8bec            mov     ebp,esp // ebp 同样指向_Ktrap_frame第一个成员
32 83e8d038 8b9e28010000    mov     ebx,dword ptr [esi+128h] // esi通过0x83e8d007处[KPCR+124],指向Ethread                              // Ethread+128h 指向的是 _KTRAP_FRAME这个结构体指针            
33 83e8d03e 895d3c          mov     dword ptr [ebp+3Ch],ebx  // 将该 _KTRAP_FRAME地址暂时保存在 edx 中                             
34 83e8d041 83652c00        and     dword ptr [ebp+2Ch],0
35 83e8d045 f64603df        test    byte ptr [esi+3],0DFh      // 判断是否属于调试状态 一个位 +0x003 DebugActive : UChar
36 83e8d049 89ae28010000    mov     dword ptr [esi+128h],ebp // 因为新的 trap_frame地址已经发生变化,所以要将新的trapFrame放到线程放到这个位置
37 
38 83e8d04f fc              cld
39 83e8d050 0f859afeffff    jne     nt!Dr_kss_a (83e8cef0)  // 在0x83e8d045处根据判断结果,如果是调试状态,则进行跳转。
40                              // 若为调试,则跳过去将调试相关的寄存器也存入_KTRAP_FRAME这个结构体
41                              // 否则不是调试器,则将中是-1。(硬件断点使用这个,反调试很管用)
42 83e8d056 8b5d60          mov     ebx,dword ptr [ebp+60h] // 3环的EBP
43 83e8d059 8b7d68          mov     edi,dword ptr [ebp+68h] // 3环的EIP
44 83e8d05c 89550c          mov     dword ptr [ebp+0Ch],edx // edx存放着三环参数的指针
45                              // {mov edx,esp;sysenter}                    
46 83e8d05f c74508000ddbba  mov     dword ptr [ebp+8],0BADB0D00h // 操作系统需要用的标志
47 83e8d066 895d00          mov     dword ptr [ebp],ebx     // 3环的ebp存储到DbgEbp的位置
48 83e8d069 897d04          mov     dword ptr [ebp+4],edi   // 3环的eip存放到 DbgEip
49 83e8d06c fb              sti     // 关闭中断
50 83e8d06d e9dd000000      jmp     nt!KiFastCallEntry+0x8f (83e8d14f) // 取出系统调用号,3环传进来的。
51                  // 注意,这个就是快速调用首先进入的函数,相当于中断多走了这一步,系统调用直接调用。

 

四、nt!KiFastCallEntry的反汇编代码

该反汇编代码阅读量同样很大,找时间一定给读出来,如果感兴趣可以自行百度相关解读。

  1 nt!KiFastCallEntry:
  2 83e8d0c0 b923000000      mov     ecx,23h
  3 83e8d0c5 6a30            push    30h
  4 83e8d0c7 0fa1            pop     fs
  5 83e8d0c9 8ed9            mov     ds,cx
  6 83e8d0cb 8ec1            mov     es,cx
  7 83e8d0cd 648b0d40000000  mov     ecx,dword ptr fs:[40h]
  8 83e8d0d4 8b6104          mov     esp,dword ptr [ecx+4]
  9 83e8d0d7 6a23            push    23h
 10 83e8d0d9 52              push    edx
 11 83e8d0da 9c              pushfd
 12 83e8d0db 6a02            push    2
 13 83e8d0dd 83c208          add     edx,8
 14 83e8d0e0 9d              popfd
 15 83e8d0e1 804c240102      or      byte ptr [esp+1],2
 16 83e8d0e6 6a1b            push    1Bh
 17 83e8d0e8 ff350403dfff    push    dword ptr ds:[0FFDF0304h]
 18 83e8d0ee 6a00            push    0
 19 83e8d0f0 55              push    ebp
 20 83e8d0f1 53              push    ebx
 21 83e8d0f2 56              push    esi
 22 83e8d0f3 57              push    edi
 23 83e8d0f4 648b1d1c000000  mov     ebx,dword ptr fs:[1Ch]
 24 83e8d0fb 6a3b            push    3Bh
 25 83e8d0fd 8bb324010000    mov     esi,dword ptr [ebx+124h]
 26 83e8d103 ff33            push    dword ptr [ebx]
 27 83e8d105 c703ffffffff    mov     dword ptr [ebx],0FFFFFFFFh
 28 83e8d10b 8b6e28          mov     ebp,dword ptr [esi+28h]
 29 83e8d10e 6a01            push    1
 30 83e8d110 83ec48          sub     esp,48h
 31 83e8d113 81ed9c020000    sub     ebp,29Ch
 32 83e8d119 c6863a01000001  mov     byte ptr [esi+13Ah],1
 33 83e8d120 3bec            cmp     ebp,esp
 34 83e8d122 7597            jne     nt!KiFastCallEntry2+0x49 (83e8d0bb)
 35 83e8d124 83652c00        and     dword ptr [ebp+2Ch],0
 36 83e8d128 f64603df        test    byte ptr [esi+3],0DFh
 37 83e8d12c 89ae28010000    mov     dword ptr [esi+128h],ebp
 38 83e8d132 0f8538feffff    jne     nt!Dr_FastCallDrSave (83e8cf70)
 39 83e8d138 8b5d60          mov     ebx,dword ptr [ebp+60h]
 40 83e8d13b 8b7d68          mov     edi,dword ptr [ebp+68h]
 41 83e8d13e 89550c          mov     dword ptr [ebp+0Ch],edx
 42 83e8d141 c74508000ddbba  mov     dword ptr [ebp+8],0BADB0D00h
 43 83e8d148 895d00          mov     dword ptr [ebp],ebx
 44 83e8d14b 897d04          mov     dword ptr [ebp+4],edi
 45 83e8d14e fb              sti
 46 83e8d14f 8bf8            mov     edi,eax
 47 83e8d151 c1ef08          shr     edi,8
 48 83e8d154 83e710          and     edi,10h
 49 83e8d157 8bcf            mov     ecx,edi
 50 83e8d159 03bebc000000    add     edi,dword ptr [esi+0BCh]
 51 83e8d15f 8bd8            mov     ebx,eax
 52 83e8d161 25ff0f0000      and     eax,0FFFh
 53 83e8d166 3b4708          cmp     eax,dword ptr [edi+8]
 54 83e8d169 0f8333fdffff    jae     nt!KiBBTUnexpectedRange (83e8cea2)
 55 83e8d16f 83f910          cmp     ecx,10h
 56 83e8d172 751a            jne     nt!KiFastCallEntry+0xce (83e8d18e)
 57 83e8d174 8b8e88000000    mov     ecx,dword ptr [esi+88h]
 58 83e8d17a 33f6            xor     esi,esi
 59 83e8d17c 0bb1700f0000    or      esi,dword ptr [ecx+0F70h]
 60 83e8d182 740a            je      nt!KiFastCallEntry+0xce (83e8d18e)
 61 83e8d184 52              push    edx
 62 83e8d185 50              push    eax
 63 83e8d186 ff154c99fb83    call    dword ptr [nt!KeGdiFlushUserBatch (83fb994c)]
 64 83e8d18c 58              pop     eax
 65 83e8d18d 5a              pop     edx
 66 83e8d18e 64ff05b0060000  inc     dword ptr fs:[6B0h]
 67 83e8d195 8bf2            mov     esi,edx
 68 83e8d197 33c9            xor     ecx,ecx
 69 83e8d199 8b570c          mov     edx,dword ptr [edi+0Ch]
 70 83e8d19c 8b3f            mov     edi,dword ptr [edi]
 71 83e8d19e 8a0c10          mov     cl,byte ptr [eax+edx]
 72 83e8d1a1 8b1487          mov     edx,dword ptr [edi+eax*4]
 73 83e8d1a4 2be1            sub     esp,ecx
 74 83e8d1a6 c1e902          shr     ecx,2
 75 83e8d1a9 8bfc            mov     edi,esp
 76 83e8d1ab 3b351c97fb83    cmp     esi,dword ptr [nt!MmUserProbeAddress (83fb971c)]
 77 83e8d1b1 0f832e020000    jae     nt!KiSystemCallExit2+0xa5 (83e8d3e5)
 78 83e8d1b7 f3a5            rep movs dword ptr es:[edi],dword ptr [esi]
 79 83e8d1b9 f6456c01        test    byte ptr [ebp+6Ch],1
 80 83e8d1bd 7416            je      nt!KiFastCallEntry+0x115 (83e8d1d5)
 81 83e8d1bf 648b0d24010000  mov     ecx,dword ptr fs:[124h]
 82 83e8d1c6 8b3c24          mov     edi,dword ptr [esp]
 83 83e8d1c9 89993c010000    mov     dword ptr [ecx+13Ch],ebx
 84 83e8d1cf 89b92c010000    mov     dword ptr [ecx+12Ch],edi
 85 83e8d1d5 8bda            mov     ebx,edx
 86 83e8d1d7 f6050869f88340  test    byte ptr [nt!PerfGlobalGroupMask+0x8 (83f86908)],40h
 87 83e8d1de 0f954512        setne   byte ptr [ebp+12h]
 88 83e8d1e2 0f858c030000    jne     nt!KiServiceExit2+0x17b (83e8d574)
 89 83e8d1e8 ffd3            call    ebx
 90 83e8d1ea f6456c01        test    byte ptr [ebp+6Ch],1
 91 83e8d1ee 7434            je      nt!KiFastCallEntry+0x164 (83e8d224)
 92 83e8d1f0 8bf0            mov     esi,eax
 93 83e8d1f2 ff156801e583    call    dword ptr [nt!_imp__KeGetCurrentIrql (83e50168)]
 94 83e8d1f8 0ac0            or      al,al
 95 83e8d1fa 0f853b030000    jne     nt!KiServiceExit2+0x142 (83e8d53b)
 96 83e8d200 8bc6            mov     eax,esi
 97 83e8d202 648b0d24010000  mov     ecx,dword ptr fs:[124h]
 98 83e8d209 f68134010000ff  test    byte ptr [ecx+134h],0FFh
 99 83e8d210 0f8543030000    jne     nt!KiServiceExit2+0x160 (83e8d559)
100 83e8d216 8b9184000000    mov     edx,dword ptr [ecx+84h]
101 83e8d21c 0bd2            or      edx,edx
102 83e8d21e 0f8535030000    jne     nt!KiServiceExit2+0x160 (83e8d559)
103 83e8d224 8be5            mov     esp,ebp
104 83e8d226 807d1200        cmp     byte ptr [ebp+12h],0
105 83e8d22a 0f8550030000    jne     nt!KiServiceExit2+0x187 (83e8d580)
106 83e8d230 648b0d24010000  mov     ecx,dword ptr fs:[124h]
107 83e8d237 8b553c          mov     edx,dword ptr [ebp+3Ch]
108 83e8d23a 899128010000    mov     dword ptr [ecx+128h],edx

 

    

 

   

  

  

 

你可能感兴趣的:(Windows系统调用中的现场保存)