Windows驱动开发入门-时间与线程 时间 KeQueryTickCount 返回自系统启动后经历的步进数,不同硬件环境不同。
1 2 3 VOID KeQueryTickCount ( OUT PLARGE_INTEGER TickCount )
KeQueryTimeIncrement 获取步进100纳秒数。
1 ULONG KeQueryTimeIncrement ()
例子 获得系统启动后经历的毫秒数:
1 2 3 4 5 6 7 8 9 VOID MyGetTickCount (PULONG msec) { LARGE_INTEGER tick_count = { 0 }; ULONG myinc = KeQueryTimeIncrement (); KeQueryTickCount (&tick_count); tick_count.QuadPart *= myinc; tick_count.QuadPart /= 10000 ; *msec = tick_count.LowPart; return ; };
KeQuerySystemTime 获取格林威治时间。
1 2 3 VOID KeQuerySystemTime ( OUT PLARGE_INTEGER CurrentTime )
ExSystemTimeToLocalTime 转换为当地时间。
1 2 3 4 VOID ExSystemTimeToLocalTime ( IN PLARGE_INTEGER SystemTime, OUT PLARGE_INTEGER LocalTime )
RtlTimeToTimeFields 把上面那俩返回的长长整型转换为TIME_FIELDS类型。
1 2 3 4 VOID RtlTimeToTimeFields ( IN PLARGE_INTEGER Time, IN PTIME_FIELDS TimeFields )
例子 返回当前年月日时分秒,因静态变量下面这段代码非多线程安全。
1 2 3 4 5 6 7 8 9 10 11 12 #include <ntstrsafe.h> #pragma comment(lib,"ntstrsafe.lib" ) PWCHAR MyCurTimeStr (VOID) { LARGE_INTEGER snow = { 0 }, now = { 0 }; TIME_FIELDS now_fields = { 0 }; static WCHAR time_str[32 ] = { 0 }; KeQuerySystemTime (&snow); ExSystemTimeToLocalTime (&snow, &now); RtlTimeToTimeFields (&now, &now_fields); RtlStringCchPrintfW (time_str, 32 , TEXT ("%4d-%2d-%2d %2d-%2d-%2d" ), now_fields.Year, now_fields.Month, now_fields.Day, now_fields.Hour, now_fields.Minute, now_fields.Second); return time_str; };
DPC定时器 KeSetTimer 设置一个定时器。
1 2 3 4 5 BOOLEAN KeSetTimer ( IN PKTIMER Timer, IN LARGE_INTEGER DueTime, IN OPTIONAL PKDPC Dpc )
KeInitializeTimer 用来初始化一个上面提到的Timer。
1 2 3 VOID KeInitializeTimer ( KTIMER Timer )
PKDEFERRED_ROUTINE 不是个结构体,这函数指针指向这样一个函数类型,该函数运行在APC中断级别:
1 2 3 4 5 6 VOID CustomDpc ( IN struct _KDPC* Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 )
例子 这其实只是个延时执行罢了,如果要实现定时反复执行的话,需要在每个自定义DPC回调函数最后用KeSetTimer
保证下次还能被执行。
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 #include <ntddk.h> #include <ntstrsafe.h> LONG count = 0 ; KTIMER g_ktimer; KDPC g_kdpc; VOID MyTimerProcess (__in struct _KDPC* Dpc, __in_opt PVOID DeferredContext, __in_opt PVOID SystemArgument1, __in_opt PVOID SystemArgument2) { LARGE_INTEGER la_dutime = { 0 }; la_dutime.QuadPart = 1000 * 1000 * -10 ; InterlockedIncrement (&count); DbgPrint ("DPC 定时执行 = %d" , count); KeSetTimer (&g_ktimer, la_dutime, &g_kdpc); return ; }; VOID UnDriver (PDRIVER_OBJECT driver) { KeCancelTimer (&g_ktimer); DbgPrint (("Uninstall Driver Is OK \n" )); return ; }; NTSTATUS DriverEntry (IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) { DbgPrint ("hello lyshark \n" ); LARGE_INTEGER la_dutime = { 0 }; la_dutime.QuadPart = 1000 * 1000 * -10 ; KeInitializeTimer (&g_ktimer); KeInitializeDpc (&g_kdpc, MyTimerProcess, NULL ); KeSetTimer (&g_ktimer, la_dutime, &g_kdpc); Driver->DriverUnload = UnDriver; return STATUS_SUCCESS; };
IO定时器 IoInitializeTimer 初始化定时器。
1 2 3 4 5 6 7 8 NTSTATUS IoInitializeTimer ( _In_ PDEVICE_OBJECT DeviceObject, _In_ PIO_TIMER_ROUTINE TimerRoutine, _In_opt_ __drv_aliasesMes PVOID Context )
IoStartTimer 启动定时器。
1 2 3 VOID IoStartTimer ( _In_ PDEVICE_OBJECT DeviceObject )
IoStopTimer 关闭定时器。
1 2 3 VOID IoStopTimer ( _In_ PDEVICE_OBEJECT DeviceObject )
例子 精度就到秒级,比DPC的方法精度低。
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 #include <ntddk.h> #include <ntstrsafe.h> LONG count = 0 ; VOID MyTimerProcess (__in struct _DEVICE_OBJECT* DeviceObject, __in_opt PVOID Context) { InterlockedIncrement (&count); DbgPrint ("定时器计数 = %d" , count); LONG preCount = InterlockedCompareExchange (&count, 3 , 0 ); if (preCount == 0 ) DbgPrint ("[LyShark] 三秒过去了 \n" ); return ; }; VOID UnDriver (PDRIVER_OBJECT driver) { IoStopTimer (driver->DeviceObject); IoDeleteDevice (driver->DeviceObject); DbgPrint (("Uninstall Driver Is OK \n" )); return ; }; NTSTATUS DriverEntry (IN PDRIVER_OBJECT Driver, PUNICODE_STRING RegistryPath) { DbgPrint ("hello lyshark \n" ); NTSTATUS status = STATUS_UNSUCCESSFUL; UNICODE_STRING dev_name = RTL_CONSTANT_STRING (L"" ); PDEVICE_OBJECT dev; status = IoCreateDevice (Driver, 0 , &dev_name, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &dev); if (!NT_SUCCESS (status)) return STATUS_UNSUCCESSFUL; else { IoInitializeTimer (dev, MyTimerProcess, NULL ); IoStartTimer (dev); }; Driver->DriverUnload = UnDriver; return STATUS_SUCCESS; };
线程 KeDelayExecutionThread 在线程中睡眠。
1 2 3 4 5 6 7 8 NTSTATUS KeDelayExecutionThread ( IN KPROCESSOR_MODE WaitMode; IN BOOLEAN Alertable; IN PLARGE_INTEGER Interval; )
例子 指定睡眠多少毫秒。
1 2 3 4 5 6 7 8 9 #define DELAY_ONE_MICROSECOND (-10) #define DELAY_ONE_MILLISECOND (DELAY_ONE_MICROSECOND*1000) VOID MySleep (LONG msec) { LARGE_INTEGER my_interval = { 0 }; my_interval.QuadPart = DELAY_ONE_MILLISECOND; my_interval.QuadPart *= msec; KeDelayExecutionThread (KernelMode, FALSE, &my_interval); return ; };
KeInitializeEvent 初始化一个事件,常见事件类型有SynchronizationEvent和NotificationEvent,这两种事件被设置后所有被该事件阻塞的线程都可同时继续进行。这俩事件类型的区别在于,NotificationEvent通知事件类型在被KeSetEvent
设置后将一直保持被设置,直到再次被KeResetEvent
设置为未设。而SynchronizationEvent同步事件类型在KeWaitForSingleObject
后自动转为未设。
1 2 3 4 5 6 7 VOID KeInitializeEvent ( IN PRKEVENT Event; IN EVENT_TYPE Type; IN BOOLEAN State )
KeSetValue 设置事件。
1 2 3 4 5 6 7 LONG KeSetEvent ( IN PRKEVENT Event; IN KPRIORITY Increment, IN BOOLEAN Wait )
KeResetEvent 重设一个NotificationEvent通知事件。
1 2 3 LONG KeResetEvent ( IN PRKEVENT Event )
PsCreateSystemThread 新建系统线程,该线程所在进程名为“System”。
1 2 3 4 5 6 7 8 9 10 11 NTSTATUS PsCreateSystemThread ( OUT PHANDLE ThreadHandle, IN ULONG DesiredAccess, IN OPTIONAL POBJECT_ATTRIBTUES ObjectAttributes, IN OPTIONAL HANDLE ProcessHandle, OUT OPTIONAL PCLIENT_ID ClientId, IN PKSTART_ROUTINE StartRoutine, IN PVOID StartContext )
PKSTART_ROUTINE 系统线程自定义回调函数。线程结束时要PsTerminateSystemThread
,句柄要用ZwClose
关闭。
1 2 3 VOID CustomThreadProc ( IN PVOID context )
例子 如果创建的新线程中使用了原进程中的某些局部变量时,如果原进程函数结束后某些句柄或指针随之失效,新线程则无法调用。所以新线程所用数据尽量在堆上申请,且原线程尽量在新线程结束后结束。这里使用同步事件来实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 static KEVENT s_event = { 0 };VOID MyThreadProc (PVOID context) { PUNICODE_STRING str = (PUNICODE_STRING)context; DbgPrintEx (DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, TEXT ("%wZ\r\n" ), str); KeSetEvent (&s_event, 0 , TRUE); PsTerminateSystemThread (STATUS_SUCCESS); return ; }; VOID MyFunction (VOID) { UNICODE_STRING str = RTL_CONSTANT_STRING (TEXT ("xxx" )); HANDLE thread = NULL ; NTSTATUS status = STATUS_SUCCESS; KeInitializeEvent (&s_event, SynchronizationEvent, TRUE); status = PsCreateSystemThread (&thread, 0 , NULL , NULL , NULL , MyThreadProc, (PVOID)&str); if (!NT_SUCCESS (status)) { }; ZwClose (thread); KeWaitForSingleObject (&s_event, Executive, KernelMode, 0 , 0 ); return ; };