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…
只要調用這裏面的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
看到這裏是不是感覺似曾相識,沒錯跟我們模擬的那個是類似的只不過有很多的細節沒有模擬。
總結:
- Windows中絕大部分API都調用了SwapContext函數也就是說,當線程只要調用了API,就是導致線程切換。
- 線程切換時會比較是否屬於同一個進程,如果不是,切換Cr3Cr3換了,進程也就切換了。