直接采用vs2019中的Empty WDM Driver 模块建立:程序员
初始的项目文件夹中有一个Driver Files里面会有一个.inf的文件,没用直接删除就好,而后在源文件里面建立一个.cpp的源文件。windows
每一个驱动都有一个入口点,叫作DriverEntry,就比如日常写的C/C++代码里面的main函数。DriverEntry是由一个叫作IRQL_PASSIVE_LEVEL(0)的系统进程调用出来的。DriverEntry函数原型:函数
NTSTATUS
DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING
RegistryPath);
代码原型里的_In _是源代码注释语言(SAL)中的一部分,用来描述函数如何使用其参数,SAL对于编译器来讲能够直接忽略,可是对程序员颇有帮助。工具
相关连接:学习
这里的最小的DriverEntry示例能够只返回一个状态,好比:测试
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
return STATUS_SUCCESS;
}
该DriverEntry函数包含在<ntddk.h>头文件中,可是添加了头文件后仍然是编译失败的,由于编译器会把警告当场错误来报错,可是不建议删除该功能,由于有时候警告就是会致使错误诞生:spa
能够对应修改这些警告,好比这里将形参删除,可是这样仅对于C++好用,由于C++有函数重载,因此这里用不上。这里有一个很经典的解决办法,就是采用一个宏函数:命令行
UNREFERENCED_PARAMETER();
这样就能够暂时解决掉前面的报错说形参没有使用了:debug
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
UNREFERENCED_PARAMETER(DriverObject);
UNREFERENCED_PARAMETER(RegistryPath);
return STATUS_SUCCESS;
}
可是这样仍然不行:3d
能够很明显得看出,编译器没问题,可是Linker连接器出了问题,DriverEntry是一个C函数,必须用C的linker来link,可是这里咱们采用的是C++的默认,因此必须给该函数设置为C的默认LInker才行
。
extern "C"
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
UNREFERENCED_PARAMETER(DriverObject);
UNREFERENCED_PARAMETER(RegistryPath);
return STATUS_SUCCESS;
}
这样最简单的驱动程序的源代码就写好了,就至关于C语言中的:
#include<stdio.h>
int main()
{
return 0;
}
这是一个驱动的卸载函数,就至关于C++中类的析构函数同样,当驱动被卸载的时候就会自动调用该函数。在DriverEntry函数中建立的东西须要由Unload Routines来释放,这就很是像C++类中的构造函数和析构函数的关系了。若是没有该函数来释放驱动加载时所开辟的内容就会致使泄露,直到下次电脑重启时内核产生的泄露才会清楚。
该函数的函数指针,必须在DriverEntry给DriverEntry的参数DriverObject中的DriverUnload字段赋值才行。Unload函数和DriverEntry函数同样都须要接受一个_In _ PDRIVER_OBJECT DriverObject 参数,可是Unload函数不须要返回值,直接用void 定义就好。
好比:
#include <ntddk.h>
void SampleUnload(_In_ PDRIVER_OBJECT DriverObject)
{
UNREFERENCED_PARAMETER(DriverObject);
}
extern "C"
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
DriverObject->DriverUnload = SampleUnload;
UNREFERENCED_PARAMETER(RegistryPath);
return STATUS_SUCCESS;
}
就是一个很是简单可是能够用的驱动了。
前面已经写好了一个驱动程序,可是咱们还须要把它跑起来,驱动程序不像平时写的普通程序同样,采用IDE就能够正常使用了,须要将其加载到系统里面,一般为了不风险,采用虚拟机来部署驱动程序。
安装驱动程序就像是在User用户态安装服务.exe同样,须要调用CreateService API或者采用现有的工具,这里采用比较经常使用的Sc.exe来进行部署内核驱动。
采用管理员权限的命令行:
sc create sample type=kernel binPath=C:\DriverTest\MyDriver3.sys
其中 sample是建立的名字,而后type表示建立的权限,binPath后面的是驱动的路径。若是没问天会弹出一个成功的标识符。
而且能够在注册表里面查到:
使用Win+R的弹出框里面输入regedit.exe,查看路径\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\sample,这就是咱们刚刚写好且用sc创造的驱动:
启动驱动
前面是注册了该驱动,还得使用它。这里也和User用户态的服务Service相似,须要采用StartService API来使用或者采用一些软件,这里也能够用Sc.exe来继续使用。
sc start sample
这里的sample就是前面注册的驱动的名字。
可是这个一般会失效,由于对于64bit位的windows系统,加载驱动必须得要有驱动的签名才行,这里为了学习方便,避开签名这个东西,能够直接把系统置为测试版本。
bcdedit /set testsigning on
若是你要生成除了Windows10之外的版本,能够在项目属性里面配置你的驱动要部署在的系统环境里面:
最后再使用前面sc来加载驱动时会看到一个关于驱动的输出:
有了这个输出就代表咱们的驱动已经成功加载了,可使用Process Explorer工具来确认是否加载成功(下载地址:https://docs.microsoft.com/zh-cn/sysinternals/downloads/process-explorer)
这里的驱动名称是你本身的sys驱动名称。
不用了将驱动程序卸下,一样的能够采用 ControlService API或者Sc.exe来处理。Sc指令:
sc stop sample
就OK了。
为了确保函数有确切被调用,这里提供一种基础的跟踪办法来确保函数被使用,驱动采用KdPrint这个宏来输出相似于printf风格的文本,该宏的内容能够被内核的调试器,或者其它工具查看到。
KdPrint这个宏只在debug模式下采用,它的底层调用的实际上是DbgPrint 内核Kernel API。
下面更新一下DriverEntry和Unload函数:
void SampleUnload(_In_ PDRIVER_OBJECT DriverObject)
{
UNREFERENCED_PARAMETER(DriverObject);
KdPrint(("Sample driver Unload called! \n"));
}
NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) {
DriverObject->DriverUnload = SampleUnload;
UNREFERENCED_PARAMETER(RegistryPath);
KdPrint(("Sample driver initialized successfully\n !"));
return STATUS_SUCCESS;
}
须要注意的是该宏函数在调用时采用了两个括号,由于它是一个宏函数,可是又显然它是能够接受任意变量的,因为宏函数不能接受可变的变量参数,因此编译器实际上调用的是DbgPrint函数。这里理解不了不要紧,先这样用着就行。
从新生成驱动并加载来查看这些Print信息,这里须要采用一个内核的调试器才行,可是为了方便,先采用一个系统的内部工具:DebugView来查看。在使用DebugView以前,须要先给它在注册表里面配置内容否则用不上。
在\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\里新建一个Key(项)Key名为DebugPrintFilter,而且添加一个DWORD类型的值名为DEFAULT,这个DEFAULT要和默认的一个值区别开来,后面那个默认的值是注册表中的每个项都有的,那个值暂时先不用管,而后给该名为DEFAULT类型为DWORD的变量赋值为8,以下图所示:
而后下载DebugView(
这样,再使用Sc.exe来从新加载驱动就能够看到KdPrint打印的内容了。
总结Summary