问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

创建第一个Windows驱动程序

创作时间:
作者:
@小白创作中心

创建第一个Windows驱动程序

引用
CSDN
1.
https://m.blog.csdn.net/zhaotianff/article/details/142334068

本教程将指导你使用Visual Studio 2022创建并运行你的第一个Windows驱动程序。通过这个简单的示例,你将了解驱动程序的基本结构和开发流程。

使用Visual Studio 2022创建一个Empty WDM Driver工程

工程创建后,添加一个MyFirstDriver.cpp文件,输入以下内容

 1 #include<ntddk.h>
 2 
 3 VOID DriverUnload(PDRIVER_OBJECT DriverObject)
 4 {
 5 if (DriverObject != NULL)
 6 {
 7 DbgPrint("Driver Unload...Driver Object Address: %p\n", DriverObject);
 8 }
 9 
10 return;
11 }
12 
13 extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
14 {
15 DbgPrint("Hello World\n");
16 
17 if (RegistryPath != NULL)
18 {
19 DbgPrint("Driver RegistryPath: %wZ\n", RegistryPath);
20 }
21 
22 if (DriverObject != NULL)
23 {
24 DbgPrint("Driver Object Address: %p\n", DriverObject);
25 DriverObject->DriverUnload = DriverUnload;
26 }
27 
28 return STATUS_SUCCESS;
29 }

这里需要注意的是,可以直接创建后缀为.c的文件,如果后缀是.cpp,需要在DriverEntry前增加extern "C",否则会报下面的错

DriverEntry函数必须具有C语言的链接方式,但是C++编译默认的方式不是C的。

下面详细介绍代码中的内容

ntddk.h

内核开发所需要的头文件

DriverEntry

和控制台的Main函数一样,驱动的入口函数就是DriverEntry。这个函数会被系统线程在IRQLPASSIVE_LEVEL (0)上调用(IRQL会在后面详细讨论)。

DriverEntry函数的原型如下:

1 NTSTATUS DriverEntry(PDRIVER_OBECT DriverObject, PUNICOOE_STRING RegistryPath);

第一个参数为DriverObject,表示一个驱动对象的指针,可以简单认为,一个驱动文件(sys)运行之后,操作系统在内存中为该驱动分配了一个类型为DRIVER_OBJECT的数据结构,用于记录该驱动的详细信息

第二个参数RegistryPath,是一个类型为UNICODE_STRING的指针,表示当前驱动所对应的注册表位置。UNICODE_STRING是内核中表示字符串的结构体,对应定义如下:

typedef struct _UNICODE_STRING {
 USHORT Length;
 USHORT MaximumLength;
 PWCH Buffer; //PWCH -> wchar_t* 不要求以'\0'结束 
} UNICODE_STRING

因为内核驱动是作为Windows服务( Service)存在的,Windows系统有众多服务,如果从服务运行的环境来分区,服务分为用户态服务,以及内核态服务,但无论何种服务,都统称为“服务( Service)”,不同服务通过服务的名字来识别。一个驱动SYS文件需要运行(加载到内核中),首先需要把这个驱动文件注册(创建)成一个服务(第三方服务),注册成功后,系统会把该服务信息写入到注册表HKEY_LOCAL_MACHINE \ SYSTEM \CurrentControlSet lServices下,以服务的名字作为一个注册表的键名。

如本示例中的驱动注册成功后可以在注册表中看到如下键值

返回值NTSTATUS,NTSTATUS实际是一个LONG类型,定义如下:

1 typedef LONG NTSTATUS;

DriverEntry返回STATUS_SUCCESS表示成功,返回其他值表示失败。

STATUS_SUCCESS定义如下:

1 #define STATUS_SUCCESS ((NTSTATUS)0x00000000L) // ntsubauth

简单来说,内核驱动作为Windows服务运行,在执行具体代码前,驱动SYS文件首先会被映射到内核地址空间,作为内核的一个驱动模块(MODULE),接着系统对这个驱动模块执行导入表初始化、修正重定位表中对应的数据偏移等操作,最后系统会调用该驱动模块的DriverEntry 入口函数,如果这个入口函数返回STATUS_SUCCESS,系统认为这个驱动初始化成功;如果这个入口函数返回除STATUS_SUCCESS以外的其他值,系统认为驱动初始化失败,系统执行一系列的清理工作,并把驱动模块从内核空间中移除,从用户态角度看,就是服务启动失败。

DriverUnload

有时候驱动程序需要卸载。在卸载驱动时(关闭服务),DriverObject->DriverUnload函数会被调用,以便执行一些清理操作。需要注意的是,如果未在DriverUnload中执行清理工作,会产生泄漏,在下一次重启之前,内核无法清除这些泄漏。(这一点不像用户层编程,在进程退出后,资源会释放,但是内核层不会自己释放。)

DriverUnload函数非常重要,但DriverUnload函数是可选的,开发者可以不提供DriverUnload函数,这样做的结果是该驱动不支持停止,也就是说,只要开发者不提供DriverUnload函数,这个驱动对应的服务一旦启动后,再也无法停止。该特性被很多安全软件利用,刻意不提供DriverUnload函数,避免驱动被恶意停止。

需要注意的是:

驱动初始化失败不会触发DriverUnload函数的调用,DriverUnload只有在驱动服务成功启动(初始化)后,被要求停止时才会触发。

DbgPrint函数

DbgPrint函数是WDK提供的API,类似用户层的OutputDebugString函数。DbgPrint与C语言的printf使用基本一样。

与DbgPrint函数功能类似的是KdPrint函数,但KdPrint函数只是针对DEBUG版本的驱动有效。

%wZ用来输出以非'\0'结束的字符串

%ws用来输出以'\0'结束的字符串

在上述的示例代码中,对DriverObject的地址和RegistyPath进行了输出。

编译

直接在Visual Studio中编译即可,编译成功后可以在输出目录得到一个MyFirstDriver.sys文件。

加载并运行驱动

按照Windows系统要求,驱动文件必须经过微软的数字签名后,才可以运行在64位系统上。像平常我们在测试阶段是不会为驱动签名的,可以通过下面两种方式绕过系统的验证。

1、启用调试模式

2、禁用驱动程序强制签名

这两种方式都是通过启动设置里的设置来完成的。下次开机时会失效,需要重新设置。

这里以禁用驱动程序强制签名为例

打开开始菜单,在选择重启时,按住shift键

然后就会出现Windows高级启动菜单,依次选择 疑难解答->启动设置->按数字键7,选择禁用驱动程序强制签名

以管理员运行cmd,执行如下命令,创建一个服务(driverpath需要替换成上面工程生成的.sys文件路径)

1 sc create MyFirstDriver binPath= "driverpath" type= kernel

创建成功后,用管理员权限运行DebugView.exe(SysInternals工具包里的一个工具),DebugView工具可以查看DbgPrint的输出内容。

在菜单中钩选相应选项,如下图所示

此时,我们可以运行驱动(启动服务)

在控制台输入

1 sc start MyFirstDriver

可以在DebugView看到DriverEntry函数里的输出内容

我们再卸载驱动(停止服务)

在控制台输入

1 sc stop MyFirstDriver

可以在DebugView看到DriverUnload函数的输出内容

至此,一个简单的驱动程序已经编写完成了,目前我们暂时先不考虑内核开发的任何理论支撑,仅仅写一个HelloWorld一样的程序。

示例代码

题外话:

因为以前也没接触过内核开发,在书上看到使用KeBugCheckEx()函数可以主动引发蓝屏,试了一下,确实可以,还挺好玩的。

1 KeBugCheckEx(INVALID_DATA_ACCESS_TRAP, NULL, NULL, NULL, NULL); 

参考资料:

Windows内核开发示例代码

GitHub - microsoft/Windows-driver-samples: This repo contains driver samples prepared for use with Microsoft Visual Studio and the Windows Driver Kit (WDK). It contains both Universal Windows Driver and desktop-only driver samples.

驱动程序概述

创建第一个驱动程序

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号