天天看点

线程切换 —— 逆向分析 KiSwapContex/SwapContex

简介

Windows

内核中,线程的切换是通过底层内核

API

函数

KiSwapContex/SwapContex

实现的。

KiSwapContex

KiSwapContex

的逆向分析如下:

.text:00404828 ; =============== S U B R O U T I N E =======================================
.text:00404828
.text:00404828
.text:00404828 ; __fastcall KiSwapContext(x)
.text:00404828 @KiSwapContext@4 proc near              ; CODE XREF: KiSwapThread()+33↓p
.text:00404828
.text:00404828 var_10          = dword ptr -10h
.text:00404828 var_C           = dword ptr -0Ch
.text:00404828 var_8           = dword ptr -8
.text:00404828 var_4           = dword ptr -4
.text:00404828
.text:00404828                 sub     esp, 10h
.text:0040482B                 mov     [esp+10h+var_4], ebx ; 保存当前寄存器的现场
.text:0040482F                 mov     [esp+10h+var_8], esi
.text:00404833                 mov     [esp+10h+var_C], edi
.text:00404837                 mov     [esp+10h+var_10], ebp
.text:0040483A                 mov     ebx, ds:0FFDFF01Ch ; SelfPcr, 即fs[0x1c], 指向_KPCR的指针
.text:00404840                 mov     esi, ecx        ; ESI中存储的是要切换线程的_KTHREAD
.text:00404842                 mov     edi, [ebx+_KPCR.PrcbData.CurrentThread] ; 取出当前线程的_KTHREAD
.text:00404848                 mov     [ebx+_KPCR.PrcbData.CurrentThread], esi ; 更新CPU的_KTHREAD
.text:0040484E                 mov     cl, [edi+58h]
.text:00404851                 call    SwapContext     ; 切换线程上下文
.text:00404851                                         ; ESI指向要切换线程的_KTHREAD
.text:00404851                                         ; EDI指向当前线程的_KTHREAD
.text:00404851                                         ; EBX中存储的是SelfPcr, 指向_KPCR的指针
.text:00404856                 mov     ebp, [esp+10h+var_10] ; 恢复寄存器现场
.text:00404859                 mov     edi, [esp+10h+var_C]
.text:0040485D                 mov     esi, [esp+10h+var_8]
.text:00404861                 mov     ebx, [esp+10h+var_4]
.text:00404865                 add     esp, 10h
.text:00404868                 retn
.text:00404868 @KiSwapContext@4 endp
           

SwapContex

SwapContex

的逆向分析如下:

.text:00404924 ; =============== S U B R O U T I N E =======================================
.text:00404924
.text:00404924 ; ESI指向要切换线程的_KTHREAD
.text:00404924 ; EDI指向当前线程的_KTHREAD
.text:00404924 ; EBX中存储的是SelfPcr, 指向_KPCR的指针
.text:00404924
.text:00404924 SwapContext     proc near               ; CODE XREF: KiUnlockDispatcherDatabase(x)+72↑p
.text:00404924                                         ; KiSwapContext(x)+29↑p ...
.text:00404924                 or      cl, cl
.text:00404926                 mov     byte ptr es:[esi+_KTHREAD.State], 2 ; es寄存器是附加段寄存器, 更新当前线程的状态
.text:0040492B                 pushf                   ; 即 pushfd
.text:0040492C
.text:0040492C loc_40492C:                             ; CODE XREF: KiIdleLoop()+5A↓j
.text:0040492C                 mov     ecx, [ebx+_KPCR.Nt_Tib.ExceptionList]
.text:0040492E                 cmp     dword ptr [ebx+994h], 0
.text:00404935                 push    ecx             ; 备份指向ExceptionList异常链表的指针
.text:00404936                 jnz     loc_404A70
.text:0040493C                 cmp     ds:_PPerfGlobalGroupMask, 0
.text:00404943                 jnz     loc_404A47
.text:00404949
.text:00404949 loc_404949:                             ; CODE XREF: SwapContext+12B↓j
.text:00404949                                         ; SwapContext+13C↓j ...
.text:00404949                 mov     ebp, cr0
.text:0040494C                 mov     edx, ebp
.text:0040494E                 mov     cl, [esi+2Ch]
.text:00404951                 mov     [ebx+_KPCR.DebugActive], cl
.text:00404954                 cli                     ; 屏蔽中断, 不被I/O事件, 时钟干扰
.text:00404955                 mov     [edi+_KTHREAD.KernelStack], esp ; 保存原线程的ESP
.text:00404958                 mov     eax, [esi+_KTHREAD.InitialStack]
.text:0040495B                 mov     ecx, [esi+_KTHREAD.StackLimit]
.text:0040495E                 sub     eax, 210h       ; 将上边的浮点寄存器去掉,指向真正的栈顶
.text:00404963                 mov     [ebx+_KPCR.StackLimit], ecx ; 将新线程的堆栈属性写入_KPCR中
.text:00404966                 mov     [ebx+_KPCR.StackBase], eax
.text:00404969                 xor     ecx, ecx
.text:0040496B                 mov     cl, [esi+_KPCR.Tcb.NpxState] ; NpxState浮点寄存器
.text:0040496E                 and     edx, 0FFFFFFF1h ; 判断NpxState有没有浮点支持
.text:00404971                 or      ecx, edx
.text:00404973                 or      ecx, [eax+20Ch]
.text:00404979                 cmp     ebp, ecx
.text:0040497B                 jnz     loc_404A3F
.text:00404981                 lea     ecx, [ecx]
.text:00404983
.text:00404983 loc_404983:                             ; CODE XREF: SwapContext+11E↓j
.text:00404983                 test    dword ptr [eax-1Ch], 20000h
.text:0040498A                 jnz     short loc_40498F ; 判断是否是虚拟8086模式
.text:0040498C                 sub     eax, 10h
.text:0040498F
.text:0040498F loc_40498F:                             ; CODE XREF: SwapContext+66↑j
.text:0040498F                 mov     ecx, [ebx+_KPCR.TSS] ; 此时EAX指向0环栈顶,即EAX = ESP
.text:00404992                 mov     [ecx+_KTSS.ESP0], eax ; 将ESP0写入TSS
.text:00404995                 mov     esp, [esi+_KTHREAD.KernelStack] ; 将新线程的KernelStack写入ESP, 完成堆栈切换
.text:00404998                 mov     eax, [esi+_KTHREAD.Teb]
.text:0040499B                 mov     [ebx+_KPCR.NtTib.Self], eax ; 更新_KPCR.NtTib.Self指向新线程的0环Teb
.text:0040499E                 sti                     ; 允许中断发生
.text:0040499F                 mov     eax, [edi+_KTHREAD.ApcState.Process] ; 获取旧线程所属的资源进程的_KPROCESS结构体指针
.text:004049A2                 cmp     eax, [esi+_KTHREAD.ApcState.Process] ; 将旧线程所属资源进程_KPROCESS指针与新线程的指针相比较
.text:004049A2                                         ; 如果发生了跨进程切换线程则更换Cr3,否则不更换Cr3
.text:004049A5                 mov     byte ptr [edi+_KTHREAD.IdleSwapBlock], 0
.text:004049A9                 jz      short loc_4049D7 ; EAX将指向_KPCR
.text:004049AB                 mov     edi, [esi+_KTHREAD.ApcState.Process] ; 发生了跨进程线程切换
.text:004049AB                                         ; 更新EDI, 指向新线程的资源提供进程的_KPROCESS
.text:004049AE                 test    word ptr [edi+_KTHREAD.Teb], 0FFFFh ; ExceptionList链表的地址的高位是否为0xffff
.text:004049B4                 jnz     short loc_404A11
.text:004049B6                 xor     eax, eax
.text:004049B8
.text:004049B8 loc_4049B8:                             ; CODE XREF: SwapContext+116↓j
.text:004049B8                 lldt    ax
.text:004049BB                 xor     eax, eax
.text:004049BD                 mov     gs, eax         ; 将GS寄存器置为0,Windows并未使用GS寄存器
.text:004049BF                 assume gs:GAP
.text:004049BF                 mov     eax, [edi+_KPROCESS.DirectoryTableBase] ; 获取新线程的Cr3
.text:004049C2                 mov     ebp, [ebx+_KPCR.TSS] ; 获取TSS
.text:004049C5                 mov     ecx, [edi+_KPROCESS.IOpmOffset]
.text:004049C8                 mov     [ebp+TSS.Cr3], eax ; 更新TSS中的Cr3
.text:004049CB                 mov     cr3, eax        ; 切换Cr3
.text:004049CE                 mov     [ebp+I/O_Map_base_Address], cx
.text:004049D2                 jmp     short loc_4049D7 ; EAX将指向_KPCR
.text:004049D2 ; ---------------------------------------------------------------------------
.text:004049D4                 db 8Dh, 49h, 0
.text:004049D7 ; ---------------------------------------------------------------------------
.text:004049D7
.text:004049D7 loc_4049D7:                             ; CODE XREF: SwapContext+85↑j
.text:004049D7                                         ; SwapContext+AE↑j
.text:004049D7                 mov     eax, [ebx+_KPCR.SelfPcr] ; EAX将指向_KPCR
.text:004049DA                 mov     ecx, [ebx+_KPCR.GDT] ; ECX将指向GDT表
.text:004049DD                 mov     [ecx+3Ah], ax   ; 更新GDT表中的0x38处的段描述符的Base
.text:004049E1                 shr     eax, 10h        ; 取EAX的高2字节
.text:004049E4                 mov     [ecx+3Ch], al   ; 更新Base
.text:004049E7                 mov     [ecx+3Fh], ah   ; 更新Base
.text:004049E7                                         ; 此时fs寄存器可以通过段选择子0x3B找到当前的段描述符
.text:004049EA                 inc     dword ptr [esi+_KTHREAD.ContextSwitches]
.text:004049ED                 inc     dword ptr [ebx+61Ch]
.text:004049F3                 pop     ecx             ; ECX将指向_KPCR.Nt_Tib
.text:004049F4                 mov     [ebx], ecx      ; 恢复新线程异常链表的地址
.text:004049F6                 cmp     byte ptr [esi+_KTHREAD.ApcState.KernelApcPending], 0
.text:004049FA                 jnz     short loc_404A00
.text:004049FC                 popf
.text:004049FD                 xor     eax, eax
.text:004049FF                 retn
.text:00404A00 ; ---------------------------------------------------------------------------
.text:00404A00
.text:00404A00 loc_404A00:                             ; CODE XREF: SwapContext+D6↑j
.text:00404A00                 popf
.text:00404A01                 jnz     short loc_404A06
.text:00404A03                 mov     al, 1
.text:00404A05                 retn
.text:00404A06 ; ---------------------------------------------------------------------------
.text:00404A06
.text:00404A06 loc_404A06:                             ; CODE XREF: SwapContext+DD↑j
.text:00404A06                 mov     cl, 1
.text:00404A08                 call    ds:[email protected]@4 ; HalRequestSoftwareInterrupt(x)
.text:00404A0E                 xor     eax, eax
.text:00404A10                 retn
.text:00404A11 ; ---------------------------------------------------------------------------
.text:00404A11
.text:00404A11 loc_404A11:                             ; CODE XREF: SwapContext+90↑j
.text:00404A11                 mov     ebp, [ebx+_KPCR.GDT]
.text:00404A14                 mov     eax, [edi+20h]
.text:00404A17                 mov     [ebp+48h], eax
.text:00404A1A                 mov     eax, [edi+24h]
.text:00404A1D                 mov     [ebp+4Ch], eax
.text:00404A20                 mov     eax, 48h
.text:00404A25                 mov     ebp, [ebx+38h]
.text:00404A28                 mov     ecx, [edi+28h]
.text:00404A2B                 mov     [ebp+108h], ecx
.text:00404A31                 mov     ecx, [edi+2Ch]
.text:00404A34                 mov     [ebp+10Ch], ecx
.text:00404A3A                 jmp     loc_4049B8
.text:00404A3F ; ---------------------------------------------------------------------------
.text:00404A3F
.text:00404A3F loc_404A3F:                             ; CODE XREF: SwapContext+57↑j
.text:00404A3F                 mov     cr0, ecx
.text:00404A42                 jmp     loc_404983
.text:00404A47 ; ---------------------------------------------------------------------------
.text:00404A47
.text:00404A47 loc_404A47:                             ; CODE XREF: SwapContext+1F↑j
.text:00404A47                 mov     eax, ds:_PPerfGlobalGroupMask
.text:00404A4C                 cmp     eax, 0
.text:00404A4F                 jz      loc_404949
.text:00404A55                 mov     edx, esi
.text:00404A57                 mov     ecx, edi
.text:00404A59                 test    dword ptr [eax+4], 4
.text:00404A60                 jz      loc_404949
.text:00404A66                 call    @WmiTraceContextSwap@8 ; WmiTraceContextSwap(x,x)
.text:00404A6B                 jmp     loc_404949
.text:00404A70 ; ---------------------------------------------------------------------------
.text:00404A70
.text:00404A70 loc_404A70:                             ; CODE XREF: SwapContext+12↑j
.text:00404A70                 push    0B8h            ; BugCheckCode
.text:00404A75                 call    _KeBugCheck@4   ; KeBugCheck(x)
.text:00404A75 SwapContext     endp
.text:00404A75
           

继续阅读