逆向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
学习总结
- fs:[0]在3环中指向了TEB,但在0环中变为了KPCR,这是因为有修改基址的操作
- 会切换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!!!不过还有个作业和几个函数还没逆。。。所以还是得逆一下