介绍
- 时钟中断在IDT中的中断号为0x30,他的IRQL是28
- 如果想获取当前的时钟间隔值,可使用API GstSystemTimeAdjustment
- 当一个线程的时限用完或者发生被抢占的情况下也会会发生线程切换,可以称之为被动切换,时钟中断的服务例程由HAL 提供,它调用了内核模块的KeUpdateSystemTime 函数。
- 当时钟中断到来时,线程调度器获得控制权并不是直接在时钟中断处理函数中,而是在KiDispatchInterrupt 函数中。线程抢占和时限用完都发生在 KiDispatchInterrupt 函数中。在线程抢占的情形下,KiDispatchInterrupt 直接调用SwapContext 函数执行线程切换;而在时限用完的情形下,它将调用KiQuantumEnd 函数
KiDispatchInterrupt 逆向
.text:00404874 public _KiDispatchInterrupt@0
.text:00404874 _KiDispatchInterrupt@0 proc near ; DATA XREF: .edata:off_5AC2A8↓o
.text:00404874 8B 1D 1C F0 DF FF mov ebx, ds:0FFDFF01Ch
.text:0040487A 8D 83 80 09 00 00 lea eax, [ebx+_KPCR.PrcbData.DpcListHead] ; ebx = KPCR_SELFPCR
.text:0040487A _KiDispatchInterrupt@0 endp
.text:0040487A
.text:00404880 FA cli
.text:00404881 3B 00 cmp eax, [eax+_LIST_ENTRY.Flink] ; 判断自己和链表下一个地址是否相同,也就是判断是否为空
.text:00404883 74 1D jz short loc_4048A2 ; 如果为空
.text:00404883
.text:00404885 55 push ebp
.text:00404886 FF 33 push dword ptr [ebx]
.text:00404888 C7 03 FF FF FF FF mov [ebx+_KPCR.NtTib.ExceptionList], 0FFFFFFFFh
.text:0040488E 8B D4 mov edx, esp
.text:00404890 8B A3 88 09 00 00 mov esp, [ebx+_KPCR.PrcbData.DpcStack]
.text:00404896 52 push edx
.text:00404897 8B E8 mov ebp, eax
.text:00404899 E8 F0 02 00 00 call KiRetireDpcList ; 指定DPC处理列表
1.判断DPC链表是否为空,如果不为空则指定DPC处理列表
.text:004048A2 FB sti
.text:004048A3 83 BB AC 09 00 00 00 cmp [ebx+_KPCR.PrcbData.QuantumEnd], 0 ; 判断QuantumEnd标志是否为空
.text:004048A3 ; 在系统时钟中断服务例程调用到
.text:004048A3 ; KeUpdateRunTime 函数时,若当前线
.text:004048A3 ; 程的时限已用完,则此标志被置上
.text:004048AA 75 56 jnz short loc_404902 ; 如果时间碎片未结束
.text:00404902 C7 83 AC 09 00 00 00 00 00 00 mov dword ptr [ebx+9ACh], 0
.text:0040490C E8 35 BB 00 00 call _KiQuantumEnd@0 ; KiQuantumEnd()
2.判断时间碎片是否为空,如果不为空,则把QuantumEnd制空并通过KiQuantumEnd函数切换线程。
.text:004048BB loc_4048BB: ; CODE XREF: .text:00404913↓j
.text:004048BB 83 EC 0C sub esp, 0Ch
.text:004048BE 89 74 24 08 mov [esp+8], esi
.text:004048C2 89 7C 24 04 mov [esp+4], edi
.text:004048C6 89 2C 24 mov [esp], ebp
.text:004048C9 8B F0 mov esi, eax
.text:004048CB 8B BB 24 01 00 00 mov edi, [ebx+_KPCR.PrcbData.CurrentThread]
.text:004048D1 C7 83 28 01 00 00 00 00 00 00 mov [ebx+_KPCR.PrcbData.NextThread], 0
.text:004048DB 89 B3 24 01 00 00 mov [ebx+_KPCR.PrcbData.CurrentThread], esi ; 把下个线程放入当前线程
.text:004048E1 8B CF mov ecx, edi
.text:004048E3 C6 47 50 01 mov [edi+_ETHREAD.Tcb.IdleSwapBlock], 1 ; 开启锁
.text:004048E7 E8 EE FD FF FF call @KiReadyThread@4 ; 把线程加入就绪列表中
.text:004048E7
.text:004048EC B1 01 mov cl, 1
.text:004048EE E8 31 00 00 00 call SwapContext ; 进行线程环境交换
.text:004048EE
.text:004048F3 8B 2C 24 mov ebp, [esp]
.text:004048F6 8B 7C 24 04 mov edi, [esp+4]
.text:004048FA 8B 74 24 08 mov esi, [esp+8]
.text:004048FE 83 C4 0C add esp, 0Ch
.text:004048FE
.text:00404901
.text:00404901 locret_404901: ; CODE XREF: .text:004048B3↑j
.text:00404901 C3 retn
3.然后判断KPCR是否选出下个进程,如果没有选出,则直接返回,如果选出则把选出的线程放入CurrentThread中,并通过KiReadyThread把线程放入就绪队列中,然后再通过SwapContext进行环境切换
KiReadyThread的逆向
.text:004046DA 8B FF mov edi, edi
.text:004046DC 55 push ebp
.text:004046DD 8B EC mov ebp, esp
.text:004046DF 51 push ecx
.text:004046E0 51 push ecx
.text:004046E1 8B C1 mov eax, ecx
.text:004046E3 8D 88 28 01 00 00 lea ecx, [eax+_ETHREAD.Tcb.Preempted]
.text:004046E9 53 push ebx
.text:004046EA 8A 19 mov bl, [ecx]
.text:004046EC C6 01 00 mov byte ptr [ecx], 0 ; 设置抢占位为空
.text:004046EF 8B 15 00 B0 47 00 mov edx, ds:_KeTickCount.LowPart ; 获取当前时间
.text:004046F5 0F BE 48 33 movsx ecx, [eax+_ETHREAD.Tcb.Priority] ; 获取优先级
.text:004046F9 56 push esi
.text:004046FA 89 50 68 mov [eax+_ETHREAD.Tcb.WaitTime], edx ; 设置等待时间
.text:004046FD 8B 50 44 mov edx, [eax+_ETHREAD.Tcb.ApcState.Process]
.text:00404700 57 push edi
.text:00404700
.text:00404701
.text:00404701 loc_404701: ; CODE XREF: KiReadyThread(x)-7↑j
.text:00404701 80 7A 65 00 cmp [edx+_EPROCESS.Pcb.State], ProcessInMemory ; 判断进程的状态是否处于内存中
.text:00404705 0F 85 18 19 03 00 jnz loc_436023 ; 如果被换出内存
.text:00404705
.text:0040470B 80 B8 2A 01 00 00 00 cmp [eax+_ETHREAD.Tcb.KernelStackResident], 0 ; 判断线程是否驻留在内存中
.text:00404712 0F 84 1B CB 00 00 jz loc_411233 ; 如果被换出内存
.text:00404712
.text:00404718 C6 40 2D 03 mov [eax+_ETHREAD.Tcb.State], 3 ; 设置线程为备用状态
.text:0040471C 83 3D E4 AF 47 00 00 cmp ds:_KiIdleSummary, 0 ; 判断KPCR是否活动状态
.text:00404723 8B 3D C0 30 48 00 mov edi, ds:_KiProcessorBlock
.text:00404729 0F 85 4A A3 00 00 jnz loc_40EA79
.text:00404729
.text:0040472F 8B 57 08 mov edx, [edi+_KPRCB.NextThread]
.text:00404732 85 D2 test edx, edx ; 判断下个线程是否为空
.text:00404734 0F 85 6A FF FF FF jnz loc_4046A4
.text:00404734
.text:0040473A 8B 57 04 mov edx, [edi+_KPRCB.CurrentThread]
.text:0040473D 0F BE 72 33 movsx esi, [edx+_ETHREAD.Tcb.Priority] ; 获取当前线程的优先级
.text:00404741 3B CE cmp ecx, esi ; 比较优先级
.text:00404743 0F 8F 5C 8C 00 00 jg loc_40D3A5
.text:00404743
.text:00404749
.text:00404749 loc_404749: ; CODE XREF: KiReadyThread(x)-30↑j
.text:00404749 C6 40 2D 01 mov [eax+_ETHREAD.Tcb.State], 1
.text:0040474D 83 C0 60 add eax, 60h ; '`'
.text:00404750 84 DB test bl, bl
.text:00404752 8D 14 CD A0 3A 48 00 lea edx, _KiDispatcherReadyListHead[ecx*8] ; 找到线程优先级的调度链表
.text:00404759 0F 85 A6 D7 FF FF jnz loc_401F05
.text:00404759
.text:0040475F 8B 72 04 mov esi, [edx+4]
.text:00404762 89 10 mov [eax], edx
.text:00404764 89 70 04 mov [eax+4], esi
.text:00404767 89 06 mov [esi], eax
.text:00404769 89 42 04 mov [edx+4], eax ; 设置优先级
.text:00404769
.text:0040476C
.text:0040476C loc_40476C: ; CODE XREF: KiReadyThread(x)-27C9↑j
.text:0040476C 33 C0 xor eax, eax
.text:0040476E 40 inc eax
.text:0040476F D3 E0 shl eax, cl
.text:00404771 09 05 EC AF 47 00 or ds:_KiReadySummary, eax
.text:00404771
.text:00404777
.text:00404777 loc_404777: ; CODE XREF: KiReadyThread(x)-27DA↑j
.text:00404777 ; KiReadyThread(x)+CB8D↓j
.text:00404777 ; KiReadyThread(x)+3196B↓j
.text:00404777 ; .text:00446BFD↓j
.text:00404777 5F pop edi
.text:00404778 5E pop esi
.text:00404779 5B pop ebx
.text:0040477A C9 leave
.text:0040477B C3 retn
.text:0040477B
.text:0040477B @KiReadyThread@4 endp
KiQuantumEnd 逆向
.text:00410446 8B FF mov edi, edi
.text:00410448 55 push ebp
.text:00410449 8B EC mov ebp, esp
.text:0041044B 51 push ecx
.text:0041044C 53 push ebx
.text:0041044D 56 push esi
.text:0041044E 57 push edi
.text:0041044F db 3Eh ; KPCR->Prcb
.text:0041044F 3E A1 20 F0 DF FF mov eax, ds:0FFDFF020h
.text:00410455 8B F8 mov edi, eax
.text:00410457 64 A1 24 01 00 00 mov eax, large fs:_KPCR.PrcbData.CurrentThread ; 当前线程
.text:0041045D 8B F0 mov esi, eax
.text:0041045F FF 15 94 06 40 00 call ds:__imp__KeRaiseIrqlToDpcLevel@0 ; 提升IRQL DISPATCH_LEVEL
.text:0041045F
.text:00410465 33 DB xor ebx, ebx
.text:00410467 38 5E 6F cmp [esi+_ETHREAD.Tcb.Quantum], bl
.text:0041046A 88 45 FF mov [ebp+var_1], al
.text:0041046D 7F 40 jg short loc_4104AF ; 如果时间碎片不为空
1.首先判断时间碎片是否为空,如果不为空则判断是否存在下个线程,如果存在则直接返回,不存在则执行KiUnlockDispatcherDatabase再返回
.text:0041046F 8B 46 44 mov eax, [esi+_ETHREAD.Tcb.ApcState.Process]
.text:00410472 38 58 69 cmp [eax+_EPROCESS.Pcb.DisableQuantum], bl
.text:00410475 0F 85 0B 66 03 00 jnz loc_446A86 ; 判断线程优先级
; .text:00446A99↓j
.text:004104AF 8B 77 08 mov esi, [edi+_KPRCB.NextThread]
.text:004104B2 3B F3 cmp esi, ebx ; 判断下一个线程是否为空
.text:004104B4 74 12 jz short loc_4104C8 ; 如果为空
.text:004104B4
.text:004104B6
.text:004104B6 loc_4104B6: ; CODE XREF: KiQuantumEnd()+8A↓j
.text:004104B6 5F pop edi
.text:004104B7 8B C6 mov eax, esi
.text:004104B9 5E pop esi
.text:004104BA 5B pop ebx
.text:004104BB C9 leave
.text:004104BC C3 retn
2.然后判断线程时间碎片是否大于0,如果大于0则判断线程优先级是否0x10,如果大于0x10,设置线程时间碎片为0x7F,然后判断下个线程是否存在,如果不存在执行KiUnlockDispatcherDatabase结束
.text:0041047B loc_41047B: ; CODE XREF: KiQuantumEnd()+36644↓j
.text:0041047B 0F BE 56 33 movsx edx, [esi+_ETHREAD.Tcb.Priority]
.text:0041047F 83 FA 10 cmp edx, 10h
.text:00410482 8A 40 63 mov al, [eax+_EPROCESS.Pcb.ThreadQuantum]
.text:00410485 88 46 6F mov [esi+_ETHREAD.Tcb.Quantum], al ; 把时间碎片放入线程的时间碎片中
.text:00410488 8B C2 mov eax, edx
.text:0041048A 7D 14 jge short loc_4104A0 ; 如果优先级大于等于0x10
.text:0041048A
.text:0041048C 0F BE 4E 6E movsx ecx, [esi+_ETHREAD.Tcb.PriorityDecrement] ; PriorityDecrement
.text:00410490 2B C1 sub eax, ecx ; 优先级-=动态优先级
.text:00410492 0F BE 4E 6C movsx ecx, [esi+_ETHREAD.Tcb.BasePriority] ; 线程的静态优先级
.text:00410496 48 dec eax ; 优先级-1
.text:00410497 3B C1 cmp eax, ecx ; 比较优先级和动态优先级
.text:00410499 7D 02 jge short loc_41049D ; 如果大于
.text:00410499
.text:0041049B 8B C1 mov eax, ecx ; 如果不大于则动态优先级覆盖优先级
.text:0041049B
.text:0041049D
.text:0041049D loc_41049D: ; CODE XREF: KiQuantumEnd()+53↑j
.text:0041049D 88 5E 6E mov [esi+_ETHREAD.Tcb.PriorityDecrement], bl ; 置0动态优先级
.text:0041049D
.text:004104A0
.text:004104A0 loc_4104A0: ; CODE XREF: KiQuantumEnd()+44↑j
.text:004104A0 3B D0 cmp edx, eax ; 判断eax==edx 此时是相等的
.text:004104A2 75 19 jnz short loc_4104BD
.text:004104A2
.text:004104A4 39 5F 08 cmp [edi+_KPRCB.NextThread], ebx ; 判断是否存在下个线程
.text:004104A7 74 29 jz short loc_4104D2 ; 如果不存在则寻址一个就绪线程,然后结束
.text:004104A7
.text:004104A9 88 9E 28 01 00 00 mov [esi+_ETHREAD.Tcb.Preempted], bl ; 设置此线程不被高优先级线程抢占
.text:004104A9
.text:004104AF
.text:004104AF loc_4104AF: ; CODE XREF: KiQuantumEnd()+27↑j
.text:004104AF ; KiQuantumEnd()+80↓j
.text:004104AF ; KiQuantumEnd()+9A↓j
.text:004104AF ; KiQuantumEnd()+A3↓j
.text:004104AF ; KiQuantumEnd()+3664E↓j
.text:004104AF ; .text:00446A99↓j
.text:004104AF 8B 77 08 mov esi, [edi+_KPRCB.NextThread]
.text:004104B2 3B F3 cmp esi, ebx ; 判断下一个线程是否为空
.text:004104B4 74 12 jz short loc_4104C8 ; 如果为空
.text:004104B4
.text:004104B6
.text:004104B6 loc_4104B6: ; CODE XREF: KiQuantumEnd()+8A↓j
.text:004104B6 5F pop edi
.text:004104B7 8B C6 mov eax, esi
.text:004104B9 5E pop esi
.text:004104BA 5B pop ebx
.text:004104BB C9 leave
.text:004104BC C3 retn
3.如果不大于0X10,则刷新线程的时间碎片,再判断优先级是否大于0x10,然后查看此时的线程动态优先级是否大于优先级,如果大于则覆盖,动态优先级清空,然后执行到不大于的步骤中,如果不大于则判断是否存在下个进程,如果不存在则寻址下个就绪线程,如果存在设置此线程不能被高优先级的线程抢占,然后结束,如果存在则执行KiUnlockDispatcherDatabase结束