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

猜你喜欢

转载自blog.csdn.net/lifeshave/article/details/87441049
08