Windows内核编程(一):基础知识和第一个程序
这是Windows内核编程的第一篇博客。
基础知识
内核模块的扩展名一般是.sys,但是这个是非强制的,可以是其他扩展名,也可以没有扩展名。
动可以分为不同的类型,如NT驱动、WDM驱动、WDF驱动等。简单来说,NT驱动是最简单的驱动模型,不支持硬件特性;WDM驱动是在NT驱动基础上引入的一套驱动模型,支持即插即用、电源事件等特性;WDF驱动是对WDM驱动的封装与升级,屏蔽了部分细节,简化了大量接口。这三种类型的驱动大同小异,但NT驱动是所有驱动类型中的基础。
第一个程序
#define _AMD64_
#include <ntddk.h>
// 为DbgPrint函数提供定义
#pragma comment(lib, "ntoskrnl.lib")
VOID DriverUnload(PDRIVER_OBJECT DriverObject)
{
if (DriverObject != NULL)
{
//ws表示以'\\0'结束的UNICODE字符串,__FUNTIONW__表示当前函数的名字(UNICODE)
DbgPrint("[%ws]Driver Unload, Driver Object Address:%p", __FUNCTIONW__, DriverObject);
}
return;
}
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
DbgPrint("[%wd]Hello Kernel World\\n", __FUNCTIONW__);
if (RegistryPath != NULL)
{
// wZ表示UNICODE_STRING类型
DbgPrint("[%ws]Driver RegistryPath:%wZ\\n", __FUNCTIONW__, RegistryPath);
}
if (DriverObject != NULL)
{
DbgPrint("[%ws]Driver Object Address:%p\\n", __FUNCTIONW__, DriverObject);
DriverObject->DriverUnload = DriverUnload;
}
return STATUS_SUCCESS;
}
入口函数的参数
内核驱动程序有一个统一的入口函数:DirverEntry。它有两个参数:

请注意,Buffer指向的字符串,并不要求以’\\0’作为结束,在大多数情况下,Buffer指向的字符串没有以’\\0’结尾。
为什么需要第二个参数?内核驱动是作为Windows系统服务(Service)而存在的。服务是一种特殊的应用程序,符合特殊的接口形式要求,操作系统许多重要功能依赖于服务。不同的服务通过服务的名字来识别,简称“服务名”。一个驱动文件需要运行(加载到内核中),首先要把这个驱动文件注册成一个服务,注册成功后,系统会把该服务的信息写入到注册表HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services 下 ,以服务的名字作为一个注册表的键名。
入口函数的返回值
DriverEntry返回值值实际上是一个LONG类型,Windows规定返回STATUS_SUCCESS表示成功,其它返回值表示失败。

内核服务作为Windows服务运行,在执行具体代码前,驱动文件SYS首先会被映射到内核地址空间,作为内核的一个驱动模块(MODULE),接着操作系统会对这个驱动模块执行加载操作,最后系统会调用驱动模块的DriverEntry函数,根据函数的返回值来判断驱动是否初始化成功。如果返回失败,系统会执行一系列清理工作,把驱动模块从内核空间中移除。
DriverUnload
上面的代码中定义了DriverUnload函数,并且把DriverObject的DriverUnload初始化为我们定义的同名函数。
前面提过,驱动是作为服务方式运行的,服务可以被启动,也可以被停止,停止的实质就是系统把该驱动模块对应在内核地址空间中的 代 码 以 及 数 据 移 除 。 当 一 个 内 核 驱 动 被 要 求 停 止 时 ,DriverObject→DriverUnload指向的函数就会被系统调用,开发者可以在这个函数中执行一些清理相关的工作。
DriverUnload函数非常重要,但重要并不等于必须,DriverUnload函数是可选的,开发者可以不提供DriverUnload函数,这样做的结果是该驱动不支持停止,也就是说,只要开发者不提供DriverUnload函数,这个驱动对应的服务一旦启动后,再也无法停止。该特性被很多安全软件利用,刻意不提供DriverUnload函数,避免驱动被恶意停止。
请注意,如果DriverEntry函数返回初始化失败的返回值,DriverUnload函数不会被调用。该函数只有在服务初始化成功后被要求停止时才会触发。
通过命令行编译驱动程序
笔者使用的是cl和link工具链,下面是makefile:
CFLAGS = /nologo /c /Od /kernel /GS- /Gs9999999 /Zi /D_WIN4 /D_KERNEL_NODE_
LFLAGS = /nologo /SUBSYSTEM:NATIVE /DRIVER /ENTRY:DriverEntry
all:First.sys
First.obj : First.c
cl $(CFLAGS) /Fo:First.obj First.c
First.sys : First.obj
link $(LFLAGS) /OUT:First.sys First.obj
执行nmake命令即可编译,也可以使用cmake生成visual studio解决方案,使用msbuild进行生成。




