6.windows线程切换_主动切换

ida 分析KiSwapThread

sub     esp, 10h
mov     [esp+10h+var_4], ebx 	;保存当前线程寄存器现场
mov     [esp+10h+var_8], esi
mov     [esp+10h+var_C], edi
mov     [esp+10h+var_10], ebp
mov     ebx, ds:0FFDFF01Ch
mov     esi, ecx			 	;ESI中存储的是要切换线程的_KTHREAD(ecx是别的地方传过来的)
mov     edi, [ebx+KPCR.PrcbData.CurrentThread];取出运行当前代码的线程的_KTHREAD
mov     [ebx+KPCR.PrcbData.CurrentThread], esi
mov     cl, [edi+58h]
call    SwapContext	;真正的线程切换函数
mov     ebp, [esp+10h+var_10];恢复新线程各种寄存器
mov     edi, [esp+10h+var_C]
mov     esi, [esp+10h+var_8]
mov     ebx, [esp+10h+var_4]
add     esp, 10h
retn

查看有多少处调用,选中函数->view->Open subviews->cross refer…
6.windows线程切换_主动切换_第1张图片
只要调用这里面的api都会导致线程切换,windows的api绝大多数都会调用这里面的api

ecx来源
@KiSwapThread@0 proc near              

                mov     edi, edi
                push    esi
                push    edi
                db      3Eh
                mov     eax, ds:0FFDFF020h
                mov     esi, eax
                mov     eax, [esi+8]
                test    eax, eax
                mov     edi, [esi+4]
                jnz     loc_4109AF
                push    ebx
                movsx   ebx, byte ptr [esi+10h]
                xor     edx, edx
                mov     ecx, ebx
call    @KiFindReadyThread@8 ;该函数返回一个_KTHREAD eax来源
test    eax, eax
jz      loc_40EA85
                      
pop     ebx
                 
mov     ecx, eax	;eac来源,eax来源上面的call
call    @KiSwapContext@4 ; 寄存器传参 参数是KiFindReadyThread找到的结构体
test    al, al
mov     cl, [edi+58h]   ; NewIrql
mov     edi, [edi+54h]
mov     esi, ds:__imp_@KfLowerIrql@4 ; KfLowerIrql(x)
jnz     loc_415ADB
     
call    esi ; 
mov     eax, edi
pop     edi
pop     esi
retn

call SwapContext ;真正的线程切换函数

SwapContext     proc near               ; CODE XREF: KiUnlockDispatcherDatabase(x)+72p
                                         ; KiSwapContext(x)+29p ...
                 or      cl, cl
                 mov     byte ptr es:[esi+2Dh], 2 ; 1就绪 2运行 5等待
                 pushf                   ; 保持EFLAGS寄存器

loc_40492C:                             ; CODE XREF: KiIdleLoop()+5Aj
                 mov     ecx, [ebx]      ; 保存本线程切换时的内核SEH链表
                 cmp     dword ptr [ebx+994h], 0 ; 是否有DPC有就蓝屏
                 push    ecx
                 jnz     loc_404A70
                 cmp     ds:_PPerfGlobalGroupMask, 0 ; log用的windows自己调试用的别的地方没用
                 jnz     loc_404A47

loc_404949:                             ; CODE XREF: SwapContext+12Bj
                                         ; SwapContext+13Cj ...
                 mov     ebp, cr0        ; CR0中的保存控制位
                 mov     edx, ebp
                 mov     cl, [esi+2Ch]
                 mov     [ebx+50h], cl
                 cli
                 mov     [edi+28h], esp  ; 当前的ESP存储到原线程结构中
                 mov     eax, [esi+18h]  ; 目标线程栈顶
                 mov     ecx, [esi+1Ch]
                 sub     eax, 210h
                 mov     [ebx+8], ecx
                 mov     [ebx+4], eax
                 xor     ecx, ecx
                 mov     cl, [esi+31h]
                 and     edx, 0FFFFFFF1h
                 or      ecx, edx
                 or      ecx, [eax+20Ch]
                 cmp     ebp, ecx
                 jnz     loc_404A3F
                 lea     ecx, [ecx]

loc_404983:                             ; CODE XREF: SwapContext+11Ej
                 test    dword ptr [eax-1Ch], 20000h
                 jnz     short loc_40498F ; 取出TSS(tss就是从3环向0环切的时候去TSS中取ESP0和SS0别的没用)
                 sub     eax, 10h

loc_40498F:                             ; CODE XREF: SwapContext+66j
                 mov     ecx, [ebx+40h]  ; 取出TSS(tss就是从3环向0环切的时候去TSS中取ESP0和SS0别的没用)
                 mov     [ecx+4], eax    ; 将修正后的栈顶存储到tss中
                 mov     esp, [esi+28h]  ; 将目标线程的ESP存储到ESP中
                 mov     eax, [esi+20h]  ; 当前线程有很多状态一份在ETHREAD里面还有一个备份在FS中
                                         ; 这样做的好处就是可以在3环通过FS获取当前线程信息
                 mov     [ebx+18h], eax  ; 临时存储目标的TEB
                 sti
                 mov     eax, [edi+44h]  ; 40h =_ETHREAD.Tcb.ApcState.Process
                 cmp     eax, [esi+44h]  ; 40h=_ETHREAD.Tcb.ApcState.Process
                 mov     byte ptr [edi+50h], 0
                 jz      short loc_4049D7
                 mov     edi, [esi+44h]
                 test    word ptr [edi+20h], 0FFFFh
                 jnz     short loc_404A11
                 xor     eax, eax

loc_4049B8:                             ; CODE XREF: SwapContext+116j
                 lldt    ax
                 xor     eax, eax
                 mov     gs, eax
                 assume gs:GAP
                 mov     eax, [edi+18h]
                 mov     ebp, [ebx+40h]
                 mov     ecx, [edi+30h]
                 mov     [ebp+1Ch], eax
                 mov     cr3, eax
                 mov     [ebp+66h], cx
                 jmp     short loc_4049D7
; ---------------------------------------------------------------------------
                 db 8Dh, 49h, 0
; ---------------------------------------------------------------------------

loc_4049D7:                             ; CODE XREF: SwapContext+85j
                                         ; SwapContext+AEj
                 mov     eax, [ebx+18h]
                 mov     ecx, [ebx+3Ch]
                 mov     [ecx+3Ah], ax
                 shr     eax, 10h
                 mov     [ecx+3Ch], al
                 mov     [ecx+3Fh], ah
                 inc     dword ptr [esi+4Ch]
                 inc     dword ptr [ebx+61Ch]
                 pop     ecx
                 mov     [ebx], ecx
                 cmp     byte ptr [esi+49h], 0
                 jnz     short loc_404A00
                 popf
                 xor     eax, eax
                 retn

看到这里是不是感觉似曾相识,没错跟我们模拟的那个是类似的只不过有很多的细节没有模拟。

总结:

  1. Windows中绝大部分API都调用了SwapContext函数也就是说,当线程只要调用了API,就是导致线程切换。
  2. 线程切换时会比较是否属于同一个进程,如果不是,切换Cr3Cr3换了,进程也就切换了。

你可能感兴趣的:(进程与线程)