4 驱动开发

news/2025/1/11 7:32:55/

环境配置

开发环境

在开发驱动程序之前,我们需要配置好开发环境, 首先安装好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


http://www.ppmy.cn/news/1561924.html

相关文章

2025届视觉算法开发工程师面试问题汇总

2025届视觉算法开发工程师面试问题汇总 1. 数据结构1.1 时间复杂度 2. 算法问题2.1 两数之和2.2 递归求二叉树的深度2.3 一个由0和1组成数组中&#xff0c;计算出这里面连续1的最大数 3. C、Python、Cuda问题3.1 C3.1.1 智能指针3.1.1.1 std::unique_ptr独占指针3.1.1.2 std::s…

内蒙古水系详细很全shp格式arcgis软件无偏移坐标下载后内容测评

标题中的“内蒙古水系详细很全shp格式arcgis软件无偏移坐标”指的是一个地理信息系统&#xff08;GIS&#xff09;数据集&#xff0c;该数据集详细记录了内蒙古地区的水系信息&#xff0c;并以ESRI公司的标准矢量数据格式——Shapefile&#xff08;.shp&#xff09;进行存储。S…

C++虚函数(八股总结)

什么是虚函数 虚函数是在父类中定义的一种特殊类型的函数&#xff0c;允许子类重写该函数以适应其自身需求。虚函数的调用取决于对象的实际类型&#xff0c;而不是指针或引用类型。通过将函数声明为虚函数&#xff0c;可以使继承层次结构中的每个子类都能够使用其自己的实现&a…

第14章 MySQL事务日志

第14章 MySQL事务日志 事务有4种特性&#xff1a;原子性、一致性、隔离性和持久性。那么事务的四种特性到底是基于什么机制实现呢&#xff1f; 事务的隔离性由锁机制实现。而事务的原子性、一致性和持久性由事务的 redo 日志和undo 日志来保证。 REDO LOG 称为重做日志&#…

ElasticSearch | Elasticsearch与Kibana页面查询语句实践

关注&#xff1a;CodingTechWork 引言 在当今大数据应用中&#xff0c;Elasticsearch&#xff08;简称 ES&#xff09;以其高效的全文检索、分布式处理能力和灵活的查询语法&#xff0c;广泛应用于各类日志分析、用户行为分析以及实时数据查询等场景。通过 ES&#xff0c;用户…

C# 对象和类型(结构)

❝ 类和结构的区别 字段、属性和方法 按值和引用传送参数 方法重载 构造函数和静态构造函数 只读字段 Object类&#xff0c;其他类型都从该类派生而来 结构 如何将类保持在堆中&#xff0c;通过这种方式可以在数据的生存期上获得很大的灵活性&#xff0c;但性能会有一定的损失。…

maven的中国镜像有哪些

根据您的请求&#xff0c;以下是一些可用的 Maven 中国镜像&#xff1a; 阿里云 官网&#xff1a;阿里云 Maven 镜像配置&#xff1a;<mirror><id>aliyunmaven</id><mirrorOf>*</mirrorOf><name>阿里云公共仓库</name><url>…

大麦抢票科技狠活

仅供学习参考&#xff0c;切勿再令您所爱的人耗费高昂的价格去购置黄牛票 ⚠️核心内容参考: 据悉&#xff0c;于购票环节&#xff0c;大麦凭借恶意流量清洗技术&#xff0c;于网络层实时甄别并阻拦凭借自动化手段发起下单请求的流量&#xff0c;强化对刷票脚本、刷票软件以及…