逆向KiSwapContext函数与SwapContext函数

由于KiSwapContext函数是由函数KiSwapThread函数调用的,所以我们可以先逆向一下这个函数

KiSwapThread

这里只是简单的逆了一下,可以知道这个函数在执行KiSwapContext函数时,会把做一些准备操作

.text:0040AB8A ; _DWORD __cdecl KiSwapThread()
.text:0040AB8A @KiSwapThread@0 proc near               ; CODE XREF: KeDelayExecutionThread(x,x,x):loc_40A5F3↑p
.text:0040AB8A                                         ; KeWaitForMultipleObjects(x,x,x,x,x,x,x,x):loc_40AB6E↑p ...
.text:0040AB8A
.text:0040AB8A ; FUNCTION CHUNK AT .text:004107BE SIZE 0000004F BYTES
.text:0040AB8A ; FUNCTION CHUNK AT .text:004109B9 SIZE 00000009 BYTES
.text:0040AB8A ; FUNCTION CHUNK AT .text:0041BC56 SIZE 00000015 BYTES
.text:0040AB8A ; FUNCTION CHUNK AT .text:0044B8CE SIZE 00000017 BYTES
.text:0040AB8A
.text:0040AB8A                 mov     edi, edi
.text:0040AB8C                 push    esi
.text:0040AB8D                 push    edi
.text:0040AB8E                 mov     eax, large fs:20h ; 将_KPRCB结构体Prcb放入eax中
.text:0040AB94                 mov     esi, eax
.text:0040AB96                 mov     eax, [esi+8]    ; 指向下一个要运行线程 即_KTHREAD结构体,给了eax _KPRCB.NextThread
.text:0040AB99                 test    eax, eax
.text:0040AB9B                 mov     edi, [esi+4]    ; 指向当前正在运行的线程,给了edi _KPRCB.CurrentThread
.text:0040AB9E                 jnz     loc_4109B9      ; 查看是否为0,不为0,并清0,则跳转到loc_40ABBB处
.text:0040ABA4                 push    ebx
.text:0040ABA5                 movsx   ebx, byte ptr [esi+10h] ; 将CPU编号给ebx _KPRCB.Number
.text:0040ABA9                 xor     edx, edx
.text:0040ABAB                 mov     ecx, ebx        ; mov ecx,_KPRCB.Number
.text:0040ABAD                 call    @KiFindReadyThread@8 ; ; KiFindReadyThread(x,x) 找到正在等待的线程
.text:0040ABB2                 test    eax, eax
.text:0040ABB4                 jz      loc_4107BE
.text:0040ABBA
.text:0040ABBA loc_40ABBA:                             ; CODE XREF: KiSwapThread()+5C54↓j
.text:0040ABBA                                         ; KiSwapThread()+5C78↓j ...
.text:0040ABBA                 pop     ebx
.text:0040ABBB
.text:0040ABBB loc_40ABBB:                             ; CODE XREF: KiSwapThread()+5E33↓j
.text:0040ABBB                 mov     ecx, eax        ; mov ecx,_KPRCB.NextThread
.text:0040ABBD                 call    @KiSwapContext@4 ; KiSwapContext(x)
.text:0040ABC2                 test    al, al
.text:0040ABC4                 mov     cl, [edi+58h]   ; NewIrql
.text:0040ABC7                 mov     edi, [edi+54h]
.text:0040ABCA                 mov     esi, ds:__imp_@KfLowerIrql@4 ; KfLowerIrql(x)
.text:0040ABD0                 jnz     loc_41BC56
.text:0040ABD6
.text:0040ABD6 loc_40ABD6:                             ; CODE XREF: KiSwapThread()+110DC↓j
.text:0040ABD6                 call    esi ; KfLowerIrql(x) ; KfLowerIrql(x)
.text:0040ABD8                 mov     eax, edi
.text:0040ABDA                 pop     edi
.text:0040ABDB                 pop     esi
.text:0040ABDC                 retn
.text:0040ABDC @KiSwapThread@0 endp

KiSwapContext

简单的逆了一下,有点疑问的是这个中断请求等级,得去看看文档才想的起来了

.text:0040580E ; __fastcall KiSwapContext(x)
.text:0040580E @KiSwapContext@4 proc near              ; CODE XREF: KiSwapThread()+33↓p
.text:0040580E
.text:0040580E var_10          = dword ptr -10h
.text:0040580E var_C           = dword ptr -0Ch
.text:0040580E var_8           = dword ptr -8
.text:0040580E var_4           = dword ptr -4
.text:0040580E
.text:0040580E                 sub     esp, 10h
.text:00405811                 mov     [esp+10h+var_4], ebx ; _KPRCB.Number
.text:00405815                 mov     [esp+10h+var_8], esi ; KPCR.Prcb
.text:00405819                 mov     [esp+10h+var_C], edi ; _KPRCB.CurrentThread
.text:0040581D                 mov     [esp+10h+var_10], ebp
.text:00405820                 mov     ebx, large fs:1Ch ; mov ebx,KPCR
.text:00405827                 mov     esi, ecx        ; mov esi,_KPRCB.NextThread   ecx=_KPRCB.NextThread
.text:00405829                 mov     edi, [ebx+124h] ; mov edi,KPCR.CurrentThread
.text:0040582F                 mov     [ebx+124h], esi ; mov KPCR.CurrentThread,KPCR.ThreadThread
.text:00405835                 mov     cl, [edi+58h]   ; _ETHREAD._KTHREAD.WaitIrq1(中断请求等级)
.text:00405838                 call    SwapContext
.text:0040583D                 mov     ebp, [esp+10h+var_10]
.text:00405840                 mov     edi, [esp+10h+var_C]
.text:00405844                 mov     esi, [esp+10h+var_8]
.text:00405848                 mov     ebx, [esp+10h+var_4]
.text:0040584C                 add     esp, 10h
.text:0040584F                 retn
.text:0040584F @KiSwapContext@4 endp

 SwapContext函数

.text:00405932 SwapContext     proc near               ; CODE XREF: KiUnlockDispatcherDatabase(x)+99↑p
.text:00405932                                         ; KiSwapContext(x)+2A↑p ...
.text:00405932
.text:00405932 ; FUNCTION CHUNK AT .text:00405AB9 SIZE 00000033 BYTES
.text:00405932
.text:00405932                 or      cl, cl
.text:00405934                 mov     byte ptr es:[esi+2Dh], 2 ; mov _KTHREAD(next).State,2
.text:00405934                                         ; 1 就绪
.text:00405934                                         ; 2 运行
.text:00405934                                         ; 5 等待
.text:00405939                 pushf
.text:0040593A                 lea     ecx, [ebx+540h] ; 这里可以到网上查的 但太长了 还没看
.text:00405940                 call    @KeAcquireQueuedSpinLockAtDpcLevel@4 ; KeAcquireQueuedSpinLockAtDpcLevel(x)
.text:00405945                 lea     ecx, [ebx+538h]
.text:0040594B                 call    @KeReleaseQueuedSpinLockFromDpcLevel@4 ; KeReleaseQueuedSpinLockFromDpcLevel(x)
.text:00405950
.text:00405950 loc_405950:                             ; CODE XREF: KiIdleLoop()+7C↓j
.text:00405950                 mov     ecx, [ebx]      ; mov ecx,ExceptionList
.text:00405952                 cmp     dword ptr [ebx+994h], 0
.text:00405959                 push    ecx
.text:0040595A                 jnz     loc_405AE2
.text:00405960                 cmp     ds:_PPerfGlobalGroupMask, 0
.text:00405967                 jnz     loc_405AB9
.text:0040596D
.text:0040596D loc_40596D:                             ; CODE XREF: SwapContext+18F↓j
.text:0040596D                                         ; SwapContext+1A0↓j ...
.text:0040596D                 mov     ebp, cr0        ; cr0 控制寄存器可以判断当前环境是实模式还是保护模式,是否开启分页模式,写保护
.text:00405970                 mov     edx, ebp
.text:00405972                 cmp     byte ptr [edi+31h], 0 ; cmp _KTHREAD.NpxState,0
.text:00405976                 jz      loc_405A94
.text:0040597C
.text:0040597C loc_40597C:                             ; CODE XREF: _ScPatchFxe+E↓j
.text:0040597C                 mov     cl, [esi+2Ch]   ; mov cl,_KTHREAD(next).DebugActive
.text:0040597F                 mov     [ebx+50h], cl   ; KPCR.DebugActive=_THREAD(next).DebugActive
.text:00405982                 cli                     ; 屏蔽中断
.text:00405983                 mov     [edi+28h], esp  ; mov _KTHREAD.KernelStack,esp
.text:00405986                 mov     eax, [esi+18h]  ; eax=_KTHREAD(next).InitialStack
.text:00405989                 mov     ecx, [esi+1Ch]  ; ecx=_KTHREAD(next).StackLimit
.text:0040598C                 sub     eax, 210h       ; 这里应该是初始化提升堆栈 TSS任务切换时,需要提升0x210字节,用来存储浮点寄存器的值
.text:00405991                 mov     [ebx+8], ecx    ; 切换栈大小 KPCR.StackLimit=_KTHREAD(next).StackLimit
.text:00405994                 mov     [ebx+4], eax    ; 切换栈基址 KPCR.StackBase=_KTHREAD(next).InitialStack-0x210
.text:00405997                 xor     ecx, ecx        ; ecx清0
.text:00405999                 mov     cl, [esi+31h]   ; cl=_KTHREAD(next).NpxState
.text:0040599C                 and     edx, 0FFFFFFF1h ; 判断NpxState是否支持浮点
.text:0040599C                                         ; 根据判断结果是否决定更新cr0
.text:0040599F                 or      ecx, edx
.text:004059A1                 or      ecx, [eax+20Ch]
.text:004059A7                 cmp     ebp, ecx
.text:004059A9                 jnz     loc_405A8C
.text:004059AF                 lea     ecx, [ecx]
.text:004059B1
.text:004059B1 loc_4059B1:                             ; CODE XREF: SwapContext+15D↓j
.text:004059B1                 test    dword ptr [eax-1Ch], 20000h ; SegCs&20000h
.text:004059B1                                         ; 判断是否是虚拟8086模式,如果不是,直接减掉
.text:004059B1                                         ; +0x7c V86Es
.text:004059B1                                         ; +0x80 V86Ds
.text:004059B1                                         ; +0x84 V86Fs
.text:004059B1                                         ; +0x88 V86Gs
.text:004059B1                                         ;
.text:004059B1                                         ; 如果是,那么就不减
.text:004059B1                                         ;
.text:004059B1                                         ; 这样做了之后,eax 就指向了0环栈顶,接下来就会存储到 TSS 里
.text:004059B1                                         ; 以后这个线程进0环,不论是中断门还是快速调用,都会从 TSS 里获取 ESP0
.text:004059B8                 jnz     short loc_4059BD ; ecx=KPCR.TSS
.text:004059BA                 sub     eax, 10h
.text:004059BD
.text:004059BD loc_4059BD:                             ; CODE XREF: SwapContext+86↑j
.text:004059BD                 mov     ecx, [ebx+40h]  ; ecx=KPCR.TSS
.text:004059C0                 mov     [ecx+4], eax    ; 将0环的esp保存在TSS的esp中
.text:004059C3                 mov     esp, [esi+28h]  ; esp=KTHREAD(next).KernelStack
.text:004059C6                 mov     eax, [esi+20h]  ; eax=KTHREAD(next).Teb
.text:004059C9                 mov     [ebx+18h], eax  ; KPCR._NT_TIB->Self=KTHREAD(next).Teb
.text:004059CC                 sti                     ; 开启时间中断
.text:004059CD                 mov     eax, [edi+44h]  ; eax=KTHREAD.ApcState.Process
.text:004059D0                 cmp     eax, [esi+44h]  ; KTHREAD(next).ApcState.Process
.text:004059D3                 mov     byte ptr [edi+50h], 0 ; KTHREAD.IdleSwapBlock=0
.text:004059D7                 jz      short loc_405A19
.text:004059D9                 mov     edi, [esi+44h]  ; edi=KTHREAD(next).ApcState.Process
.text:004059DC                 mov     ecx, [ebx+48h]  ; ecx=KPCR.SetMember
.text:004059DF                 xor     [eax+34h], ecx
.text:004059E2                 xor     [edi+34h], ecx
.text:004059E5                 test    word ptr [edi+20h], 0FFFFh
.text:004059EB                 jnz     short loc_405A5E
.text:004059ED                 xor     eax, eax
.text:004059EF
.text:004059EF loc_4059EF:                             ; CODE XREF: SwapContext+155↓j
.text:004059EF                 lldt    ax              ; 将LDT的段描述符存入ax中
.text:004059F2                 lea     ecx, [ebx+540h]
.text:004059F8                 call    @KeReleaseQueuedSpinLockFromDpcLevel@4 ; 释放旋转锁队列
.text:004059FD                 xor     eax, eax        ; 清0
.text:004059FF                 mov     gs, eax         ; gs=0
.text:004059FF                                         ; 这就是Windows不使用这个寄存器的依据
.text:00405A01                 assume gs:nothing
.text:00405A01                 mov     eax, [edi+18h]  ; eax=BParent(KPROCESS).DirectoryTableBase 也就是cr3的值
.text:00405A04                 mov     ebp, [ebx+40h]  ; ebp=TSS
.text:00405A07                 mov     ecx, [edi+30h]  ; ecx=BParent(KPROCESS).IopmOffset
.text:00405A0A                 mov     [ebp+1Ch], eax  ; TSS.cr3=eax
.text:00405A0D                 mov     cr3, eax        ; 切换cr3的值
.text:00405A10                 mov     [ebp+66h], cx   ; TSS.IOMap=cx
.text:00405A14                 jmp     short loc_405A24 ; eax=KPCR
.text:00405A16 ; ---------------------------------------------------------------------------
.text:00405A16                 lea     ecx, [ecx+0]
.text:00405A19
.text:00405A19 loc_405A19:                             ; CODE XREF: SwapContext+A5↑j
.text:00405A19                 lea     ecx, [ebx+540h]
.text:00405A1F                 call    @KeReleaseQueuedSpinLockFromDpcLevel@4 ; KeReleaseQueuedSpinLockFromDpcLevel(x)
.text:00405A24
.text:00405A24 loc_405A24:                             ; CODE XREF: SwapContext+E2↑j
.text:00405A24                 mov     eax, [ebx+18h]  ; eax=KPCR
.text:00405A27                 mov     ecx, [ebx+3Ch]  ; ecx=GDT
.text:00405A2A                 mov     [ecx+3Ah], ax   ; 修改段描述符的BaseAddress的15:00
.text:00405A2E                 shr     eax, 10h
.text:00405A31                 mov     [ecx+3Ch], al   ; 修改段描述符的BaseAddress的23:16
.text:00405A34                 mov     [ecx+3Fh], ah   ; 修改段描述符的BaseAddress的31:24
.text:00405A37                 inc     dword ptr [esi+4Ch] ; KTHREAD(next).ContextSwitches++
.text:00405A3A                 inc     dword ptr [ebx+61Ch]
.text:00405A40                 pop     ecx
.text:00405A41                 mov     [ebx], ecx
.text:00405A43                 cmp     byte ptr [esi+49h], 0
.text:00405A47                 jnz     short loc_405A4D
.text:00405A49                 popf
.text:00405A4A                 xor     eax, eax
.text:00405A4C                 retn
.text:00405A4D ; ---------------------------------------------------------------------------
.text:00405A4D
.text:00405A4D loc_405A4D:                             ; CODE XREF: SwapContext+115↑j
.text:00405A4D                 popf
.text:00405A4E                 jnz     short loc_405A53
.text:00405A50                 mov     al, 1
.text:00405A52                 retn
.text:00405A53 ; ---------------------------------------------------------------------------
.text:00405A53
.text:00405A53 loc_405A53:                             ; CODE XREF: SwapContext+11C↑j
.text:00405A53                 mov     cl, 1
.text:00405A55                 call    ds:__imp_@HalRequestSoftwareInterrupt@4 ; HalRequestSoftwareInterrupt(x)
.text:00405A5B                 xor     eax, eax
.text:00405A5D                 retn
.text:00405A5E ; ---------------------------------------------------------------------------
.text:00405A5E
.text:00405A5E loc_405A5E:                             ; CODE XREF: SwapContext+B9↑j
.text:00405A5E                 mov     ebp, [ebx+3Ch]
.text:00405A61                 mov     eax, [edi+20h]
.text:00405A64                 mov     [ebp+48h], eax
.text:00405A67                 mov     eax, [edi+24h]
.text:00405A6A                 mov     [ebp+4Ch], eax
.text:00405A6D                 mov     eax, 48h ; 'H'
.text:00405A72                 mov     ebp, [ebx+38h]
.text:00405A75                 mov     ecx, [edi+28h]
.text:00405A78                 mov     [ebp+108h], ecx
.text:00405A7E                 mov     ecx, [edi+2Ch]
.text:00405A81                 mov     [ebp+10Ch], ecx
.text:00405A87                 jmp     loc_4059EF      ; 将LDT的段描述符存入ax中
.text:00405A8C ; ---------------------------------------------------------------------------
.text:00405A8C
.text:00405A8C loc_405A8C:                             ; CODE XREF: SwapContext+77↑j
.text:00405A8C                 mov     cr0, ecx
.text:00405A8F                 jmp     loc_4059B1      ; SegCs&20000h
.text:00405A8F                                         ; 判断是否是虚拟8086模式,如果不是,直接减掉
.text:00405A8F                                         ; +0x7c V86Es
.text:00405A8F                                         ; +0x80 V86Ds
.text:00405A8F                                         ; +0x84 V86Fs
.text:00405A8F                                         ; +0x88 V86Gs
.text:00405A8F                                         ;
.text:00405A8F                                         ; 如果是,那么就不减
.text:00405A8F                                         ;
.text:00405A8F                                         ; 这样做了之后,eax 就指向了0环栈顶,接下来就会存储到 TSS 里
.text:00405A8F                                         ; 以后这个线程进0环,不论是中断门还是快速调用,都会从 TSS 里获取 ESP0
.text:00405A94 ; ---------------------------------------------------------------------------
.text:00405A94
.text:00405A94 loc_405A94:                             ; CODE XREF: SwapContext+44↑j
.text:00405A94                 and     edx, 0FFFFFFF1h
.text:00405A97                 mov     ecx, [ebx+4]
.text:00405A9A                 cmp     ebp, edx
.text:00405A9C                 jz      short _ScPatchFxb
.text:00405A9E                 mov     cr0, edx
.text:00405AA1                 mov     ebp, edx
.text:00405AA1 SwapContext     endp ; sp-analysis failed

学习总结

  1. fs:[0]在3环中指向了TEB,但在0环中变为了KPCR,这是因为有修改基址的操作
  2. 会切换cr3寄存器,并且用的是0x44偏移位置的process(这个process是给进程提供资源的进程例如cr3,另外一个0x220是创建它的进程)

作业

逆向分析KiSwapContext和SwapContext函数,解决如下问题:
1、SwapContxt有几个参数,分别是什么?你是如何判断出来参数的?

ebx,esi,edi,esp
2、SwapContxt在哪里实现了线程切换?

mov     esp, [esi+_ETHREAD.Tcb.KernelStack] ; 此处是切换线程,切换线程本质是切换堆栈


3、线程切换的时候,会切换Cr3吗?切换Cr3的条件是什么?

会切换,是否为同一个进程

.text:004059CD                 mov     eax, [edi+44h]  ; eax=KTHREAD.ApcState.Process
.text:004059D0                 cmp     eax, [esi+44h]  ; KTHREAD(next).ApcState.Process 新老线程的process是否相同
.text:004059D3                 mov     byte ptr [edi+50h], 0 ; KTHREAD.IdleSwapBlock=0
.text:004059D7                 jz      short loc_405A19

 


4、中断门提权时,CPU会从TSS得到ESP0和SS0,TSS中存储的一定是当前线程的ESP0和SS0吗?如何做到的?

是的,因为切换线程的过程中对TSS的修改会屏蔽可屏蔽中断(CLI & STI)

.text:004059C0                 mov     [ecx+4], eax    ; 将0环的esp保存在TSS的esp中

 

但代码里好像只切换了esp0
5、FS:[0]在3环时指向TEB 但是 线程有很多 FS:[0]指向的是哪个线程的TEB 如何做到的?

会自己切换BaseAddress值,上面提到了
6、0环的ExceptionList在哪里备份的?

.text:00405950                 mov     ecx, [ebx]      ; mov ecx,ExceptionList
.text:00405952                 cmp     dword ptr [ebx+994h], 0
.text:00405959                 push    ecx

 


7、IdleThread是什么?什么时候执行?找到这个函数?

空闲线程,当没有线程运行时,就会运行这个函数,对SwapContext函数使用交叉引用即可
8、如何找到下一个就绪线程?

程序会调用KiFindReadyThread函数进行查找线程
9、模拟线程切换与Windows的线程切换有哪些区别?

本质上没有区别,都是通过切换堆栈来实现切换线程

 

 

感叹

总算可以看句柄表了  nice!!!不过还有个作业和几个函数还没逆。。。所以还是得逆一下

posted @ 2021-01-11 16:56  PYozo_free  阅读(651)  评论(1编辑  收藏  举报