谈到定时器在ring3下有一个,启动和关闭都比较简单,通过两个api函数SetTimer和KillTimer来完成的。对于ring0中的定时器,稍微复杂一些。
Ring0 下的定时器有两个,今天我们先看一个。
即:利用I/O定时器例程实现的。用于进行固定的1秒时间间隔的操作定时。对应的函数有三个。
在IoInitializeTimer 函数中,我们设置回调函数,回调函数定义格式如下:
VOID (*PIO_TIMER_ROUTINE) (
IN PDEVICE_OBJECT DeviceObject,
IN PVOID Context
);
说明如下:
对于这三个函数,我们用windbg看看都作了些什么工作。
一、NTSTATUS IoInitializeTimer(
IN PDEVICE_OBJECT DeviceObject,
IN PIO_TIMER_ROUTINE TimerRoutine,
IN PVOID Context
);
typedef struct _IO_TIMER
{
SHORT Type;
SHORT TimerFlag;
LIST_ENTRY TimerList;
PVOID TimerRoutine;
PVOID Context;
PDEVICE_OBJECT DeviceObject;
} IO_TIMER, *PIO_TIMER;
lkd> u IoInitializeTimer l 50
nt!IoInitializeTimer:
80569792 8bff mov edi,edi
80569794 55 push ebp
80569795 8bec mov ebp,esp
80569797 56 push esi
80569798 8b7508 mov esi,dword ptr [ebp+8] ;参数1,DeviceObject
8056979b 8b5618 mov edx,dword ptr [esi+18h];DeviceObject->Timer
8056979e 85d2 test edx,edx
;判断DEVICE_OBJECT中的Timer是否为null. 如果为空则为其申请内存,然后再初始化
805697a0 7530 jne nt!IoInitializeTimer+0x40 (805697d2)
805697a2 68496f5469 push 69546F49h ;"IoTI"
805697a7 6a18 push 18h
805697a9 52 push edx
805697aa e8d1bcfdff call nt!ExAllocatePoolWithTag (80545480)
805697af 8bd0 mov edx,eax
805697b1 85d2 test edx,edx
805697b3 7507 jne nt!IoInitializeTimer+0x2a (805697bc)
;如果申请内存不成功则,直接返回STATUS_INSUFFICIENT_RESOURCES
805697b5 b89a0000c0 mov eax,0C000009Ah
805697ba eb36 jmp nt!IoInitializeTimer+0x60 (805697f2)
;内存申请成功,则初始化申请的24字节内存为0,IO_TIMER大小为24字节
805697bc 57 push edi
805697bd 6a06 push 6
805697bf 59 pop ecx
805697c0 33c0 xor eax,eax
805697c2 8bfa mov edi,edx
805697c4 f3ab rep stos dword ptr es:[edi]
805697c6 66c7020900 mov word ptr [edx],9 ;设置IO_TIMER.Type
;将参数1,DeviceObject赋值给IO_TIMER.DeviceObject
805697cb 897214 mov dword ptr [edx+14h],esi
;DeviceObject->Timer 指向刚申请的IO_TIMER内存
805697ce 895618 mov dword ptr [esi+18h],edx
805697d1 5f pop edi
;开始初始化DeviceObject->Timer
805697d2 8b450c mov eax,dword ptr [ebp+0Ch] ;取TimerRoutine参数
;设置IO_TIMER.TimerRoutine = 参数2,TimerRoutine
805697d5 89420c mov dword ptr [edx+0Ch],eax
;取参数3 Context
805697d8 8b4510 mov eax,dword ptr [ebp+10h]
;设置IO_TIMER.Context = 参数3 Context
805697db 894210 mov dword ptr [edx+10h],eax
805697de 6844b35480 push offset nt!IopTimerLock (8054b344) ;参数3
805697e3 83c204 add edx,4 ;参数2
805697e6 b9601e5580 mov ecx,offset nt!IopTimerQueueHead (80551e60) ;参数1
805697eb e8f893fdff call nt!ExfInterlockedInsertTailList (80542be8);调用
805697f0 33c0 xor eax,eax
805697f2 5e pop esi
805697f3 5d pop ebp
805697f4 c20c00 ret 0Ch
下面看看ExfInterlockedInsertTailList函数:
PLIST_ENTRY _fastcall
ExfInterlockedInsertTailList(PLIST_ENTRY ListHead , PLIST_ENTRY
ListEntry , PKSPIN_LOCK Lock ) ;
由于采用fastcall调用约定, ecx = ListHead ,edx = ListEntry ,[ebp+8] = 参数3
该函数功能是把IO_TIMER.TimerList加入对象链表。
nt!ExfInterlockedInsertTailList:
80542be8 9c pushfd
80542be9 fa cli
;eax = ListHead.Blink
80542bea 8b4104 mov eax,dword ptr [ecx+4]
; ListEntry.Flink = ListHead
80542bed 890a mov dword ptr [edx],ecx
; ListEntry.Blink = ListHead.Blink
80542bef 894204 mov dword ptr [edx+4],eax
;ListHead.Blink = ListEntry
80542bf2 895104 mov dword ptr [ecx+4],edx
;ListHead.Blink Flink = ListEntry
80542bf5 8910 mov dword ptr [eax],edx
80542bf7 9d popfd
80542bf8 33c1 xor eax,ecx
80542bfa 7402 je nt!ExfInterlockedInsertTailList+0x16 (80542bfe)
80542bfc 33c1 xor eax,ecx
80542bfe c20400 ret 4
其中,ListHead 对应于
805697e6 b9601e5580 mov ecx,offset nt!IopTimerQueueHead (80551e60) ;参数1
这个ListHead是:
lkd> dd 80551e60
80551e60 89aaf434 8987852c
其中:ListHead.Flink = 89aaf434, ListHead.Blink = 8987852c
二、VOID IoStartTimer(
IN PDEVICE_OBJECT DeviceObject
);
lkd> u IoStartTimer l 30
nt!IoStartTimer:
804f02da 8bff mov edi,edi
804f02dc 55 push ebp
804f02dd 8bec mov ebp,esp
804f02df 8b4d08 mov ecx,dword ptr [ebp+8] ;取参数
804f02e2 8b4118 mov eax,dword ptr [ecx+18h];取DeviceObject->Timer
;取DeviceObject->DeviceObjectExtension
804f02e5 8b89b0000000 mov ecx,dword ptr [ecx+0B0h]
;DeviceObject->DeviceObjectExtension.ExtensionFlags = 0Fh
804f02eb f641100f test byte ptr [ecx+10h],0Fh
804f02ef 7515 jne nt!IoStartTimer+0x2c (804f0306)
804f02f1 fa cli
;DeviceObject->Timer.TimerFlag与0比较
804f02f2 6683780200 cmp word ptr [eax+2],0
804f02f7 750c jne nt!IoStartTimer+0x2b (804f0305) ; != 0跳
;DeviceObject->Timer.TimerFlag = 1
804f02f9 66c740020100 mov word ptr [eax+2],1
;计数变量+1
804f02ff ff05f41d5580 inc dword ptr [nt!IoAdapterObjectType+0x4 (80551df4)]
804f0305 fb sti
804f0306 5d pop ebp
804f0307 c20400 ret 4
三,VOID IoStopTimer(
IN PDEVICE_OBJECT DeviceObject
);
lkd> u IoStopTimer l 20
nt!IoStopTimer:
804f0310 8bff mov edi,edi
804f0312 55 push ebp
804f0313 8bec mov ebp,esp
804f0315 8b4508 mov eax,dword ptr [ebp+8] ;取参数
804f0318 8b4018 mov eax,dword ptr [eax+18h];取DeviceObject->Timer
804f031b fa cli
;DeviceObject->Timer.TimerFlag与0比较
804f031c 6683780200 cmp word ptr [eax+2],0
804f0321 740b je nt!IoStopTimer+0x1e (804f032e) ;=0跳
;DeviceObject->Timer.TimerFlag = 0
804f0323 6683600200 and word ptr [eax+2],0
;计数变量-1
804f0328 ff0df41d5580 dec dword ptr [nt!IopTimerCount (80551df4)]
804f032e fb sti
804f032f 5d pop ebp
804f0330 c20400 ret 4
总结:通过上述分析可以看出这个定时器跟一个驱动程序创建的设备对象相关联,一个设备只能对应一个这样的定时器。定时器的启停在于DeviceObject->Timer.TimerFlag值。并且这个Timer位于DeviceObject->Timer。