1. 程式人生 > >Windows系統呼叫中的現場儲存

Windows系統呼叫中的現場儲存

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

Windows系統呼叫中的現場儲存

  我們之前介紹過三環進零環的步驟,通過中斷或者快速呼叫來實現。

  但是我們是否考慮過CPU從三環進入零環時,其三環的暫存器該如何儲存。

  這一篇檔案就來介紹其系統呼叫中的(三環)現場儲存的問題。

 

一、幾個重要的結構體介紹

1. _Ktrap_frame

  

  該結構體簡單來說用於三環的暫存器儲存,儲存於零環,由作業系統維護,每個執行緒都有自己的 _Ktrap_frame 結構體(ethread+0x108處)。

  詳細資訊可以檢視這篇文章:解析windows核心每日一講 陷阱排程

  我們之前講過進入0環時獲取新的四個暫存器非常重要, SS\CS\EIP\ESP

  如下圖,當進入零環時,作業系統會獲取ESP值,該值指向_Ktrap_frame結構體,將舊的SS\CS\EIP\ESP\EFLAG依次壓入(0x78-0x68處),之後ESP +0x070 ErrCode處。

  之後便進入獲取的EIP來執行核心函式。

  

 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 // 壓棧 按照 _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