首页
社区
课程
招聘
[原创]SwapContext函数逆向,好像没有发现线程切换之前保存当前寄存器的值
发表于: 2020-8-20 11:46 4081

[原创]SwapContext函数逆向,好像没有发现线程切换之前保存当前寄存器的值

2020-8-20 11:46
4081

kd> u KiSwapThread
nt!KiSwapThread:
80501c90 8bff mov edi,edi
80501c92 56 push esi
80501c93 57 push edi
80501c94 3ea120f0dfff mov eax,dword ptr ds:[FFDFF020h] KPRCR 0FFSET=0x20得到KPRCB地址
80501c9a 8bf0 mov esi,eax
80501c9c 8b4608 mov eax,dword ptr [esi+8] [esi+8]取出NextThread的成员的值到eax
80501c9f 85c0 test eax,eax 判断eax是否等于0
80501ca1 8b7e04 mov edi,dword ptr [esi+4] [esi+4]取出CurrentThread成员的值到edi
kd> u
nt!KiSwapThread+0x14: 如果不为NULL把NextThread指向的线程作为切换线程,位NULL则调用KiFindReadyThread函数找那32条链表
80501ca4 7406 je nt!KiSwapThread+0x1c (80501cac) 如果eax=0 NextThread成员位NULL;
80501ca6 83660800 and dword ptr [esi+8],0 eax!=0 把NextThread成员置为NULL 跳转过去
80501caa eb23 jmp nt!KiSwapThread+0x3f (80501ccf)
80501cac 53 push ebx <<<<====
80501cad 0fbe5e10 movsx ebx,byte ptr [esi+10h]
80501cb1 33d2 xor edx,edx
80501cb3 8bcb mov ecx,ebx //似乎是 fastcall ecx edc作为参数入栈
80501cb5 e8cafbffff call nt!KiFindReadyThread (80501884) 只有NextThread成员=NULL,找不到下一个线程在执行这个
kd> u
nt!KiSwapThread+0x2a:
80501cba 85c0 test eax,eax eax==0 没有找到线程
80501cbc 7510 jne nt!KiSwapThread+0x3e (80501cce) 不等于0跳转
80501cbe 8b460c mov eax,dword ptr [esi+0Ch] eax=IdleThread成员的值
80501cc1 33d2 xor edx,edx edx=0
80501cc3 42 inc edx dex=1
80501cc4 8bcb mov ecx,ebx
80501cc6 d3e2 shl edx,cl
80501cc8 091584bd5480 or dword ptr [nt!KiIdleSummary (8054bd84)],edx
kd> u
nt!KiSwapThread+0x3e:
80501cce 5b pop ebx
80501ccf 8bc8 mov ecx,eax NextThread不等于0直接跳着里 eax记录这ETHREAD指针
80501cd1 e80e0b0400 call nt!KiSwapContext (805427e4)
80501cd6 84c0 test al,al
80501cd8 8a4f58 mov cl,byte ptr [edi+58h]
80501cdb 8b7f54 mov edi,dword ptr [edi+54h]
80501cde 8b351c874d80 mov esi,dword ptr [nt!_imp_KfLowerIrql (804d871c)]
80501ce4 7410 je nt!KiSwapThread+0x66 (80501cf6)

 

kd> u KiSwapContext fastcall调用约定,寄存器传参,因为线程切换的非常频繁
nt!KiSwapContext:
805427e4 83ec10 sub esp,10h 提升堆栈保护现场
805427e7 895c240c mov dword ptr [esp+0Ch],ebx
805427eb 89742408 mov dword ptr [esp+8],esi
805427ef 897c2404 mov dword ptr [esp+4],edi
805427f3 892c24 mov dword ptr [esp],ebp
805427f6 8b1d1cf0dfff mov ebx,dword ptr ds:[0FFDFF01Ch] ebx=KPCR的首地址
805427fc 8bf1 mov esi,ecx esi=ecx 此时ecx等于NextThread(下一个线程的)的ETHREAD结构的地址
805427fe 8bbb24010000 mov edi,dword ptr [ebx+124h] edi指向当前的KTHREAD结构
kd> u
nt!KiSwapContext+0x20:
80542804 89b324010000 mov dword ptr [ebx+124h],esi 把KPCR当前线程结构(CurrentThread成员)指向为 NextThread值的或者其他的
8054280a 8a4f58 mov cl,byte ptr [edi+58h]
8054280d e8ce000000 call nt!SwapContext (805428e0)
80542812 8b2c24 mov ebp,dword ptr [esp]
80542815 8b7c2404 mov edi,dword ptr [esp+4]
80542819 8b742408 mov esi,dword ptr [esp+8]
8054281d 8b5c240c mov ebx,dword ptr [esp+0Ch]
80542821 83c410 add esp,10h
kd> u
nt!KiSwapContext+0x40:
80542824 c3 ret

 

kd> u SwapContext
nt!SwapContext:
805428e0 0ac9 or cl,cl
805428e2 26c6462d02 mov byte ptr es:[esi+2Dh],2 KTHREAD 0FFSET =0x2D 存储着线程的状态
805428e7 9c pushfd
805428e8 8b0b mov ecx,dword ptr [ebx] ebx=KPCR ecx=线程的ExceptionList异常链表指针
805428ea 83bb9409000000 cmp dword ptr [ebx+994h],0 KPRCB的0x874的DpcRoutineActive成员
805428f1 51 push ecx 将异常链表指针存入堆栈
805428f2 0f8535010000 jne nt!SwapContext+0x14d (80542a2d)
805428f8 833d0cbf558000 cmp dword ptr [nt!PPerfGlobalGroupMask (8055bf0c)],0
kd> u
nt!SwapContext+0x1f:
805428ff 0f85ff000000 jne nt!SwapContext+0x124 (80542a04)
80542905 0f20c5 mov ebp,cr0
80542908 8bd5 mov edx,ebp edx=Cr0
8054290a 8a4e2c mov cl,byte ptr [esi+2Ch] 0x2C NextThread的DebugActive成员 ESI指向的NextThread哦
8054290d 884b50 mov byte ptr [ebx+50h],cl NextThread的DebugActive成员写入KPCR的0x50处DebugActive

 

80542910 fa cli //禁止中断发送
80542911 896728 mov dword ptr [edi+28h],esp edi没有切换前的线程KThread结构 KernelStack ESP0 内核的堆栈的ESP0 将切换前的ESP0保存
80542914 8b4618 mov eax,dword ptr [esi+18h] NextThread的0x18 是EBP保存在eax中
kd> u
nt!SwapContext+0x37:
80542917 8b4e1c mov ecx,dword ptr [esi+1Ch] NextThread的StackLimit保存到ecx
8054291a 2d10020000 sub eax,210h eax-=0x210 需要0x210个字节存储 浮点寄存器 此时eax指向FTrap_Frame结构底部
8054291f 894b08 mov dword ptr [ebx+8],ecx ecx写入KPCR的 _NT_TIB 的StackLimit成员中
80542922 894304 mov dword ptr [ebx+4],eax eax写入KPCR的 _NT_TIB 的StackBase
80542925 33c9 xor ecx,ecx ecx=0
80542927 8a4e31 mov cl,byte ptr [esi+31h] cl=NextThread.NpxState
8054292a 83e2f1 and edx,0FFFFFFF1h 低7位清空
8054292d 0bca or ecx,edx 判断NextThread.NpxState最高位是否为0
kd> u
nt!SwapContext+0x4f:
8054292f 0b880c020000 or ecx,dword ptr [eax+20Ch]
80542935 3be9 cmp ebp,ecx
80542937 0f85bf000000 jne nt!SwapContext+0x11c (805429fc)
8054293d 8d4900 lea ecx,[ecx]
80542940 f740e400000200 test dword ptr [eax-1Ch],20000h eax指向FTrap_Frame最底部 [eax-0x1c]指向Eflag寄存器 判断Eflag寄存器的17位是否为1 为1支持虚拟8086模式
80542947 7503 jne nt!SwapContext+0x6c (8054294c)
80542949 83e810 sub eax,10h eax-0x10跳过FTrap_Frame虚拟8086模式指向FTrap_Frame.V86Es
8054294c 8b4b40 mov ecx,dword ptr [ebx+40h] ebx指向KPCR ebx+0x40指向TSS ecx存储TSS地址
kd> u
nt!SwapContext+0x6f: (填充TSS ESP0 TSS一共填入了3个值 1 ESP0, 2 CR3(只有源线程和切换线程父进程不同才填充) 3往TSS的0x66处填充一个值)
(切换线程的本质是切换堆栈) 这里意味着,TSS中的ESP0,SS0永远都是当前线程的
8054294f 894104 mov dword ptr [ecx+4],eax [TSS+0x4]=eax =FTrap_Frame.V86Es的地址 TSS+4就是ESP0 指向FTrap_Frame的FTrap_Frame.V86Es成员
80542952 8b6628 mov esp,dword ptr [esi+28h] esp=[NextThread+0x28] KernelStack 切换堆栈
80542955 8b4620 mov eax,dword ptr [esi+20h] eax=[NextThread+0x20]存储这TEB
80542958 894318 mov dword ptr [ebx+18h],eax 目标线程TEB地址存储在 KPCR+18处
8054295b fb sti
8054295c 8b4744 mov eax,dword ptr [edi+44h]
8054295f 3b4644 cmp eax,dword ptr [esi+44h] 当前线程与下一个线程结构的 KTHREAD的0x44成员比较判断这两个进程父进程EPROCESS结构是否相同 妙妙
80542962 c6475000 mov byte ptr [edi+50h],0
kd> u
nt!SwapContext+0x86:
80542966 742c je nt!SwapContext+0xb4 (80542994) 两个线程的父进程相同就跳转
80542968 8b7e44 mov edi,dword ptr [esi+44h] 获得切换目标线程的进程 EPROCESS结构首地址
8054296b 66f74720ffff test word ptr [edi+20h],0FFFFh
80542971 755b jne nt!SwapContext+0xee (805429ce)
80542973 33c0 xor eax,eax
80542975 0f00d0 lldt ax
80542978 33c0 xor eax,eax eax=0
8054297a 8ee8 mov gs,ax gs=0;
kd> u
nt!SwapContext+0x9c: 父进程不相同切换CR3(填充TSS CR3)
8054297c 8b4718 mov eax,dword ptr [edi+18h] eax=[EPROCESS+0x18]=Cr3 取出该进程的CR3
8054297f 8b6b40 mov ebp,dword ptr [ebx+40h] ebp=[ebx+0x40]指向TSS
80542982 8b4f30 mov ecx,dword ptr [edi+30h]
80542985 89451c mov dword ptr [ebp+1Ch],eax 将该进程的Cr3存储到TSS的0x1C处
80542988 0f22d8 mov cr3,eax 切换进程(切换Cr3就是切换进程)
8054298b 66894d66 mov word ptr [ebp+66h],cx
8054298f eb03 jmp nt!SwapContext+0xb4 (80542994)
80542991 8d4900 lea ecx,[ecx]

 

kd> u
nt!SwapContext+0xb4: 两个线程的父进程相同跳转到这里
80542994 8b4318 mov eax,dword ptr [ebx+18h] <<<<<=======eax取出目标线程TEB
80542997 8b4b3c mov ecx,dword ptr [ebx+3Ch] ecx=[KPCR+0x3C]=GDT表地址
8054299a 6689413a mov word ptr [ecx+3Ah],ax
8054299e c1e810 shr eax,10h
805429a1 88413c mov byte ptr [ecx+3Ch],al
805429a4 88613f mov byte ptr [ecx+3Fh],ah
805429a7 ff464c inc dword ptr [esi+4Ch]
805429aa ff831c060000 inc dword ptr [ebx+61Ch]
kd> u
nt!SwapContext+0xd0:
805429b0 59 pop ecx 取出存储堆栈的异常链表指针
805429b1 890b mov dword ptr [ebx],ecx 存入 [KPCR]中
805429b3 807e4900 cmp byte ptr [esi+49h],0
805429b7 7504 jne nt!SwapContext+0xdd (805429bd)
805429b9 9d popfd
805429ba 33c0 xor eax,eax
805429bc c3 ret
805429bd 9d popfd
kd> u
nt!SwapContext+0xde:
805429be 7503 jne nt!SwapContext+0xe3 (805429c3)
805429c0 b001 mov al,1
805429c2 c3 ret
805429c3 b101 mov cl,1
805429c5 ff1500874d80 call dword ptr [nt!_imp_HalRequestSoftwareInterrupt (804d8700)]
805429cb 33c0 xor eax,eax
805429cd c3 ret
805429ce 8b6b3c mov ebp,dword ptr [ebx+3Ch]
kd> u
nt!SwapContext+0xf1:
805429d1 8b4720 mov eax,dword ptr [edi+20h]
805429d4 894548 mov dword ptr [ebp+48h],eax
805429d7 8b4724 mov eax,dword ptr [edi+24h]
805429da 89454c mov dword ptr [ebp+4Ch],eax
805429dd b848000000 mov eax,48h
805429e2 8b6b38 mov ebp,dword ptr [ebx+38h]
805429e5 8b4f28 mov ecx,dword ptr [edi+28h]
805429e8 898d08010000 mov dword ptr [ebp+108h],ecx
kd> u
nt!SwapContext+0x10e:
805429ee 8b4f2c mov ecx,dword ptr [edi+2Ch]
805429f1 898d0c010000 mov dword ptr [ebp+10Ch],ecx
805429f7 e979ffffff jmp nt!SwapContext+0x95 (80542975)
805429fc 0f22c1 mov cr0,ecx
805429ff e93cffffff jmp nt!SwapContext+0x60 (80542940)
80542a04 a10cbf5580 mov eax,dword ptr [nt!PPerfGlobalGroupMask (8055bf0c)]
80542a09 83f800 cmp eax,0
80542a0c 0f84f3feffff je nt!SwapContext+0x25 (80542905)
kd> u
nt!SwapContext+0x132:
80542a12 8bd6 mov edx,esi
80542a14 8bcf mov ecx,edi
80542a16 f7400404000000 test dword ptr [eax+4],4
80542a1d 0f84e2feffff je nt!SwapContext+0x25 (80542905)
80542a23 e8aaa81100 call nt!WmiTraceContextSwap (8065d2d2)
80542a28 e9d8feffff jmp nt!SwapContext+0x25 (80542905)
80542a2d 68b8000000 push 0B8h
80542a32 e84572fbff call nt!KeBugCheck (804f9c7c)
kd> u
nt!SwapContext+0x157:
80542a37 c3 ret

 

80542952 8b6628 mov esp,dword ptr [esi+28h]这里堆栈切换了,线程也就切换了,esp修改之前好像没有把寄存器的值都拷贝一份存储在堆栈或是哪里啊,那下一次切换到这个线程?还是SwapContext这个函数寄存器的值不用保存,用的都是上一个线程的??求解


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 1
支持
分享
最新回复 (3)
雪    币: 3322
活跃值: (3918)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
2
保存在被切换的线程的堆栈了
2020-8-22 15:51
0
雪    币: 167
活跃值: (876)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
Mr.hack 保存在被切换的线程的堆栈了
805427e7 895c240c                mov     dword ptr [esp+0Ch],ebx        
805427eb 89742408                mov     dword ptr [esp+8],esi
805427ef 897c2404                mov     dword ptr [esp+4],edi
805427f3 892c24                  mov     dword ptr [esp],ebp,大哥只保存了这几个吧,其他的好像不需要保存
2020-8-23 13:23
0
雪    币: 3738
活跃值: (3872)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4

我以前也有这个困惑,后来想明白了。线程切换分为主动切换和被动切换。被动切换好说,被保存在TRAP_FRAME里。一个内核线程主动切换就更好说了,保存该保存的,用不着保存的还保存它干嘛。

最后于 2020-9-6 19:21 被fengyunabc编辑 ,原因: 表达有误
2020-9-6 01:52
0
游客
登录 | 注册 方可回帖
返回
//