文章目录
SwapContext有几个参数
esi : 当前线程 (_ETHREAD)
edi : 要切换的线程 (_ETHREAD)
ebx : _KPCR
cl : _KTHREAD.WaitIrql ,进去就被改了
SwapContext在哪里实现了线程切换
哪里进行了进程切换(修改CR3)
这里的 _ETHREAD + 0x44
为养父母,当前在读取哪个进程的数据就填哪个进程
而_ETHREAD + 0x220
为亲父母,一定是创建自己的进程
所以这里的语义是,当 前线程读取的进程 与 下一个线程读取的进程 是否为同一个。与创建自己的进程无关
如果我们自己修改CR3,那么如果在读取数据的时候线程被切走了,那么线程重新回来的时候,CR3又变回_EHTREAD + 0x44
了。
所以自己修改CR3的时候有两个办法:
1.关中断后,修改CR3之
2.把_ETHREAD + 0x44也改了,之后再改回来
TSS存储当前的 SS0 : ESP0
FS:[0]在3环总能正确指向当前TEB
先回顾一下TSS段描述符,我们假设当前段寄存器值为3B,那么就有上图
为什么是FS段选择子是0x3B?当然也可以是其他的,只要3环和0环规定使用同一个选择子就行了,这样他们就能通过同一个选择子读到同一块内存 (GDT[selector.index].base)
异常链表的切换
上一幅图 pop ecx 我有写注释,这也是异常链表切换的一部分
这幅图在切换堆栈之前push ecx,将异常链表存入了旧的堆栈中
完整分析代码
.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 es:[esi+_ETHREAD.Tcb.State], 2 ; esi 新Thread
.text:0040492B pushf
.text:0040492C
.text:0040492C loc_40492C: ; CODE XREF: KiIdleLoop()+5A↓j
.text:0040492C mov ecx, [ebx+_KPCR.NtTib.ExceptionList] ; ebx = _KPCR
.text:0040492E cmp dword ptr [ebx+994h], 0
.text:00404935 push ecx ; 将旧的线程的异常链表存入旧堆栈
.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+_ETHREAD.Tcb.DebugActive]
.text:00404951 mov [ebx+_KPCR.DebugActive], cl
.text:00404954 cli
.text:00404955 mov [edi+_ETHREAD.Tcb.KernelStack], esp ; edi 当前Thread
.text:00404958 mov eax, [esi+_ETHREAD.Tcb.InitialStack]
.text:0040495B mov ecx, [esi+_ETHREAD.Tcb.StackLimit]
.text:0040495E sub eax, 210h
.text:00404963 mov [ebx+_KPCR.NtTib.StackLimit], ecx
.text:00404966 mov [ebx+_KPCR.NtTib.StackBase], eax
.text:00404969 xor ecx, ecx
.text:0040496B mov cl, [esi+_ETHREAD.Tcb.NpxState]
.text:0040496E and edx, 0FFFFFFF1h
.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 ; 是否为8086保护模式
.text:0040498A jnz short loc_40498F ; (!= 保护模式) 实模式,跳转
.text:0040498C sub eax, 10h ; (== 保护模式),跳过填充实模式的四个值
.text:0040498C ; Trap_Frame + 0x88 --> Trap_Frame + 0x78
.text:0040498F
.text:0040498F loc_40498F: ; CODE XREF: SwapContext+66↑j
.text:0040498F mov ecx, [ebx+_KPCR.TSS] ; ecx = TSS (准备修改当前线程的TSS)
.text:00404992 mov [ecx+4], eax ; TSS.esp0 = newEsp (修正正在使用的TSS.esp0)
.text:00404995 mov esp, [esi+_ETHREAD.Tcb.KernelStack] ; 切换堆栈
.text:00404998 mov eax, [esi+_ETHREAD.Tcb.Teb]
.text:0040499B mov [ebx+_KPCR.NtTib.Self], eax
.text:0040499E sti
.text:0040499F mov eax, [edi+_ETHREAD.Tcb.ApcState.Process]
.text:004049A2 cmp eax, [esi+_ETHREAD.Tcb.ApcState.Process]
.text:004049A5 mov [edi+_ETHREAD.Tcb.IdleSwapBlock], 0
.text:004049A9 jz short loc_4049D7 ; if (curProcess == newProcess) jmp
.text:004049A9 ; else 修改CR3 (进程切换)
.text:004049AB mov edi, [esi+_ETHREAD.Tcb.ApcState.Process] ; edi = _EPROCESS
.text:004049AE test word ptr [edi+20h], 0FFFFh
.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
.text:004049BF assume gs:GAP
.text:004049BF mov eax, [edi+_EPROCESS.Pcb.DirectoryTableBase]
.text:004049C2 mov ebp, [ebx+_KPCR.TSS]
.text:004049C5 mov ecx, dword ptr [edi+_EPROCESS.Pcb.IopmOffset]
.text:004049C8 mov [ebp+1Ch], eax ; TSS.CR3 = newCR3
.text:004049CB mov cr3, eax
.text:004049CE mov [ebp+66h], cx ; TSS.I/O Map Base Address = _EPROCESS.Pcb.IopmOffset
.text:004049CE ; 我也不懂
.text:004049D2 jmp short loc_4049D7
.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.NtTib.Self]
.text:004049DA mov ecx, [ebx+_KPCR.GDT]
.text:004049DD mov [ecx+3Ah], ax ; Base Address 15:00
.text:004049E1 shr eax, 10h ; 取高16位
.text:004049E4 mov [ecx+3Ch], al ; Base Address 23:16
.text:004049E7 mov [ecx+3Fh], ah ; Base Address 31:24
.text:004049EA inc [esi+_ETHREAD.Tcb.ContextSwitches]
.text:004049ED inc [ebx+_KPCR.PrcbData.KeContextSwitches]
.text:004049F3 pop ecx ; 当前线程的异常链表 (上面已经切换过堆栈了,所以这里是新的异常链表)
.text:004049F3 ; 最开始的push ecx,旧的异常链表被放入了旧的堆栈中
.text:004049F4 mov [ebx+_KPCR.NtTib.ExceptionList], ecx
.text:004049F6 cmp [esi+_ETHREAD.Tcb.ApcState.KernelApcPending], 0
.text:004049FA jnz short loc_404A00
.text:004049FC popf
.text:004049FD xor eax, eax
.text:004049FF retn