08 Windows線程切換--TSS

1、內核堆棧
在這裏插入圖片描述

2、內核堆棧結構
在這裏插入圖片描述

kd> dt _ktrap_frame
ntdll!_KTRAP_FRAME
   +0x000 DbgEbp           : Uint4B
   +0x004 DbgEip           : Uint4B
   +0x008 DbgArgMark       : Uint4B
   +0x00c DbgArgPointer    : Uint4B
   +0x010 TempSegCs        : Uint4B
   +0x014 TempEsp          : Uint4B
   +0x018 Dr0              : Uint4B
   +0x01c Dr1              : Uint4B
   +0x020 Dr2              : Uint4B
   +0x024 Dr3              : Uint4B
   +0x028 Dr6              : Uint4B
   +0x02c Dr7              : Uint4B
   +0x030 SegGs            : Uint4B
   +0x034 SegEs            : Uint4B
   +0x038 SegDs            : Uint4B
   +0x03c Edx              : Uint4B
   +0x040 Ecx              : Uint4B
   +0x044 Eax              : Uint4B
   +0x048 PreviousPreviousMode : Uint4B
   +0x04c ExceptionList    : Ptr32 _EXCEPTION_REGISTRATION_RECORD
   +0x050 SegFs            : Uint4B
   +0x054 Edi              : Uint4B
   +0x058 Esi              : Uint4B
   +0x05c Ebx              : Uint4B
   +0x060 Ebp              : Uint4B
   +0x064 ErrCode          : Uint4B
   +0x068 Eip              : Uint4B
   +0x06c SegCs            : Uint4B
   +0x070 EFlags           : Uint4B
   +0x074 HardwareEsp      : Uint4B
   +0x078 HardwareSegSs    : Uint4B
   +0x07c V86Es            : Uint4B
   +0x080 V86Ds            : Uint4B
   +0x084 V86Fs            : Uint4B
   +0x088 V86Gs            : Uint4B

3、API進0環
普通調用:通過TSS.ESP0得到0環堆棧
快速調用:從MSR得到一個0環的堆棧,代碼執行後仍然通過TSS.ESP0的到當前線程0環堆棧

4、TSS
Intel設計TSS是爲了任務切換(線程切換),但Windows和Linux並沒有使用,而是使用堆棧來保存寄存器的值

text:0046A9F0 SwapContext     proc near               ; CODE XREF: KiUnlockDispatcherDatabase(x)+72↑p
.text:0046A9F0                                         ; KiSwapContext(x)+29↑p ...
.text:0046A9F0                 or      cl, cl
.text:0046A9F2                 mov     byte ptr es:[esi+2Dh], 2 ; 目標KTHREAD( +0x02d State            : UChar)改爲2
.text:0046A9F7                 pushf                   ; 存儲當前的EFLAGS寄存器
.text:0046A9F8
.text:0046A9F8 loc_46A9F8:                             ; CODE XREF: KiIdleLoop()+5A↓j
.text:0046A9F8                 mov     ecx, [ebx]      ; EBX存儲的是KPCR的地址,此處是讀取異常鏈表
.text:0046A9FA                 cmp     dword ptr [ebx+994h], 0 ; +0x874 DpcRoutineActive : Uint4B是否有dpc有就藍屏
.text:0046AA01                 push    ecx
.text:0046AA02                 jnz     loc_46AB3D
.text:0046AA08                 cmp     ds:_PPerfGlobalGroupMask, 0 ; LOG用的 Windows自己調試用的,別的地方沒有用
.text:0046AA0F                 jnz     loc_46AB14
.text:0046AA15
.text:0046AA15 loc_46AA15:                             ; CODE XREF: SwapContext+12C↓j
.text:0046AA15                                         ; SwapContext+13D↓j ...
.text:0046AA15                 mov     ebp, cr0        ; 1.cr0的保護控制位
.text:0046AA15                                         ; 2.協處理器控制位
.text:0046AA18                 mov     edx, ebp
.text:0046AA1A                 mov     cl, [esi+2Ch]   ; 當前cpu所跑的線程正在被調試
.text:0046AA1A                                         ; 調試器在3環調用API設置的(Atach就有)
.text:0046AA1A                                         ; 這個位置0不支持硬件斷點了
.text:0046AA1D                 mov     [ebx+50h], cl
.text:0046AA20                 cli                     ; 清CPU,表示這個CPU不在被時鐘中斷打擾
.text:0046AA20                                         ; 時鐘 鍵盤 鼠標等有時間,CPU默認指向IAT,關閉中段則忽略這些硬件時間
.text:0046AA21                 mov     [edi+28h], esp  ; 將當前的ESP存儲到線程結構體中
.text:0046AA24                 mov     eax, [esi+18h]  ; 目標線程棧底
.text:0046AA27                 mov     ecx, [esi+1Ch]
.text:0046AA2A                 sub     eax, 210h       ; -210h浮點寄存器
.text:0046AA2F                 mov     [ebx+8], ecx
.text:0046AA32                 mov     [ebx+4], eax
.text:0046AA35                 xor     ecx, ecx
.text:0046AA37                 mov     cl, [esi+31h]   ;  +0x031 NpxState         : UChar
.text:0046AA37                                         ; 浮點寄存器 運行浮點用這個,沒有不用
.text:0046AA3A                 and     edx, 0FFFFFFF1h ; 判斷NpxState有沒有浮點支持
.text:0046AA3A                                         ; 如果上一個線程跟這一個線程浮點支持是一樣的,就不用替換Cr0
.text:0046AA3D                 or      ecx, edx
.text:0046AA3F                 or      ecx, [eax+20Ch]
.text:0046AA45                 cmp     ebp, ecx
.text:0046AA47                 jnz     loc_46AB0C
.text:0046AA4D                 lea     ecx, [ecx+0]
.text:0046AA50
.text:0046AA50 loc_46AA50:                             ; CODE XREF: SwapContext+11F↓j
.text:0046AA50                 test    dword ptr [eax-1Ch], 20000h ; 檢查是否是虛擬8086模式,如果是eax-10h,也就是減掉
.text:0046AA50                                         ; _ktrap_frame中的四個成員
.text:0046AA50                                         ;    +0x07c V86Es            : Uint4B
.text:0046AA50                                         ;    +0x080 V86Ds            : Uint4B
.text:0046AA50                                         ;    +0x084 V86Fs            : Uint4B
.text:0046AA50                                         ;    +0x088 V86Gs            : Uint4B
.text:0046AA57                 jnz     short loc_46AA5C
.text:0046AA59                 sub     eax, 10h        ; 減去4個成員,在虛擬8086模式下使用
.text:0046AA5C
.text:0046AA5C loc_46AA5C:                             ; CODE XREF: SwapContext+67↑j
.text:0046AA5C                 mov     ecx, [ebx+40h]  ; 通過KPCR取出TSS的值
.text:0046AA5F                 mov     [ecx+4], eax    ; 將修正過的棧底存到TSS中
.text:0046AA62                 mov     esp, [esi+28h]  ; 將目標棧的ESP存儲到ESP中
.text:0046AA65                 mov     eax, [esi+20h]  ; 當前線程中有很多狀態,一個在ETHREAD中,一個在FS中
.text:0046AA65                                         ; 這樣的好處就是可以在3環通過FS獲取當前線程的狀態
.text:0046AA68                 mov     [ebx+18h], eax  ; 臨時存儲目標線程的TEB
.text:0046AA6B                 sti
.text:0046AA6C                 mov     eax, [edi+44h]
.text:0046AA6F                 cmp     eax, [esi+44h]
.text:0046AA72                 mov     byte ptr [edi+50h], 0
.text:0046AA76                 jz      short loc_46AAA4 ; 如果是一個進程中的線程切換跳轉
.text:0046AA78                 mov     edi, [esi+44h]  ; 如果不是一個進程,取出線程的KPROCESS
.text:0046AA7B                 test    word ptr [edi+20h], 0FFFFh ; 判斷LdtDescriptor是否爲-1
.text:0046AA7B                                         ; win95之前用Ldt,之後不用
.text:0046AA81                 jnz     short loc_46AADE
.text:0046AA83                 xor     eax, eax
.text:0046AA85
.text:0046AA85 loc_46AA85:                             ; CODE XREF: SwapContext+117↓j
.text:0046AA85                 lldt    ax
.text:0046AA88                 xor     eax, eax
.text:0046AA8A                 mov     gs, eax         ; gs段寄存器清0,這就能解釋爲什麼3環gs爲0
.text:0046AA8C                 assume gs:GAP
.text:0046AA8C                 mov     eax, [edi+18h]  ; 得到目標進程的Cr3
.text:0046AA8F                 mov     ebp, [ebx+40h]  ; TSS寄存器
.text:0046AA92                 mov     ecx, [edi+30h]
.text:0046AA95                 mov     [ebp+1Ch], eax  ; 將當前Tss中的Cr3修改爲目標進程的CR3
.text:0046AA98                 mov     cr3, eax        ; 切換Cr3
.text:0046AA9B                 mov     [ebp+66h], cx   ; 存儲IO權限位圖到TSS 當前線程的IO權限位圖  windows2000以後沒用了
.text:0046AA9F                 jmp     short loc_46AAA4
.text:0046AA9F ; ---------------------------------------------------------------------------
.text:0046AAA1                 align 4
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章