首页
社区
课程
招聘
[原创] Windows线程恢复流程逆向分析
2023-5-9 16:29 16646

[原创] Windows线程恢复流程逆向分析

2023-5-9 16:29
16646

目录

目录

前言

这线程恢复真的是比线程挂起麻烦的多,挂起线程插个apc就结了,线程恢复这块流程和分支非常多,wrk在这块的源码也不全,本次分析的流程和分支仅按照下方测试代码进行,不考虑其它情况;调试的操作系统是WindowsXP,单核

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
VOID WINAPI testThreadA() {
    while (TRUE) {
        Sleep(1000);
        printf("AAA\n");
    }
}
HANDLE threadA = -1;
int main()
{
    threadA = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)testThreadA, 0, 0, 0);
    SuspendThread(threadA);
    __asm {
        int 3;
    }
    ResumeThread(threadA);
    system("pause");
    return 0;
}

基本流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ResumeThread(ThreadHandle);
    //
NtResumeThread(ThreadHandle);
    //
PsResumeThread(Thread);
    //
KeResumeThread(Thread);
    //
KiWaitTest(Thread->SuspendSemaphore,increment(0));
    //
KiUnwaitThread(increment(0),OUT &waitListEntry);
    //
KiUnlinkThread(Thread,increment(0));//断链WaitBlockList
    //
KiReadyThread(); //挂入调度链表

调用关系

1
2
KiWaitTest  -> KiUnwaitThread -> KiUnlinkThread
            -> KiReadyThread

函数分析

以下操作流程用伪代码表示(非IDA)(if均成立)

KeResumeThread

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int __stdcall KeResumeThread(_ETHREAD *Thread)
{
  char SuspendCount = Thread->Tcb.SuspendCount;
  if ( SuspendCount )//如果没调用过SuspendThread()挂起线程,该值为0
  {
    char newSuspendCount = SuspendCount - 1;
    Thread->Tcb.SuspendCount = newSuspendCount;
    if ( !newSuspendCount && !Thread->Tcb.FreezeCount )
    {
      ++Thread->Tcb.SuspendSemaphore.Header.SignalState;
      KiWaitTest(&Thread->Tcb.SuspendSemaphore, 0);
    }
  }
  KiUnlockDispatcherDatabase(KeRaiseIrqlToDpcLevel());
  return;
}

KiWaitTest

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
__fastcall KiWaitTest(_KEVENT *Event, _KTHREAD *Increment)
{
    LIST_ENTRY waitListEntry;
    waitListEntry.Blink = &waitListEntry;
    waitListEntry.Flink = &waitListEntry;
 
    PLIST_ENTRY ListHead = &Event->Header.WaitListHead;
    PKWAIT_BLOCK = WaitEntry = Event->Header.WaitListHead.Flink;
    if(Event->Header.SignalState > 0){
        if ( WaitEntry != ListHead ){
            PKTHREAD Thread = WaitEntry->Thread;
            if(WaitEntry->WaitType == WaitAny){
                USHORT waitStatus = WaitEntry->WaitKey;
                UCHAR Type = Event->Header.Type;
            }
            //获得一个需要挂入调度链表的就绪线程链表
            KiUnwaitThread(Thread, waitStatus, increment, &waitListEntry);
            WaitEntry = ListHead->Flink;
            while(IsListEmpty(&waitListEntry)){
                PVOID nextThread = waitListEntry.Flink;
                WaitEntry = &waitListEntry;
                waitListEntry.Flink = waitListEntry.Flink->Flink;
                waitListEntry.Flink->Blink = &waitListEntry;
                KiReadyThread((KTHREAD *)(nextThread - 96));
            }
        }
    }
}

KiUnwaitThread()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
KiUnwaitThread(_KTHREAD *thread, LONG_PTR WaitStatus, int Increment, OUT _LIST_ENTRY *WaitListEntry){
    //将线程的等待块摘除
    KiUnlinkThread(thread, WaitStatus);
    CHAR Priority = thread->Priority;
    KPROCESS Process = thread->ApcState.Process;
    if ( Priority >= 16 ){
        if(WaitListEntry != NULL){
            PLIST_ENTRY prev = WaitListEntry->Blink;
            PLIST_ENTRY waitList = &thread->WaitListEntry;
            thread->WaitListEntry.Flink = WaitListEntry;
            thread->WaitListEntry.Blink = Blink;
            prev->Flink = &thread->WaitListEntry;
            WaitListEntry->Blink = &thread->WaitListEntry;
            return;
        }
    }
}

KiUnlinkThread()

1
2
3
4
5
6
7
8
9
10
11
12
BOOL __fastcall KiUnlinkThread(_KTHREAD *Thread, LONG_PTR WaitStatus){
    Thread->WaitStatus |= WaitStatus;
    WaitBlockList = Thread->WaitBlockList;
    do{//将Thread从等待链中断链
        PLIST_ENTRY Flink = WaitBlockList->WaitListEntry.Flink;
        PLIST_ENTRY Blink = WaitBlockList->WaitListEntry.Blink;
        Blink->Flink = WaitBlockList->WaitListEntry.Flink;
        Flink->Blink = Blink;
        WaitBlockList = WaitBlockList->NextWaitBlock;
    }while ( WaitBlockList != Thread->WaitBlockList );
    return;
}

KiReadyThread()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
KiReadyThread(_KTHREAD *Thread){
//设置字段属性
    Thread->State = Standby;
    Thread->WaitTime = KeTickCount
    Thread->NextProcessor = Thread->IdleProcessor
    Thread->State = Ready;
 
//将线程挂入调度链表
    Thread->WaitListEntry.Flink = KiDispatcherReadyListHead
    Thread->WaitListEntry.Blink = KiDispatcherReadyListHead.Blink
    KiDispatcherReadyListHead.Blink.Flink = Thread->WaitListEntry
    KiDispatcherReadyListHead.Blink = Thread->WaitListEntry
    KiReadySummary |= 1 << Thread->priority;
}

KiReadyThread() /汇编/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
nt!KiReadyThread:
 mov     edi,edi
 push    ebp
 mov     ebp,esp
 push    ecx
 push    ecx
 push    ebx
 push    esi
 mov     esi,ecx        ;esi = ecx = KTHREAD = ReadyThread
 lea     ecx,[esi+128h]    ;ecx = PCHAR Preempted = &_KTHREAD->Preempted
 mov     al,byte ptr [ecx]    ;al = *Preempted;
 mov     byte ptr [ecx],0    ;*Preempted = 0;
 mov     ecx,dword ptr [nt!KeTickCount (8055b000)]
 push    edi
 mov     edi,dword ptr [esi+44h]    ;esi = _KTHREAD->APC_STATE->Process
 mov     byte ptr [ebp-1],al
 movsx   eax,byte ptr [esi+33h]        ;eax = _KTHREAD->Priority
 mov     dword ptr [esi+68h],ecx    ;_KTHREAD->WaitTime = KeTickCount
 
nt!KiReadyThread+0x12a:
 cmp     byte ptr [edi+65h],0                ;if
 jne     nt!KiReadyThread+0x134 ;_KPROCESS->State != STANDBY(0)
 
nt!KiReadyThread+0x2f:
 cmp     byte ptr [esi+12Ah],0                ;if
 je      nt!KiReadyThread+0x17d ;_KTHREAD->KernelStackResident == FALSE 
 
nt!KiReadyThread+0x3c:
 movzx   ecx,byte ptr [esi+1BAh]        ;ecx = _KTHREAD->IdealProcessor
 mov     edi,dword ptr [esi+124h]        ;edi = _KTHREAD->Affinity
 mov     byte ptr [esi+2Dh],3            ;_KTHREAD->State = ProcessInSwap(3)
 mov     edx,dword ptr nt!KiProcessorBlock (805633c0)[ecx*4]    ;edx = KPRCB
 mov     ebx,dword ptr [edx+4D0h]        ;ebx = _KPRCB->MultiThreadProcessorSet
 mov     edx,dword ptr [esi+120h]        ;edx = _KTHREAD->SoftAffinity
 and     edx,edi                        ;if
 je      nt!KiReadyThread+0x66 (804dd6c2)  ;Affinity == SoftAffinity
 
nt!KiReadyThread+0x64:
 mov     edi,edx        ;Affinity = SoftAffinity
 
nt!KiReadyThread+0x66:
 mov     edx,dword ptr [nt!KiIdleSummary (8055ae80)];edx = 调度链表数组
 and     edx,edi        ;根据cpu编号找到对应调度链表,edx=调度链表
 jne     nt!KiReadyThread+0x1aa (804e8755)  ;如果调度链表不为空,跳转
 
nt!KiReadyThread+0x74:
 xor     edx,edx    ;edx = 0
 inc     edx        ;edx = 1
 mov     ebx,edx    ;
 shl     ebx,cl        ;ebx << cpu
 test    edi,ebx    ;edi = Affinity & (1 << cpu)
 je      nt!KiReadyThread+0x7f (8051992b)  ; if(! edi & ebx)
 
nt!KiReadyThread+0xb4:
 mov     byte ptr [esi+12Bh],cl        ;_KTHREAD->NextProcessor = ecx
 mov     ebx,dword ptr nt!KiProcessorBlock (805633c0)[ecx*4]    ;ebx = KPRCB
 mov     edi,dword ptr [ebx+8]        ;edi = NextThread
 test    edi,edi                            ;if
 jne     nt!KiReadyThread+0xcc (804e6ea0)      ;NextThread != NULL
 
nt!KiReadyThread+0x2d1:
 mov     ecx,dword ptr [ebx+4]                ;ecx = CurrentThread
 movsx   edi,byte ptr [ecx+33h]                ;edi = _KTHREAD->Priority
 cmp     eax,edi                            ;if
 jg      nt!KiReadyThread+0x2dc ;ReadyThread->Priority > CurrentThread->Priority
 
nt!KiReadyThread+0x307:
 mov     byte ptr [esi+2Dh],1                ;_KTHREAD->STATE = READY(1)
 add     esi,60h                            ;esi = &_KTHREAD->WaitListEntry
 cmp     byte ptr [ebp-1],0                    ; if _KTHREAD->Preetempt != 0
 lea     ecx,nt!KiDispatcherReadyListHead (80563da0)[eax*8]    ;ecx = 调度链表
 jne     nt!KiReadyThread+0x31b (804dd641)  ;_KTHREAD->Preetempt ! 0
 
nt!KiReadyThread+0x329:    ;将ReadyThread挂入调度链表
 mov     edi,dword ptr [ecx+4]    ;edi = 调度链表->Blink
 mov     dword ptr [esi],ecx    ;_KTHREAD->WaitListEntry.Flink = 调度链表
 mov     dword ptr [esi+4],edi    ;_KTHREAD->WaitListEntry.Blink = 调度链表.Blink
 mov     dword ptr [edi],esi    ;调度链表.Blink.Flink = _KTHREAD->WaitListEntry
 mov     dword ptr [ecx+4],esi    ;调度链表.Blink = _KTHREAD->WaitListEntry
 
nt!KiReadyThread+0x336:
 mov     ecx,eax    ;ecx = ReadyThread->Priority
 shl     edx,cl        ;edx = cpu
 or      dword ptr [nt!KiReadySummary (8055ae88)],edx
 
nt!KiReadyThread+0x340:
 pop     edi
 pop     esi
 pop     ebx
 leave
 ret

结语

如分析有错,欢迎指出


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

上传的附件:
收藏
点赞9
打赏
分享
最新回复 (10)
雪    币: 6
活跃值: (2925)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
咖啡_741298 2023-5-9 16:38
2
0
很好,有x64的就更好了-_-
雪    币: 1782
活跃值: (2876)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
LeaMov 2 2023-5-9 16:49
3
1
咖啡_741298 很好,有x64的就更好了-_-
x32还没学完
雪    币: 4427
活跃值: (3454)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
木志本柯 2023-5-9 17:53
4
0
调度链表最后是怎么取出线程来执行的呢?
雪    币: 1782
活跃值: (2876)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
LeaMov 2 2023-5-9 18:16
5
0
木志本柯 调度链表最后是怎么取出线程来执行的呢?
在这里只是将它挂到表里,进入就绪状态,取是在KiFindReadyThread()里处理的,等系统切换
雪    币: 1782
活跃值: (2876)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
LeaMov 2 2023-5-9 18:18
6
1
还有一个抢占的情况,优先级大于原定的下一个线程的话,会把下一个线程挤下去,在系统调用KiSwapThread()的时候就不走KiFindReadyThread()去调度链表找了,直接找KPRCB->NextThread
雪    币: 1782
活跃值: (2876)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
LeaMov 2 2023-5-9 18:29
7
1
木志本柯 调度链表最后是怎么取出线程来执行的呢?
抢占的分支你可以看一下KiFindReadyThread()的汇编,nt!KiReadyThread+0x2d1处,我刚刚重新看了一下,比较的是 准备就绪的线程ReadyThread,也就是ResumeThread传入的线程,它的优先级和当前CPU正在运行的线程,KPRCB->CurrentThread的优先级作比较
 if(ReadyThread->Priority > CurrentThread->Priority){
    CurrentThread->Preempted = 1;
    kprcb->NextThread = ReadyThread;
    return;(进入这个分支后面就不挂调度链表了)
}
雪    币: 7910
活跃值: (2632)
能力值: ( LV9,RANK:180 )
在线值:
发帖
回帖
粉丝
layerfsd 4 2023-5-10 10:03
8
0
2003泄露后,wrk没什么意义了
雪    币: 204
活跃值: (714)
能力值: ( LV9,RANK:195 )
在线值:
发帖
回帖
粉丝
palkiver 2023-5-10 16:08
9
0
加油,来个win10、win11的
雪    币: 3350
活跃值: (3372)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
fengyunabc 1 2023-5-10 17:01
10
0
感谢分享!
雪    币: 1782
活跃值: (2876)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
LeaMov 2 2023-5-12 11:26
11
1
我重新翻阅了WindowsXP的源码,关于KiUnwaitThread函数的补充定义如下:
KiUnwaitThread (
    IN PRKTHREAD Thread,
    IN LONG_PTR WaitStatus,
    IN KPRIORITY Increment,
    IN PLIST_ENTRY ThreadList OPTIONAL
    )
最后一个参数是一个线程链表指针,如果传入了该值,则会根据传入线程的等待链填充该链表,如果没有,会直接拿着传入线程调用KiReadyThread(thread);
游客
登录 | 注册 方可回帖
返回