内核中实现简单定时器(练习代码)

本文分享了一个基于Windows内核的定时器实现案例,通过线程和KeWaitForSingleObject函数,作者详细介绍了如何在内核模式下创建和管理定时器,包括创建定时器线程、设置间隔时间、定时调用函数以及定时器的销毁过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

(新手原创,欢迎转载,转载请注明出处https://blog.youkuaiyun.com/qq_29730023/article/details/81298782

在《Windows 内核安全与驱动开发》64页,作者给出练习用线程实现定时器。本人尝试写了一下,因为初学所以多次蓝屏。Windbg单步定位问题在KeWaitForSingleObject的调用上,原以为和用户态一样等待线程句柄,百度后在看雪的一篇求助贴找到了答案,帖子中也提到了用ZwWaitxxxx系列函数可以简化,但是为了加深印象还是使用KeWaitForSingleObject来完成了该练习。

代码如下(初学勿笑)

// Timer.c

#include <ntddk.h>

// 定时器例程类型
typedef VOID (*CUSTOM_TIMER_ROUTINE)();

// 定时器结构
typedef struct {
	LARGE_INTEGER Interval;				// 间隔的时间
	CUSTOM_TIMER_ROUTINE Routine;			// 定时调用的函数指针
	PVOID Thread;					// 线程句柄
	KEVENT Exit;					// 退出通知
}CUSTOM_TIMER, *PCUSTOM_TIMER;

// 全局定时器
PCUSTOM_TIMER g_Timer = NULL;

// 执行定时器线程
VOID TimerThread (PVOID context) {
	UNREFERENCED_PARAMETER (context);

	// 等待间隔时间的通知,无通知则循环。没有用KeDelayExecutionThread
	// 因为调用KeDelayExecutionThread,每次卸载时都有间隔时间内的卡顿
	// 而等待系列函数只要获得通知会立即返回,这也是我在用户模式常用的方法
	while (STATUS_SUCCESS != KeWaitForSingleObject (
		&g_Timer->Exit, Executive, KernelMode, FALSE, &g_Timer->Interval)) {
		g_Timer->Routine ();
	}
	KdPrint (("安全退出\n"));

	// 似乎不用调用PsTerminateSystemThread (STATUS_SUCCESS)也退出了
}

// 创建定时器
BOOLEAN CreateTimer (LONG MilliSecond, CUSTOM_TIMER_ROUTINE routine) {
	KdPrint (("创建\n"));
	
	BOOLEAN bOK = TRUE;
	NTSTATUS status = STATUS_SUCCESS;
	HANDLE hThread = NULL;

	g_Timer = (PCUSTOM_TIMER)ExAllocatePool (NonPagedPool, sizeof (CUSTOM_TIMER));
	if (NULL == g_Timer) {
		bOK = FALSE;
		goto RETURN;
	}

	g_Timer->Interval.QuadPart = -10000 * MilliSecond;
	g_Timer->Routine = routine;
	KeInitializeEvent (&g_Timer->Exit, NotificationEvent, FALSE);

	status = PsCreateSystemThread (
		&hThread,
		0,
		NULL,
		NULL,
		NULL,
		TimerThread,
		NULL
	);
	if (!NT_SUCCESS (status)) {
		bOK = FALSE;
		goto RETURN;
	}
	
	status = ObReferenceObjectByHandle (
		hThread,
		EVENT_ALL_ACCESS,
		NULL,
		KernelMode,
		&g_Timer->Thread,
		NULL
	);
	if (!NT_SUCCESS (status)) {
		bOK = FALSE;
		goto RETURN;
	}

RETURN:
	if (!bOK && NULL != g_Timer) {
		ExFreePool (g_Timer);
		g_Timer = NULL;
	}
	if (NULL != hThread)
		ZwClose (hThread);

	return bOK;
}

// 销毁定时器
VOID DestroyTimer () {
	KdPrint (("销毁\n"));

	if (NULL != g_Timer) {
		KeSetEvent (&g_Timer->Exit, IO_NO_INCREMENT, FALSE);
		KeWaitForSingleObject (g_Timer->Thread, Executive, KernelMode, FALSE, NULL);
		ObDereferenceObject (g_Timer->Thread);
		ExFreePool (g_Timer);
		g_Timer = NULL;
	}
}

// 定时器例程实现
VOID TimerFunc () {
	// 简单的打印计数
	static int count = 0;
	KdPrint (("%d\n", ++count));
}

// 驱动卸载
VOID DriverUnload (PDRIVER_OBJECT DriverObject) {
	UNREFERENCED_PARAMETER (DriverObject);
	DestroyTimer ();
}

// 驱动入口
NTSTATUS DriverEntry (PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
	UNREFERENCED_PARAMETER (DriverObject);
	UNREFERENCED_PARAMETER (RegistryPath);

	DriverObject->DriverUnload = DriverUnload;

	if(CreateTimer (5000, TimerFunc))
		KdPrint (("创建成功:%#x\n", g_Timer));
	else
		KdPrint (("创建失败:%#x\n", g_Timer));

	return STATUS_SUCCESS;
}

模块启动前截图

模块运行中截图

模块卸载后截图

注意红色箭头的线程数增减,说明了线程的创建和退出。以上截图虽然是三个工具同框,但是Monitor和任务管理器是在虚拟机中,Windbg在主机。

编译环境:Windows 10 + Visual Studio 2017 + WDK17134

运行环境:Virtual Box + Windows 7

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值