欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 资讯 > 4 驱动开发

4 驱动开发

2025/1/10 0:44:26 来源:https://blog.csdn.net/qicaiyuwu/article/details/144999571  浏览:    关键词:4 驱动开发

环境配置

开发环境

在开发驱动程序之前,我们需要配置好开发环境, 首先安装好VS IDE(这里自己选择版本),其次因为我们需要开发驱动程序所以需要安装WDK(WDK下载地址:以前的 WDK 版本和其他下载 - Windows drivers | Microsoft Learn),在我们安装WDK时候需要注意其版本应与SDK的版本一致。我们可以通过控制面板-程序卸载,找到当前VS IDE安装的SDK版本,如下所示,我的系统上SDK的版本是17763:

images/download/attachments/1015847/image2022-9-1_10-43-35.png

接着你需要在WDK的下载地址中选择对应系统的安装包,由于我当前是Windows 10 21H2版本,但是页面中并没有对应的版本选择,所以我把所有Windows10的WDK安装包都下载下来了:

images/download/attachments/1015847/image2022-9-1_10-45-40.png

安装包依次打开,就找到了与SDK对应版本的WDK安装包,接着按步骤安装即可:

images/download/attachments/1015847/image2022-9-1_10-47-28.png

项目配置

安装好开发环境之后我们打开VS2017,新建项目并选择VC++下的Wimdows Drivers,创建Empty WDM Driver项目:

images/download/attachments/1015847/image2022-9-1_15-13-20.png

创建完项目之后,进入到项目属性,按如下图所示进行配置:

images/download/attachments/1015847/image2022-9-1_15-19-12.png

images/download/attachments/1015847/image2022-9-1_15-19-26.png

images/download/attachments/1015847/image2022-9-1_15-19-42.png

images/download/attachments/1015847/image2022-9-1_15-19-51.png

至此,我们所有的配置工作就搞定了。

Hello Driver

驱动程序代码

当我们配置好驱动开发环境及项目之后,可以创建代码文件,但需要注意的是我们在学习阶段时候建议代码文件使用C语言,这样编译器就不会进行太多的优化,也便于我们调试:

images/download/attachments/1015847/image2022-9-1_15-22-35.png

接着我们将项目代码的警告配置修改一下:

images/download/attachments/1015847/image2022-9-1_15-31-11.png

然后我们开始写自己的第一个驱动程序代码,最基本的格式就是包含ntddk.h头文件,以及写好驱动程序入口函数DriverEntry:

#include <ntddk.h> // 必须要包含的头文件

// 自定义的驱动程序卸载函数

VOID DriverUnload(PDRIVER_OBJECT DriverObject)

{

DbgPrint("Bye. \n");

}

// 驱动程序入口函数

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)

{

DbgPrint("Hello Driver. \n");

// 设置一个卸载函数,当驱动停止时触发

DriverObject->DriverUnload = DriverUnload;

return STATUS_SUCCESS;

}

最后我们编译代码,在项目的Debug目录下找到.sys文件,这就是我们编译出来的驱动程序:

images/download/attachments/1015847/image2022-9-1_16-56-23.png

使用驱动程序

将编译好的驱动程序、KdmManager、DebugView拖到XP虚拟机中(在虚拟机中去调试驱动比实体机中方便),使用KdmManager加载驱动,按下图所示步骤依次进行,于此同时也要打开DebugView,同时开启监听内核的选项:

images/download/attachments/1015847/image2022-9-2_10-51-43.png

images/download/attachments/1015847/image2022-9-2_10-52-52.png

当我们依次进行注册、运行、停止、卸载,可以清晰的在DebugView中观察到我们驱动程序所输出的内容,并且我们也知道了函数执行的流程:当点击Run按钮时进入驱动程序入口函数,当点击Stop按钮时进入自定义的驱动程序卸载函数:

images/download/attachments/1015847/image2022-9-2_10-54-23.png

调试驱动程序

调试环境

调式驱动程序不像应用程序一样简单(直接在OD之类的调试工具中下断点),要想调试驱动程序就需要使用双机调试,在虚拟机中运行驱动程序,在实体机上使用0环调试器(也就是WinDbg)进行调试。

之前我们进行双机调试配置时,需要手动修改文件然后重启,这个步骤相对来说比较繁琐,我们可以通过VirtualKD(开源项目地址:GitHub - 4d61726b/VirtualKD-Redux: VirtualKD-Redux - A revival and modernization of VirtualKD)程序来简化整个步骤。

如下图所示就整个程序的目录结构,target32/64目录下的文件就是根据虚拟机操作系统的类型选择并复制进虚拟机的,vmmon64.exe就是主程序,直接实体机打开:

images/download/attachments/1015847/image2022-9-2_13-51-56.png

我的虚拟机系统是Windows XP 32位,所以我将target32目录放入虚拟机中,并且按如下步骤操作:

双击运行vminstall.exe-点击Install按钮-点击“是”-系统重启-选择VKD-Redux启动

images/download/attachments/1015847/image2022-9-2_13-56-23.png

于此同时打开vmmon64.exe,选择好对应的Windbg程序路径(一般情况下程序会寻找默认Windbg程序路径):

images/download/attachments/1015847/image2022-9-2_13-59-16.png

最后进入调试模式,管道连接上会自动打开Windbg程序:

images/download/attachments/1015847/image2022-9-2_14-0-32.png

PDB文件

无论是OD还是Windbg,都需要符号文件的加持才能在逆向时获得到更多的信息,例如你用OD去调试程序时经常可以看到一些Windows API是以函数名称的形式存在于反汇编里的,这就是符号文件起的作用。

PDB文件也就是符号文件,我们在编译程序时候,只要不取消调试信息的输出,默认情况下是可以在编译输出的目录中找到所编译程序的PDB文件的,例如我们上文中编译的驱动程序的输出目录下就有对应的PDB文件。

images/download/attachments/1015847/image2022-9-6_22-37-49.png

我们要想在Windbg中调试驱动程序就需要这个PDB文件,但是在这之前我们需要先在驱动代码内写上内联汇编,用于断点:

images/download/attachments/1015847/image2022-9-6_22-42-50.png

编译之后将文件放入虚拟机,以及在Windbg重载符号文件,按如下图操作填入PDB文件所在路径即可:

images/download/attachments/1015847/image2022-9-6_22-58-51.png

接着在虚拟机中用KdmManager加载并运行驱动程序,运行时(KdmManager软件中点击Run按钮)就会在Windbg中断下来,如下图在Windbg中新建了一个窗口(左边的窗口)展示当前驱动程序断点的位置,并且都是以源代码形式展示给我们的,这种效果与我们在调试应用程序时用VS是一样的:

images/download/attachments/1015847/image2022-9-6_22-45-35.png

基础内容

内核API的使用

在应用层编程时我们可以通过包含Windows.h这个头文件来使用Windows提供的API,但是在内核编程时我们不可以使用应用层的API,而要使用内核专用的API,所以我们需要包含的头文件就变成了ntddk.h(需要安装好WDK)。

images/download/attachments/1015847/image2022-9-7_15-38-23.png

与应用程序开发一样,我们想要去了解某个内核API的信息,也需要查阅文档,老版本的WDK安装之后会自带文档信息,较新一点的WDK不会自带,我们需要去官网查看:Windows 驱动程序工具包 (WDK) 的 API 参考文档 - Windows drivers | Microsoft Learn

images/download/attachments/1015847/image2022-9-7_15-48-5.png

未导出函数的使用

WDK文档里只包含了导出的函数信息,对于未导出的函数是查阅不到相关资料的(我们也可以将其称之为未文档化函数)。如果我们想使用未导出的函数,需要定义一个函数指针,并且为函数指针提供正确的函数地址就可以使用了。未导出函数的地址可以通过特征码搜索、解析内核的PDB文件来找到。

基本数据类型的使用

在内核编程的时候,必须要遵守WDK的编码习惯,例如无符号类型不要在类型前加上关键词unsigned,而是要遵循WDK自己的类型:

WDK的写法

表达的意思

ULONG

unsigned long

UCHAR

unsigned char

UINT

unsigned int

VOID

void

PULONG

unsigned long *

PUCHAR

unsigned char *

PUNIT

unsigned int *

PVOID

void *

如果你按后者去写,在不同的平台上移植代码可能会导致数据宽度不同,要修改到相同宽度就需要修改代码,而按WDK的写法就可以减少这种不必要的情况。

返回值

大部分内核函数的返回值都是NTSTATUS类型,它本质是一个宏,里面包含的类型有很多,如下三个就是常见的返回值:

宏名称

实际值

含义

STATUS_SUCCESS

0x00000000

成功

STATUS_INVALID_PARAMETER

0xC000000D

参数无效

STATUS_BUFFER_OVERFLOW

0x80000005

缓冲区长度不够

当你调用的内核函数返回结果不是STATUS_SUCCESS,那就说明函数在执行时遇到了问题,具体的问题可以根据返回值在ntstatus.h头文件中去寻找:

images/download/attachments/1015847/image2022-9-7_16-7-36.png

通过宏的名称也能知道个大概,如果仍然不知道问题的含义,可以在微软的WDK文档中去搜索相关宏名称。

内核中的异常处理

在内核中一个小小的错误就可能导致蓝屏,例如我们去读写一个无效的内存地址。为了让自己的内核程序更加健壮,在编写内核程序时要使用到异常处理。

在Windows下提供了结构化异常处理机制,编译器普遍都支持,如下就是代码使用方法:

__try

{

// 填入可能要出错的代码

}

__except (filter_value)

{

// 填入出错后要执行的代码

}

如上示例中的filter_value,就是当内核程序出现异常时决定程序如何执行的,一般有这三种情况:

宏名称

实际值

含义

EXCEPTION_EXECUTE_HANDLER

1

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com