(新手原创,欢迎转载,转载请注明出处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