-
-
[原创]Windows内核学习笔记之等待机制
-
发表于: 2022-1-9 16:42 10446
-
一.分发器对象
在Windows系统中,一个线程可以通过等待一个或者多个可等待对象,从而让线程进入等待状态,而其他的线程则可以在一些特定时刻可以唤醒这些等待这些可等待对象的线程。这些可等待对象也就是分发器对象,在Windows内核中,凡是以DISPATCH_HEADER结构开头的对象都是分发器对象,该结构定义如下:
1 2 3 4 5 6 7 8 | kd> dt _DISPATCHER_HEADER nt!_DISPATCHER_HEADER + 0x000 Type : UChar + 0x001 Absolute : UChar + 0x002 Size : UChar + 0x003 Inserted : UChar + 0x004 SignalState : Int4B + 0x008 WaitListHead : _LIST_ENTRY |
名称 | 作用 |
---|---|
Type | 代表该对象的类型 |
SignalState | 代表该分发器对象的信号状态 |
WaitListHead | 双向链表头,此链表包含了所有正在等待该分发器对象的线程 |
以下是Windows中所有分发器的基本信息。
前三种是比较常见的三种分发器对象,接下来将对这三种分发器对象进行介绍。
二.事件对象
事件对象_KEVENT的定义如下:
1 2 3 | kd> dt _KEVENT ntdll!_KEVENT + 0x000 Header : _DISPATCHER_HEADER |
由于事件对象只有一个DISPATCH_HEADER结构,因此它的初始化也比较简单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | .text: 00410D36 ; void __stdcall KeInitializeEvent(PRKEVENT Event, EVENT_TYPE Type , BOOLEAN State) .text: 00410D36 public _KeInitializeEvent@ 12 .text: 00410D36 _KeInitializeEvent@ 12 proc near ; CODE XREF: KeInitializeEventPair(x) + 1B ↓p .text: 00410D36 ; KeInitializeEventPair(x) + 28 ↓p .text: 00410D36 .text: 00410D36 Event = dword ptr 8 .text: 00410D36 Type = dword ptr 0Ch .text: 00410D36 State = byte ptr 10h .text: 00410D36 .text: 00410D36 mov edi, edi .text: 00410D38 push ebp .text: 00410D39 mov ebp, esp .text: 00410D3B mov eax, [ebp + Event] .text: 00410D3E mov cl, byte ptr [ebp + Type ] .text: 00410D41 mov [eax + _KEVENT.Header. Type ], cl .text: 00410D43 movzx ecx, [ebp + State] .text: 00410D47 mov [eax + _KEVENT.Header.Size], 4 .text: 00410D4B mov [eax + _KEVENT.Header.SignalState], ecx .text: 00410D4E add eax, 8 ; eax指向WaitListHead .text: 00410D51 mov [eax + LIST_ENTRY.Blink], eax .text: 00410D54 mov [eax + LIST_ENTRY.Flink], eax .text: 00410D56 pop ebp .text: 00410D57 retn 0Ch .text: 00410D57 _KeInitializeEvent@ 12 endp |
将事件对象设为有信号状态是通过KeSetEvent函数来实现的,该函数首先会判断是否有等待该分发器对象的线程,如果没有则直接修改事件对象的信号状态为有信号状态(1)。
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 | .text: 0040AFF9 ; LONG __stdcall KeSetEvent(PRKEVENT Event, KPRIORITY Increment, BOOLEAN Wait) .text: 0040AFF9 public _KeSetEvent@ 12 .text: 0040AFF9 _KeSetEvent@ 12 proc near ; CODE XREF: MiInsertPageInFreeList(x) + 4ED ↓p .text: 0040AFF9 ; IopfCompleteRequest(x,x) + 9912 ↓p ... .text: 0040AFF9 .text: 0040AFF9 Event = dword ptr 8 .text: 0040AFF9 Increment = dword ptr 0Ch .text: 0040AFF9 Wait = byte ptr 10h .text: 0040AFF9 .text: 0040AFF9 .text: 0040AFF9 mov edi, edi .text: 0040AFFB push ebp .text: 0040AFFC mov ebp, esp .text: 0040AFFE push ebx .text: 0040AFFF push esi .text: 0040B000 push edi .text: 0040B001 xor ecx, ecx .text: 0040B003 call ds:__imp_@KeAcquireQueuedSpinLockRaiseToSynch@ 4 ; KeAcquireQueuedSpinLockRaiseToSynch(x) .text: 0040B009 mov ecx, [ebp + Event] .text: 0040B00C mov edi, [ecx + KEVENT.Header.SignalState] ; 将分发器信号状态赋给edi .text: 0040B00F mov bl, al .text: 0040B011 lea eax, [ecx + KEVENT.Header.WaitListHead] .text: 0040B014 mov esi, [eax + LIST_ENTRY.Flink] .text: 0040B016 cmp esi, eax ; 判断是否有等待的线程 .text: 0040B018 jnz short loc_40B03C .text: 0040B01A mov [ecx + KEVENT.Header.SignalState], 1 ; 将信号状态修改为有信号状态 |
如果有在等待该事件对象的线程,则判断事件对象是事件通知对象还是事件同步对象(1为事件同步对象,0为事件通知对象)。如果是事件通知状态,则通过调用KiWaitTest唤醒所有正在等待该事件对象的线程并把信号状态改为有信号状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | .text: 0040B03C loc_40B03C: ; CODE XREF: KeSetEvent(x,x,x) + 1F ↑j .text: 0040B03C xor eax, eax .text: 0040B03E inc eax ; eax = 1 .text: 0040B03F cmp [ecx + KEVENT.Header. Type ], 0 ; 判断为事件通知对象还是事件同步对象 .text: 0040B042 jnz loc_40ED61 .text: 0040B048 .text: 0040B048 loc_40B048: ; CODE XREF: KeSetEvent(x,x,x) + 3D6C ↓j .text: 0040B048 test edi, edi ; 判断是否有分发器信号 .text: 0040B04A jnz short loc_40B021 .text: 0040B04C mov edx, [ebp + Increment] .text: 0040B04F mov [ecx + KEVENT.Header.SignalState], eax ; 修改信号状态为有信号状态 .text: 0040B052 call @KiWaitTest@ 8 ; KiWaitTest(x,x) .text: 0040B057 jmp short loc_40B021 .text: 0040B057 _KeSetEvent@ 12 endp |
否则就调用KiUnwaitThread唤醒一个正在等待该事件对象的事件对象,此时不将信号状态设为有信号状态
1 2 3 4 5 6 7 8 9 | .text: 0040ED61 loc_40ED61: ; CODE XREF: KeSetEvent(x,x,x) + 49 ↑j .text: 0040ED61 cmp [esi + _KWAIT_BLOCK.WaitType], ax .text: 0040ED65 jnz loc_40B048 .text: 0040ED6B movzx edx, [esi + _KWAIT_BLOCK.WaitKey] .text: 0040ED6F mov ecx, [esi + _KWAIT_BLOCK.Thread] .text: 0040ED72 push 0 .text: 0040ED74 push [ebp + Increment] .text: 0040ED77 call @KiUnwaitThread@ 16 ; KiUnwaitThread(x,x,x,x) .text: 0040ED7C jmp loc_40B021c_40B021 |
三.信号量对象
信号量对象_KSEMAPHORE的定义如下:
1 2 3 4 | kd> dt _KSEMAPHORE ntdll!_KSEMAPHORE + 0x000 Header : _DISPATCHER_HEADER + 0x010 Limit : Int4B |
由于信号量对象可以让多个线程共享一个资源,因此信号量对象增加了Limit成员,该成员用来指明信号量计数器的最大值。在信号量初始化中,会为该成员赋值,且此时的信号量由用户指定,可以不止为1。
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 | .text: 00411859 ; void __stdcall KeInitializeSemaphore(PRKSEMAPHORE Semaphore, LONG Count, LONG Limit) .text: 00411859 public _KeInitializeSemaphore@ 12 .text: 00411859 _KeInitializeSemaphore@ 12 proc near ; CODE XREF: ExpAllocateSharedWaiterSemaphore(x,x) + 32 ↓p .text: 00411859 ; ExReinitializeResourceLite(x) + 57F7 ↓p ... .text: 00411859 .text: 00411859 Semaphore = dword ptr 8 .text: 00411859 Count = dword ptr 0Ch .text: 00411859 Limit = dword ptr 10h .text: 00411859 .text: 00411859 mov edi, edi .text: 0041185B push ebp .text: 0041185C mov ebp, esp .text: 0041185E mov eax, [ebp + Semaphore] .text: 00411861 mov ecx, [ebp + Count] .text: 00411864 mov [eax + _KSEMAPHORE.Header.SignalState], ecx ; 为信号量赋值 .text: 00411867 lea ecx, [eax + _KSEMAPHORE.Header.WaitListHead] .text: 0041186A mov [eax + _KSEMAPHORE.Header. Type ], SemaphoreObject .text: 0041186D mov [eax + _KSEMAPHORE.Header.Size], 5 .text: 00411871 mov [ecx + LIST_ENTRY.Blink], ecx .text: 00411874 mov [ecx + LIST_ENTRY.Flink], ecx .text: 00411876 mov ecx, [ebp + Limit] .text: 00411879 mov [eax + _KSEMAPHORE.Limit], ecx ; 为信号量最大值赋值 .text: 0041187C pop ebp .text: 0041187D retn 0Ch .text: 0041187D _KeInitializeSemaphore@ 12 endp |
在释放信号量函数KeReleaseSemaphore中,首先会判断原来信号量和要增加的信号量是否大于最大信号量,以及原信号量在加上增加的信号量以后是否小于原信号量。
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 | .text: 004123CE ; LONG __stdcall KeReleaseSemaphore(PRKSEMAPHORE Semaphore, KPRIORITY Increment, LONG Adjustment, BOOLEAN Wait) .text: 004123CE public _KeReleaseSemaphore@ 16 .text: 004123CE _KeReleaseSemaphore@ 16 proc near ; CODE XREF: ExReleaseResourceLite(x) + CF19↓p .text: 004123CE ; ExConvertExclusiveToSharedLite(x) + C708↓p ... .text: 004123CE .text: 004123CE var_1 = byte ptr - 1 .text: 004123CE Semaphore = dword ptr 8 .text: 004123CE Increment = dword ptr 0Ch .text: 004123CE Adjustment = dword ptr 10h .text: 004123CE Wait = byte ptr 14h .text: 004123CE .text: 004123CE mov edi, edi .text: 004123D0 push ebp .text: 004123D1 mov ebp, esp .text: 004123D3 push ecx .text: 004123D4 push ebx .text: 004123D5 push esi .text: 004123D6 push edi .text: 004123D7 xor ecx, ecx .text: 004123D9 call ds:__imp_@KeAcquireQueuedSpinLockRaiseToSynch@ 4 ; KeAcquireQueuedSpinLockRaiseToSynch(x) .text: 004123DF mov esi, [ebp + Semaphore] .text: 004123E2 mov ebx, [esi + _KSEMAPHORE.Header.SignalState] ; 将信号量对象的信号量赋值给ebx .text: 004123E5 mov cl, al .text: 004123E7 mov eax, [ebp + Adjustment] ; 将要增加的信号量赋值给eax .text: 004123EA lea edi, [eax + ebx] ; 将信号量对象的信号状态以及增加的信号量的和赋给edi .text: 004123ED cmp edi, [esi + _KSEMAPHORE.Limit] ; 判断增加以后的信号量是否大于最大信号量 .text: 004123F0 mov [ebp + var_1], cl .text: 004123F3 jg loc_44ACE7 .text: 004123F9 cmp edi, ebx ; 判断增加以后的信号量是否小于原信号量 .text: 004123FB jl loc_44ACE7 |
如果任意一项满足,则抛出异常
1 2 3 4 5 | .text: 0044ACE7 loc_44ACE7: ; CODE XREF: KeReleaseSemaphore(x,x,x,x) + 25 ↑j .text: 0044ACE7 ; KeReleaseSemaphore(x,x,x,x) + 2D ↑j .text: 0044ACE7 call @KiUnlockDispatcherDatabase@ 4 ; KiUnlockDispatcherDatabase(x) .text: 0044ACEC push STATUS_SEMAPHORE_LIMIT_EXCEEDED ; Status .text: 0044ACF1 call _ExRaiseStatus@ 4 |
否则的话为信号量对象的信号状态赋值,判断原信号量是否为0且是否有等待该信号量对象的线程,如果两者都满足,则调用KiWaitTest来唤醒线程。
1 2 3 4 5 6 7 8 9 10 | .text: 00412401 loc_412401: ; CODE XREF: .text: 0044ACF6 ↓j .text: 00412401 test ebx, ebx ; 判断信号量对象的信号状态是否为 0 .text: 00412403 mov [esi + _KSEMAPHORE.Header.SignalState], edi ; 为信号状态赋值 .text: 00412406 jnz short loc_412419 .text: 00412408 lea eax, [esi + _KSEMAPHORE.Header.WaitListHead] .text: 0041240B cmp [eax + LIST_ENTRY.Flink], eax ; 判断是否有等待该对象的线程 .text: 0041240D jz short loc_412419 .text: 0041240F mov edx, [ebp + Increment] .text: 00412412 mov ecx, esi .text: 00412414 call @KiWaitTest@ 8 |
四.突变体对象
这是Windows内核中互斥体(mutex)概念的具体实现,针对互斥体的操作实际上是突变体操作的特例。该对象不仅有信号状态,而且,如果它当前无信号,则一定被某个线程所占有,所以,它有所有者的概念。该对象的KMUTANT结构体定义如下:
1 2 3 4 5 6 7 | kd> dt _KMUTANT nt!_KMUTANT + 0x000 Header : _DISPATCHER_HEADER + 0x010 MutantListEntry : _LIST_ENTRY + 0x018 OwnerThread : Ptr32 _KTHREAD + 0x01c Abandoned : UChar + 0x01d ApcDisable : UChar |
名称 | 作用 |
---|---|
MutantListEntry | 由于突变体对象有所有者概念,所有被一个线程拥有的突变体都将加入到该线程的一个链表中,以KTHREAD偏移0x10的MutantListHead成员为链表头。因此,当一个线程终止时,它可以把自己拥有的所有突变体释放掉,以便其它线程有机会获得这些对象 |
OwnerThread | 代表了当前正在拥有该突变体对象的线程 |
Abandoned | 表明了该突变体对象是否已被弃之不用 |
ApcDisable | 表明是否禁止APC,会影响到所有者线程的KernelApcDisable域 |
在KeInitializeMutant中,函数会判断是否指定了所有者线程,如果没指定则将所有者线程赋值为NULL,信号量赋值为1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | .text: 0041DED9 ; int __stdcall KeInitializeMutant(PKMUTANT Mutant, BOOLEAN InitialOwner) .text: 0041DED9 public _KeInitializeMutant@ 8 .text: 0041DED9 _KeInitializeMutant@ 8 proc near ; CODE XREF: NtCreateMutant(x,x,x,x) + 6F ↓p .text: 0041DED9 ; VerifierKeInitializeMutant(x,x) + C↓p ... .text: 0041DED9 .text: 0041DED9 Mutant = dword ptr 8 .text: 0041DED9 InitialOwner = byte ptr 0Ch .text: 0041DED9 .text: 0041DED9 ; FUNCTION CHUNK AT .text: 0041DF54 SIZE 00000039 BYTES .text: 0041DED9 .text: 0041DED9 mov edi, edi .text: 0041DEDB push ebp .text: 0041DEDC mov ebp, esp .text: 0041DEDE xor eax, eax .text: 0041DEE0 push esi .text: 0041DEE1 mov esi, [ebp + Mutant] .text: 0041DEE4 inc eax ; eax = 1 .text: 0041DEE5 cmp [ebp + InitialOwner], al ; 判断是否指定了所有者线程 .text: 0041DEE8 mov [esi + _KMUTANT.Header. Type ], MutantObject .text: 0041DEEB mov [esi + _KMUTANT.Header.Size], 8 .text: 0041DEEF jz short loc_41DF54 .text: 0041DEF1 and [esi + _KMUTANT.OwnerThread], 0 .text: 0041DEF5 mov [esi + _KMUTANT.Header.SignalState], eax |
如果有,则指定突变体对象的所有者线程,并突变体对象加入到线程链表MutantListHead中,此时的信号量设为0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | .text: 0041DF54 loc_41DF54: ; CODE XREF: KeInitializeMutant(x,x) + 16 ↑j .text: 0041DF54 push ebx .text: 0041DF55 push edi .text: 0041DF56 mov eax, large fs: 124h .text: 0041DF5C and [esi + _KMUTANT.Header.SignalState], 0 .text: 0041DF60 mov edi, eax .text: 0041DF62 xor ecx, ecx .text: 0041DF64 mov [esi + _KMUTANT.OwnerThread], edi .text: 0041DF67 call ds:__imp_@KeAcquireQueuedSpinLockRaiseToSynch@ 4 ; KeAcquireQueuedSpinLockRaiseToSynch(x) .text: 0041DF6D mov edi, [edi + _ETHREAD.Tcb.MutantListHead.Blink] .text: 0041DF70 mov ebx, [edi + LIST_ENTRY.Flink] .text: 0041DF72 lea edx, [esi + _KMUTANT.MutantListEntry] .text: 0041DF75 mov [edx + LIST_ENTRY.Flink], ebx .text: 0041DF77 mov [edx + LIST_ENTRY.Blink], edi .text: 0041DF7A mov [ebx + LIST_ENTRY.Blink], edx .text: 0041DF7D mov cl, al .text: 0041DF7F mov [edi + LIST_ENTRY.Flink], edx .text: 0041DF81 call @KiUnlockDispatcherDatabase@ 4 ; KiUnlockDispatcherDatabase(x) .text: 0041DF86 pop edi .text: 0041DF87 pop ebx .text: 0041DF88 jmp loc_41DEF8 |
为剩余成员赋值以后退出函数
1 2 3 4 5 6 7 8 9 10 | .text: 0041DEF8 loc_41DEF8: ; CODE XREF: KeInitializeMutant(x,x) + AF↓j .text: 0041DEF8 lea eax, [esi + _KMUTANT.Header.WaitListHead] .text: 0041DEFB mov [eax + LIST_ENTRY.Blink], eax .text: 0041DEFE mov [eax + LIST_ENTRY.Flink], eax .text: 0041DF00 mov [esi + _KMUTANT.Abandoned], 0 .text: 0041DF04 mov [esi + _KMUTANT.ApcDisable], 0 .text: 0041DF08 pop esi .text: 0041DF09 pop ebp .text: 0041DF0A retn 8 .text: 0041DF0A _KeInitializeMutant@ 8 endp |
而在函数KeInitializeMutex中初始化的突变体对象是没有指定所有者线程的,且ApcDisable被设为了1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | .text: 00441B63 ; void __stdcall KeInitializeMutex(PRKMUTEX Mutex, ULONG Level) .text: 00441B63 public _KeInitializeMutex@ 8 .text: 00441B63 _KeInitializeMutex@ 8 proc near ; CODE XREF: RawInitializeVcb(x,x,x) + 36 ↓p .text: 00441B63 ; PpInitializeNotification() + 2D ↓p ... .text: 00441B63 .text: 00441B63 Mutex = dword ptr 8 .text: 00441B63 Level = dword ptr 0Ch .text: 00441B63 .text: 00441B63 mov edi, edi .text: 00441B65 push ebp .text: 00441B66 mov ebp, esp .text: 00441B68 mov eax, [ebp + Mutex] .text: 00441B6B lea ecx, [eax + _KMUTANT.Header.WaitListHead] .text: 00441B6E mov [eax + _KMUTANT.Header. Type ], 2 .text: 00441B71 mov [eax + _KEVENT.Header.Size], 8 .text: 00441B75 mov [eax + _KMUTANT.Header.SignalState], 1 .text: 00441B7C mov [ecx + LIST_ENTRY.Blink], ecx .text: 00441B7F mov [ecx + LIST_ENTRY.Flink], ecx .text: 00441B81 and [eax + _KMUTANT.OwnerThread], 0 .text: 00441B85 mov [eax + _KMUTANT.Abandoned], 0 .text: 00441B89 mov [eax + _KMUTANT.ApcDisable], 1 .text: 00441B8D pop ebp .text: 00441B8E retn 8 .text: 00441B8E _KeInitializeMutex@ 8 endp |
在KeReleaseMutant中会判断是否要释放突变体对象
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 | .text: 00402B4C ; int __stdcall KeReleaseMutant(PKMUTANT Mutant, int Increment, BOOLEAN Abandoned, BOOLEAN Wait) .text: 00402B4C public _KeReleaseMutant@ 16 .text: 00402B4C _KeReleaseMutant@ 16 proc near ; CODE XREF: KeReleaseMutex(x,x) + F↓p .text: 00402B4C ; ExpDeleteMutant(x) + E↓p ... .text: 00402B4C .text: 00402B4C Mutant = dword ptr 8 .text: 00402B4C Increment = dword ptr 0Ch .text: 00402B4C Abandoned = byte ptr 10h .text: 00402B4C Wait = byte ptr 14h .text: 00402B4C .text: 00402B4C .text: 00402B4C mov edi, edi .text: 00402B4E push ebp .text: 00402B4F mov ebp, esp .text: 00402B51 push ebx .text: 00402B52 push esi .text: 00402B53 push edi .text: 00402B54 xor ecx, ecx .text: 00402B56 call ds:__imp_@KeAcquireQueuedSpinLockRaiseToSynch@ 4 ; KeAcquireQueuedSpinLockRaiseToSynch(x) .text: 00402B5C mov esi, [ebp + Mutant] .text: 00402B5F mov bl, al .text: 00402B61 mov eax, [esi + _KMUTANT.Header.SignalState] .text: 00402B64 mov [ebp + Mutant], eax ; 将信号量保存在Mutant中 .text: 00402B67 mov eax, large fs: 124h .text: 00402B6D cmp [ebp + Abandoned], 0 .text: 00402B71 mov edi, eax ; 将当前线程赋给edi .text: 00402B73 jnz loc_41DF28 |
如果要释放,则修改信号量为1,且Abandoned置为1
1 2 3 4 | .text: 0041DF28 loc_41DF28: ; CODE XREF: KeReleaseMutant(x,x,x,x) + 27 ↑j .text: 0041DF28 mov [esi + _KMUTANT.Header.SignalState], 1 .text: 0041DF2F mov [esi + _KMUTANT.Abandoned], 1 .text: 0041DF33 jmp loc_402B85 |
如果不需释放,则判断突变体的所有者线程是否是当前线程
1 2 | .text: 00402B79 cmp [esi + _KMUTANT.OwnerThread], edi ; 判断所有者线程是否是当前线程 .text: 00402B7C jnz loc_4425A0 |
如果不是当前线程,则抛出异常,增加信号量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | .text: 004425A0 loc_4425A0: ; CODE XREF: KeReleaseMutant(x,x,x,x) + 30 ↑j .text: 004425A0 mov cl, bl .text: 004425A2 call @KiUnlockDispatcherDatabase@ 4 ; KiUnlockDispatcherDatabase(x) .text: 004425A7 mov al, [esi + _KMUTANT.Abandoned] .text: 004425AA neg al .text: 004425AC sbb eax, eax .text: 004425AE and eax, 4000003Ah .text: 004425B3 add eax, 0C0000046h .text: 004425B8 push eax ; Status .text: 004425B9 call _ExRaiseStatus@ 4 ; ExRaiseStatus(x) .text: 004425BE jmp loc_402B82 。。。 .text: 00402B82 loc_402B82: ; CODE XREF: .text: 004425BE ↓j .text: 00402B82 inc [esi + _KMUTANT.Header.SignalState] |
判断信号量是否为1,如果不为1则退出函数
1 2 3 | .text: 00402B85 loc_402B85: ; CODE XREF: KeReleaseMutant(x,x,x,x) + 1B3E7 ↓j .text: 00402B85 cmp [esi + _KMUTANT.Header.SignalState], 1 .text: 00402B89 jnz short loc_402BBB |
如果为1,继续判断原信号量是否大于0,此时的Mutant中保存的是原信号量
1 2 | .text: 00402B8B cmp [ebp + Mutant], 0 ; 判断信号量是否大于 0 .text: 00402B8F jg short loc_402BAC |
如果不大于0,则将突变体从链表中删除
1 2 3 4 5 6 7 | .text: 00402B91 mov eax, [esi + _KMUTANT.MutantListEntry.Flink] .text: 00402B94 mov ecx, [esi + _KMUTANT.MutantListEntry.Blink] .text: 00402B97 mov [ecx + LIST_ENTRY.Flink], eax .text: 00402B99 mov [eax + LIST_ENTRY.Blink], ecx .text: 00402B9C movzx eax, [esi + _KMUTANT.ApcDisable] .text: 00402BA0 add [edi + _ETHREAD.Tcb.KernelApcDisable], eax .text: 00402BA6 jz loc_40E979 |
清空当前所有者线程,判断是否有其他线程在等待该突变体对象
1 2 3 4 5 6 | .text: 00402BAC loc_402BAC: ; CODE XREF: KeReleaseMutant(x,x,x,x) + 43 ↑j .text: 00402BAC ; KeReleaseMutant(x,x,x,x) + BE32↓j ... .text: 00402BAC and [esi + _KMUTANT.OwnerThread], 0 .text: 00402BB0 lea eax, [esi + _KMUTANT.Header.WaitListHead] .text: 00402BB3 cmp [eax + LIST_ENTRY.Flink], eax ; 判断是否有线程对象在等待该突变体对象 .text: 00402BB5 jnz loc_424732 |
如果有等待该突变体对象的线程,则调用KiWaitTest来唤醒线程
1 2 3 4 5 | .text: 00424732 loc_424732: ; CODE XREF: KeReleaseMutant(x,x,x,x) + 69 ↑j .text: 00424732 mov edx, [ebp + Increment] .text: 00424735 mov ecx, esi .text: 00424737 call @KiWaitTest@ 8 ; KiWaitTest(x,x) .text: 0042473C jmp loc_402BBB |
函数最终会将原信号量作为返回值返回给调用者
1 2 | .text: 00402BCD loc_402BCD: ; CODE XREF: KeReleaseMutant(x,x,x,x) + 480DC ↓j .text: 00402BCD mov eax, [ebp + Mutant] |
只有所有者线程才可以释放突变体对象,否则会触发异常。该函数参数Abandoned可以强迫释放突变体对象
KeReleaseMutex函数的实现是通过调用KeReleastMutant函数来实现的,此时的参数Abandoned被指定为FALSE
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | .text: 00411471 ; LONG __stdcall KeReleaseMutex(PRKMUTEX Mutex, BOOLEAN Wait) .text: 00411471 public _KeReleaseMutex@ 8 .text: 00411471 _KeReleaseMutex@ 8 proc near ; CODE XREF: RawCheckForDismount(x,x) + 4F ↓p .text: 00411471 ; WmipNotificationIrpCancel(x,x) + 28 ↓p ... .text: 00411471 .text: 00411471 Mutex = dword ptr 8 .text: 00411471 Wait = byte ptr 0Ch .text: 00411471 .text: 00411471 mov edi, edi .text: 00411473 push ebp .text: 00411474 mov ebp, esp .text: 00411476 push dword ptr [ebp + Wait] ; Wait .text: 00411479 push 0 ; Abandoned .text: 0041147B push 1 ; Increment .text: 0041147D push [ebp + Mutex] ; Mutant .text: 00411480 call _KeReleaseMutant@ 16 ; KeReleaseMutant(x,x,x,x) .text: 00411485 pop ebp .text: 00411486 retn 8 .text: 00411486 _KeReleaseMutex@ 8 endp |
五.线程与分发器对象的连接
分发器对象的DISPATCHER_HEADER中的WaitListHead成员是一个双向链表,链表上的每个节点代表了正在等待该分发器的线程。线程对象KTHREAD偏移0x5C的WaitBlockList成员,它指向一个链表,链表上的每个节点代表了该线程正在等待的分发器对象。这两个链表上的节点都是等待块对象,其数据类型是KWAIT_BLOCK,定义如下:
1 2 3 4 5 6 7 8 | kd> dt _KWAIT_BLOCK nt!_KWAIT_BLOCK + 0x000 WaitListEntry : _LIST_ENTRY + 0x008 Thread : Ptr32 _KTHREAD + 0x00c Object : Ptr32 Void + 0x010 NextWaitBlock : Ptr32 _KWAIT_BLOCK + 0x014 WaitKey : Uint2B + 0x016 WaitType : Uint2B |
名称 | 作用 |
---|---|
WaitListEntry | 链入线程对象的双向链表 |
Thread | 指向等待该分发器对象的线程对象 |
Object | 指向被等待的分发器对象 |
NextWaitBlock | 链入分发器对象的单向链表 |
WaitKey | 该域是指当Thread等待Object成功从而被解除等待时的完成状态值 |
WaitType | WAIT_TYPE枚举类型:WaitAll或WaitAny,说明线程Thread要等待所有对象变成有信号状态,或者只要任意一个对象变成有信号状态即可 |
等待快对象是要同时加入到线程对象中的链表和分发器对象中的链表。其中的WaitListEntry用来加入到线程的WaitBlockList链表中,这样线程就可以找到其等待的所有分发器对象,而NextWaitBlock则用来加入到分发器对象的WaitListHead中,这样同一类型的分发器对象就可以连接在一起
下图展现了线程对象,分发器对象和等待块对象的关系:
基于这样的结构,当一个线程通过某个Wait函数进入等待状态的时候,线程对象偏移0x5C的WaitBlockList将指向一个非空链表,链表上的每个等待块对象描述了它所等待的一个分发器对象。只要链表节点上的分发器对象是无信号状态,则该线程始终处于等待状态。
线程对象偏移0x70处预留了4个KWAIT_BLOCK,即WaitBlock数组成员。因此,如果一个线程要等待的分发器对象不超过4个,则它就不需要申请额外的KWAIT_BLOCK对象,直接使用预留的这4个等待块对象即可。
六.KeWaitForSingleObject函数分析
KeWaitForSingleObject函数能让线程进入等待状态的函数,该函数首先会对线程对象偏移0x70的WaitBlck[0]等待块对象赋值,将线程对象的WaitBlockList指向第一个等待块对象,判断超时时间是否为0
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 | .text: 00405400 ; NTSTATUS __stdcall KeWaitForSingleObject(PVOID Object , KWAIT_REASON WaitReason, KPROCESSOR_MODE WaitMode, BOOLEAN Alertable, PLARGE_INTEGER Timeout) .text: 00405400 public _KeWaitForSingleObject@ 20 .text: 00405400 _KeWaitForSingleObject@ 20 proc near ; CODE XREF: ExAcquireFastMutexUnsafe(x) + 15 ↑p .text: 00405400 ; ExpWaitForResource(x,x) + 2A ↓p ... .text: 00405400 .text: 00405400 var_14 = dword ptr - 14h .text: 00405400 var_10 = dword ptr - 10h .text: 00405400 var_C = byte ptr - 0Ch .text: 00405400 var_4 = dword ptr - 4 .text: 00405400 Object = dword ptr 8 .text: 00405400 WaitReason = dword ptr 0Ch .text: 00405400 WaitMode = byte ptr 10h .text: 00405400 Alertable = byte ptr 14h .text: 00405400 Timeout = dword ptr 18h .text: 00405400 .text: 00405400 mov edi, edi ; KeWaitForMutexObject .text: 00405402 push ebp .text: 00405403 mov ebp, esp .text: 00405405 sub esp, 14h .text: 00405408 push ebx .text: 00405409 push esi .text: 0040540A push edi .text: 0040540B mov eax, large fs: 124h .text: 00405411 mov edx, [ebp + Timeout] .text: 00405414 mov ebx, [ebp + Object ] .text: 00405417 mov esi, eax ; 将线程对象赋给esi .text: 00405419 cmp [esi + _KTHREAD.WaitNext], 0 .text: 0040541D mov [ebp + var_4], edx .text: 00405420 lea edi, [esi + _ETHREAD.Tcb.WaitBlock] ; edi指向WaitBlock[ 0 ] .text: 00405423 lea eax, [esi + 0B8h ] ; 取出ETHREAD.Tcb.WaitBlock[ 3 ]的地址 .text: 00405429 jnz loc_43EBB5 .text: 0040542F .text: 0040542F loc_40542F: ; CODE XREF: KeWaitForSingleObject(x,x,x,x,x) + 579D ↓j .text: 0040542F ; KeWaitForSingleObject(x,x,x,x,x) + 57B5 ↓j ... .text: 0040542F call ds:__imp__KeRaiseIrqlToSynchLevel@ 0 ; KeRaiseIrqlToSynchLevel() .text: 00405435 xor ecx, ecx .text: 00405437 cmp [ebp + Timeout], ecx ; 判断超时时间是否为 0 .text: 0040543A mov [esi + _KTHREAD.WaitIrql], al .text: 0040543D mov [esi + _KTHREAD.WaitBlockList], edi .text: 00405440 mov [edi + _KWAIT_BLOCK. Object ], ebx .text: 00405443 mov [edi + _KWAIT_BLOCK.WaitKey], cx .text: 00405447 mov [edi + _KWAIT_BLOCK.WaitType], WaitAny .text: 0040544D mov [esi + _KTHREAD.WaitStatus], ecx .text: 00405450 jz loc_40EA6F |
如果超时时间为0,则第一个等待块的NextWaitBlock指向自己
1 2 3 | .text: 0040EA6F loc_40EA6F: ; CODE XREF: KeWaitForSingleObject(x,x,x,x,x) + 50 ↑j .text: 0040EA6F mov [edi + _KWAIT_BLOCK.NextWaitBlock], edi .text: 0040EA72 jmp loc_40546E |
如果超时时间不为0,则将第一个等待块和第四个等待块的NextWaitBlock互相连接起来,并将其连入线程的定时器对象链表中
1 2 3 4 5 | .text: 00405456 lea eax, [esi + 0B8h ] ; eax指向WaitBlock[ 3 ]的地址 .text: 0040545C mov [edi + _KWAIT_BLOCK.NextWaitBlock], eax .text: 0040545F mov [eax + _KWAIT_BLOCK.NextWaitBlock], edi .text: 00405462 mov [esi + _KTHREAD.Timer.Header.WaitListHead.Flink], eax .text: 00405468 mov [esi + _KTHREAD.Timer.Header.WaitListHead.Blink], eax |
继续为线程中的成员赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | .text: 0040546E loc_40546E: ; CODE XREF: KeWaitForSingleObject(x,x,x,x,x) + 9672 ↓j .text: 0040546E mov al, [ebp + Alertable] .text: 00405471 mov dl, byte ptr [ebp + WaitReason] .text: 00405474 mov [esi + _KTHREAD.Alertable], al .text: 0040547A mov al, [ebp + WaitMode] .text: 0040547D test al, al .text: 0040547F mov [esi + _KTHREAD.WaitMode], al .text: 00405482 mov [esi + _KTHREAD.WaitReason], dl .text: 00405485 mov [esi + _KTHREAD.___u25.WaitListEntry.Flink], ecx .text: 00405488 jz loc_405538 .text: 0040548E cmp [esi + _KTHREAD.EnableStackSwap], 0 .text: 00405495 jz loc_405538 .text: 0040549B cmp [esi + _KTHREAD.Priority], 19h .text: 0040549F mov [ebp + Object ], 1 .text: 004054A6 jge loc_405538 |
接下来会进入一个死循环,每次线程被其他线程唤醒的时候都会进入到这个循环中。
在循环中会判断分发器对象是否是突变体对象,如果是则接着判断其信号量是否大于0,或者所有者线程是当前线程
1 2 3 4 5 6 7 8 | .text: 004054D2 loc_4054D2: ; CODE XREF: KeWaitForSingleObject(x,x,x,x,x) + 3C1C8 ↓j .text: 004054D2 cmp [ebx + _DISPATCHER_HEADER. Type ], 2 ; 判断是否是突变体对象 .text: 004054D5 jnz loc_40265F .text: 004054DB mov eax, [ebx + _DISPATCHER_HEADER.SignalState] .text: 004054DE test eax, eax ; 判断是否有信号量 .text: 004054E0 jg loc_40261E .text: 004054E6 cmp esi, [ebx + _KMUTANT.OwnerThread] .text: 004054E9 jz loc_40261E |
如果信号量大于0,或者所有者线程是当前线程,就会判断信号量是否等于0x80000000,如果不相等,则会将突变体对象的信号量减一,判断减一以后是否等于0,如果不等于0,则会退出函数
1 2 3 4 5 6 | .text: 0040261E loc_40261E: ; CODE XREF: KeWaitForSingleObject(x,x,x,x,x) + E0↓j .text: 0040261E ; KeWaitForSingleObject(x,x,x,x,x) + E9↓j .text: 0040261E cmp eax, 80000000h ; 信号量是否等于 0x80000000 .text: 00402623 jz loc_44AB42 .text: 00402629 dec [ebx + _KMUTANT.Header.SignalState] ; 信号量减 1 .text: 0040262C jnz short loc_402657 |
如果等于0,则为突变体对象赋值,判断Abandoned是否为TRUE
1 2 3 4 5 | .text: 0040262E movzx eax, [ebx + _KMUTANT.ApcDisable] .text: 00402632 sub [esi + _KTHREAD.KernelApcDisable], eax .text: 00402638 cmp [ebx + _KMUTANT.Abandoned], 1 ; 判断Abandoned是否等于 1 .text: 0040263C mov [ebx + _KMUTANT.OwnerThread], esi .text: 0040263F jz loc_442A97loc_405521 |
如果为TRUE,继续修改突变体对象
1 2 3 4 | .text: 00442A97 loc_442A97: ; CODE XREF: KeWaitForSingleObject(x,x,x,x,x) - 2DC1 ↑j .text: 00442A97 mov [ebx + _KMUTANT.Abandoned], 0 .text: 00442A9B mov [esi + _KTHREAD.WaitStatus], 80h .text: 00442AA2 jmp loc_402645 |
将突变体对象从线程对象的MutantListHead中删除
1 2 3 4 5 6 7 8 9 10 11 12 | .text: 00402645 loc_402645: ; CODE XREF: KeWaitForSingleObject(x,x,x,x,x) + 3D6A2 ↓j .text: 00402645 mov ecx, [esi + _KTHREAD.MutantListHead.Blink] .text: 00402648 mov edx, [ecx + LIST_ENTRY.Flink] .text: 0040264A lea eax, [ebx + _KMUTANT.MutantListEntry] .text: 0040264D mov [eax + LIST_ENTRY.Flink], edx .text: 0040264F mov [eax + LIST_ENTRY.Blink], ecx .text: 00402652 mov [edx + LIST_ENTRY.Blink], eax .text: 00402655 mov [ecx + LIST_ENTRY.Flink], eax .text: 00402657 .text: 00402657 loc_402657: ; CODE XREF: KeWaitForSingleObject(x,x,x,x,x) - 2DD4 ↑j .text: 00402657 mov edi, [esi + _ETHREAD.Tcb.WaitStatus] .text: 0040265A jmp loc_405521 |
如果分发器对象类型不是突变体对象,就会继续判断信号量是否小于等于0
1 2 3 | .text: 0040265F loc_40265F: ; CODE XREF: KeWaitForSingleObject(x,x,x,x,x) + D5↓j .text: 0040265F cmp [ebx + _DISPATCHER_HEADER.SignalState], 0 ; 信号量是否小于等于 0 .text: 00402663 jle loc_4054EF |
如果大于0,则取出分发器对象的类型,保留后3位后判断分发器对象的类型
1 2 3 4 5 6 7 | .text: 00402669 mov al, [ebx + _DISPATCHER_HEADER. Type ] .text: 0040266B mov cl, al .text: 0040266D and cl, 7 .text: 00402670 cmp cl, 1 ; 判断是否是事件对象 .text: 00402673 jz loc_410930 .text: 00402679 cmp al, 5 ; 判断是否是信号量对象 .text: 0040267B jz loc_4126C4 |
如果是事件类型,则将信号量赋值为0,接着退出函数
1 2 3 | .text: 00410930 loc_410930: ; CODE XREF: KeWaitForSingleObject(x,x,x,x,x) - 2D8D ↑j .text: 00410930 and [ebx + KEVENT.Header.SignalState], 0 .text: 00410934 jmp loc_402681 |
如果是信号量类型,则将信号量减1,接着退出函数
1 2 3 | .text: 004126C4 loc_4126C4: ; CODE XREF: KeWaitForSingleObject(x,x,x,x,x) - 2D85 ↑j .text: 004126C4 dec [ebx + _KSEMAPHORE.Header.SignalState] .text: 004126C7 jmp loc_402681 |
如果条件不满足(非突变体对象的信号量小于等于0或突变体对象没有退出循环),接下来就会将等待块对象(WAIT_BLOCK)连接进分发器对象的WaitListHead。此时的edi指向的就是WaitBlock数组的第一个等待块对象
1 2 3 4 5 6 7 8 | .text: 0040AB5D loc_40AB5D: ; CODE XREF: KeWaitForSingleObject(x,x,x,x,x) + 10B ↑j .text: 0040AB5D ; KeWaitForSingleObject(x,x,x,x,x) + 9944 ↓j .text: 0040AB5D lea eax, [ebx + _DISPATCHER_HEADER.WaitListHead] .text: 0040AB60 mov ecx, [eax + LIST_ENTRY.Blink] .text: 0040AB63 mov [edi + LIST_ENTRY.Flink], eax .text: 0040AB65 mov [edi + LIST_ENTRY.Blink], ecx .text: 0040AB68 mov [ecx + LIST_ENTRY.Flink], edi .text: 0040AB6A mov [eax + LIST_ENTRY.Blink], edi |
修改线程的状态为等待状态,判断是否存在分发器对象
1 2 3 4 | .text: 0040AB7B loc_40AB7B: ; CODE XREF: KeWaitForSingleObject(x,x,x,x,x) + B982↓j .text: 0040AB7B cmp [ebp + Object ], 0 ; 判断是否存在分发器对象 .text: 0040AB7F mov [esi + _KTHREAD.State], 5 .text: 0040AB83 jnz loc_40F8C4 |
如果存在,则将线程对象加入到KiWaitListHead链表中
1 2 3 4 5 6 7 8 | .text: 0040F8C4 loc_40F8C4: ; CODE XREF: KeWaitForSingleObject(x,x,x,x,x) + 5783 ↑j .text: 0040F8C4 mov ecx, ds:dword_48B30C .text: 0040F8CA lea eax, [esi + 60h ] ; 取出线程对象的WaitListEntry .text: 0040F8CD mov [eax + LIST_ENTRY.Flink], offset _KiWaitListHead .text: 0040F8D3 mov [eax + LIST_ENTRY.Blink], ecx .text: 0040F8D6 mov [ecx + LIST_ENTRY.Flink], eax .text: 0040F8D8 mov ds:dword_48B30C, eax .text: 0040F8DD jmp loc_40AB89 |
调用KiSwapThread来主动切换线程,当该函数返回的时候,意味着此线程获得了控制权,若不考虑内核APC的因素,则意味着此等待以被满足;或者被等待的对象变成了有信号的状态,或者超时
1 2 3 4 5 6 7 8 9 10 11 12 13 | .text: 0040AB89 loc_40AB89: ; CODE XREF: KeWaitForSingleObject(x,x,x,x,x) + A4DD↓j .text: 0040AB89 call @KiSwapThread@ 0 ; KiSwapThread() .text: 0040AB8E cmp eax, 100h .text: 0040AB93 jnz loc_405531 .text: 0040AB99 cmp [ebp + Timeout], 0 .text: 0040AB9D jz loc_40542F .text: 0040ABA3 mov ecx, [ebp + var_4] .text: 0040ABA6 lea eax, [ebp + var_C] .text: 0040ABA9 push eax .text: 0040ABAA lea edx, [ebp + var_14] .text: 0040ABAD call @KiComputeWaitInterval@ 12 ; KiComputeWaitInterval(x,x,x) .text: 0040ABB2 mov [ebp + Timeout], eax .text: 0040ABB5 jmp loc_40542F |
线程进入等待状态的核心操作是将线程对象挂到KiWaitListHead以后,调用KiSwapThread来主动切换线程,等待其他线程将其从链表中摘除
七.KiWaitTest函数分析
KeSetEvent,KeReleaseSemaphore和KeReleaseMutant函数可以改变分发器对象的信号状态,在这些函数中,它们会调用其他函数来测试一个等待是否已经满足,KiWaitTest函数就是其中之一。
在KiWaitTest中会首先取出分发器对象,判断信号量是否小于等于0,如果小于等于0,则退出函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | .text: 0040A687 ; __fastcall KiWaitTest(x, x) .text: 0040A687 @KiWaitTest@ 8 proc near ; CODE XREF: KeSetEvent(x,x,x) + 59 ↓p .text: 0040A687 ; KiTimerListExpire(x,x) + 75 ↓p ... .text: 0040A687 .text: 0040A687 var_10 = dword ptr - 10h .text: 0040A687 var_C = dword ptr - 0Ch .text: 0040A687 var_Increment = dword ptr - 8 .text: 0040A687 var_WaitKey = dword ptr - 4 .text: 0040A687 .text: 0040A687 mov edi, edi .text: 0040A689 push ebp .text: 0040A68A mov ebp, esp .text: 0040A68C sub esp, 10h .text: 0040A68F push ebx .text: 0040A690 push esi ; Thread .text: 0040A691 mov esi, ecx .text: 0040A693 cmp [esi + _DISPATCHER_HEADER.SignalState], 0 ; 判断信号量是否小于等于 0 .text: 0040A697 lea ecx, [ebp + var_10] .text: 0040A69A lea ebx, [esi + _DISPATCHER_HEADER.WaitListHead] .text: 0040A69D mov eax, [ebx + LIST_ENTRY.Flink] .text: 0040A69F mov [ebp + var_Increment], edx .text: 0040A6A2 mov [ebp + var_C], ecx .text: 0040A6A5 mov [ebp + var_10], ecx .text: 0040A6A8 jle short loc_40A709 ; 小于等于 0 则跳转 |
每个KWAIT_BLOCK对象都代表了一个线程在等待该对象,利用KWAIT_BLOCK结构可以获得等待该分发器对象的线程以及等待类型,根据类型的不同,修改分发器的信号量等信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | .text: 0040A6AB loc_40A6AB: ; CODE XREF: KiWaitTest(x,x) + 75 ↓j .text: 0040A6AB cmp eax, ebx ; 判断是否有分发器对象 .text: 0040A6AD jz short loc_40A6FE .text: 0040A6AF cmp [eax + _KWAIT_BLOCK.WaitType], 1 .text: 0040A6B4 mov ecx, [eax + _KWAIT_BLOCK.Thread] .text: 0040A6B7 mov [ebp + var_WaitKey], 100h .text: 0040A6BE jnz short loc_40A6E7 .text: 0040A6C0 movzx eax, [eax + _KWAIT_BLOCK.WaitKey] .text: 0040A6C4 mov [ebp + var_WaitKey], eax .text: 0040A6C7 mov al, [esi + _DISPATCHER_HEADER. Type ] .text: 0040A6C9 mov dl, al .text: 0040A6CB and dl, 7 .text: 0040A6CE cmp dl, 1 .text: 0040A6D1 jz loc_4135F3 .text: 0040A6D7 cmp al, 5 .text: 0040A6D9 jz loc_412435 .text: 0040A6DF cmp al, 2 .text: 0040A6E1 jz loc_4246FB |
调用KiUnWaitThread函数
1 2 3 4 5 6 7 | .text: 0040A6E7 loc_40A6E7: ; CODE XREF: KiWaitTest(x,x) + 37 ↑j .text: 0040A6E7 ; KiWaitTest(x,x) + 7DB1 ↓j ... .text: 0040A6E7 mov edx, [ebp + var_WaitKey] .text: 0040A6EA lea eax, [ebp + var_10] .text: 0040A6ED push eax .text: 0040A6EE push [ebp + var_Increment] .text: 0040A6F1 call @KiUnwaitThread@ 16 |
在KiUnwaitThread函数中会调用KiUnlinkThread函数,将一个满足等待条件的线程从它的等待块链表中移除出来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | .text: 0040A5E8 ; __fastcall KiUnwaitThread(x, x, x, x) .text: 0040A5E8 @KiUnwaitThread@ 16 proc near ; CODE XREF: KiWaitTest(x,x) + 6A ↓p .text: 0040A5E8 ; KeSetEvent(x,x,x) + 3D7E ↓p ... .text: 0040A5E8 .text: 0040A5E8 var_4 = dword ptr - 4 .text: 0040A5E8 arg_0 = dword ptr 8 .text: 0040A5E8 arg_4 = dword ptr 0Ch .text: 0040A5E8 .text: 0040A5E8 ; FUNCTION CHUNK AT .text: 00402F4B SIZE 00000036 BYTES .text: 0040A5E8 ; FUNCTION CHUNK AT .text: 0040EE83 SIZE 00000023 BYTES .text: 0040A5E8 ; FUNCTION CHUNK AT .text: 0040FBD0 SIZE 0000000B BYTES .text: 0040A5E8 ; FUNCTION CHUNK AT .text: 0041C168 SIZE 00000008 BYTES .text: 0040A5E8 .text: 0040A5E8 mov edi, edi .text: 0040A5EA push ebp .text: 0040A5EB mov ebp, esp .text: 0040A5ED push ecx .text: 0040A5EE push ebx .text: 0040A5EF push esi ; Thread .text: 0040A5F0 mov [ebp + var_4], edx .text: 0040A5F3 mov esi, ecx .text: 0040A5F5 call @KiUnlinkThread@ 8 |
KiUnwaitThread函数返回后,会调用KiReadThread让线程进入就绪状态,这就相当于将等待状态的线程唤醒
1 2 3 4 5 6 7 8 9 | .text: 0040A70D loc_40A70D: ; CODE XREF: KiWaitTest(x,x) + 80 ↑j .text: 0040A70D mov eax, [ecx] .text: 0040A70F lea edx, [ebp + var_10] .text: 0040A712 mov [ebp + var_10], eax .text: 0040A715 add ecx, 0FFFFFFA0h ; Thread .text: 0040A718 mov [eax + 4 ], edx .text: 0040A71B call @KiReadyThread@ 4 ; KiReadyThread(x) .text: 0040A720 jmp short loc_40A6FF .text: 0040A720 @KiWaitTest@ 8 endp |
KiUnlinkThread函数则首先设置线程的完成状态,将线程的等待块对象从线程中全部删除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | .text: 0040A59A ; __fastcall KiUnlinkThread(x, x) .text: 0040A59A @KiUnlinkThread@ 8 proc near ; CODE XREF: KiUnwaitThread(x,x,x,x) + D↓p .text: 0040A59A ; KeSetEventBoostPriority(x,x) + 8B ↓p .text: 0040A59A .text: 0040A59A .text: 0040A59A or [ecx + _KTHREAD.WaitStatus], edx .text: 0040A59D mov eax, [ecx + _ETHREAD.Tcb.WaitBlockList] ; 获取线程的分发器对象 .text: 0040A5A0 push esi .text: 0040A5A1 .text: 0040A5A1 loc_40A5A1: ; CODE XREF: KiUnlinkThread(x,x) + 17 ↓j .text: 0040A5A1 mov edx, [eax + _KWAIT_BLOCK.WaitListEntry.Flink] .text: 0040A5A3 mov esi, [eax + _KWAIT_BLOCK.WaitListEntry.Blink] .text: 0040A5A6 mov [esi + LIST_ENTRY.Flink], edx .text: 0040A5A8 mov [edx + LIST_ENTRY.Blink], esi .text: 0040A5AB mov eax, [eax + _KWAIT_BLOCK.NextWaitBlock] ; 获取下一分发器 .text: 0040A5AE cmp eax, [ecx + _KTHREAD.WaitBlockList] ; 是否存在分发器对象 .text: 0040A5B1 jnz short loc_40A5A1 |
判断线程是否在全局等待链表中,如果在则从全局的等待链表中删除
1 2 3 4 5 6 7 | .text: 0040A5B3 cmp [ecx + _KTHREAD.___u25.WaitListEntry.Flink], 0 ; 线程等待链表是否为NULL .text: 0040A5B7 pop esi .text: 0040A5B8 jz short loc_40A5C5 .text: 0040A5BA mov eax, [ecx + _KTHREAD.___u25.WaitListEntry.Flink] .text: 0040A5BD mov edx, [ecx + _KTHREAD.___u25.WaitListEntry.Blink] .text: 0040A5C0 mov [edx + LIST_ENTRY.Flink], eax .text: 0040A5C2 mov [eax + LIST_ENTRY.Blink], edx |
八.参考资料
- 《Windows内核原理与实现》
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
|
|
---|---|
|
如果没记错,《windows内核原理与实现》这本书的图5.13画错了,楼主可以看wrk的代码核实一下。
|
|
fengyunabc 如果没记错,《windows内核原理与实现》这本书的图5.13画错了,楼主可以看wrk的代码核实一下。这个任务就交给你们了 |
|
嗨感谢分享,我想请教一个问题,假设一个线程调用waitforsingleobject陷入了等待,在这个函数里死循环的的简化逻辑是:检查信号量的值-陷入等待-被唤醒-检查信号量的值-陷入等待…我的问题是:在“被唤醒”的这个步骤里,已经有将信号量的值递减的操作了,因为唤醒一个线程需要消耗一个值,不仿假设信号量的值因此从1变为了0。被唤醒的线程进入了新的一轮循环,检查信号量的值,因为是零,所以又陷入了等待,那这个过程岂不是无休无止了吗?似乎跳不出这个死循环?
|
- [原创]CVE-2022-21882提权漏洞学习笔记 16383
- [原创]CVE-2021-1732提权漏洞学习笔记 19489
- [原创]CVE-2014-1767提权漏洞学习笔记 15192
- [原创]CVE-2018-8453提权漏洞学习笔记 18526
- [原创]CVE-2020-1054提权漏洞学习笔记 13542