Windows驱动开发(一)第一个驱动程序

2023-11-20

首先我们需要了解,在操作系统中,是分两种权限的,一种是内核态,我们也称为0环,一种是用户态,称之为3环。而在我们的电脑中,驱动程序是运行在内核态的,这意味着和操作系统内核是在同一权限的,而普通的应用程序的权限是最低的。高权限谁不想拥有呢,因此驱动程序是很有必要了解与学习的。比如我们熟知的防病毒软件,游戏保护等,现在都在利用内核驱动技术来保护自己的数据。

windows提供了好几种驱动框架模型,这里我们采用WDM模型,这是自winodws 2000来提供给开发者的一种框架,我们用来开发NT驱动。下面我们介绍驱动程序该如何编写,其实驱动程序和普通的应用程序C语言没什么特别大的区别,在C语言中我们需要一个main函数作为入口,那么在驱动程序中DriverEntry就是主函数入口,下面我们来实现一个hello,world版本的驱动程序,代码如下:

#include<Ntddk.h>
void Unload(IN PDRIVER_OBJECT DriverObject)
{
	UNREFERENCED_PARAMETER(DriverObject);
	DbgPrint("Unload\n");

	}

}

NTSTATUS DriverEntry(
	IN PDRIVER_OBJECT DriverObject,
	IN PUNICODE_STRING RegistryPathName)
{
	UNREFERENCED_PARAMETER(RegistryPathName);
	DbgPrint("Hello Driver!!\n");
    DriverObject->DriverUnload = Unload;
    return  STATUS_SUCCESS;

}

我第一次接触这段代码的时候非常陌生和惶恐,竟然没有一句看懂的,但是只要我们耐下性子来研究这个东西,没什么难的。我们再写一个C语言版本的helo,world来对比研究:

#include<stdio.h>

int main(){

printf("hello,world!");
return 0;
}

 在编写一个程序时我们需要一个主函数,而且需要一个返回值来表示程序的结束。在驱动中返回值一般都是NTSTATUS,这是一种状态码,成功的时候需要返回STATUS_SUCCESS。相当于C语言中的return 0。接着看驱动程序主函数中有两个参数,第一个是一个驱动对象。在内核中,系统管理的都是对象。一些重要的数据结构都是以对象为主体来操控的。我们写的是驱动程序,自然需要一个驱动对象。PDRIVER_OBJECT是一个指针,指向一个驱动对象。这个驱动对象就是对应着我们写的这个驱动程序。是系统帮我们规划好的模板,按照这个模板写程序就行了。因此我们可以看看这个驱动对象的样子:

typedef struct _DRIVER_OBJECT {
    CSHORT Type;
    CSHORT Size;

    //
    // The following links all of the devices created by a single driver
    // together on a list, and the Flags word provides an extensible flag
    // location for driver objects.
    //

    PDEVICE_OBJECT DeviceObject;
    ULONG Flags;

    //
    // The following section describes where the driver is loaded.  The count
    // field is used to count the number of times the driver has had its
    // registered reinitialization routine invoked.
    //

    PVOID DriverStart;
    ULONG DriverSize;
    PVOID DriverSection;
    PDRIVER_EXTENSION DriverExtension;

    //
    // The driver name field is used by the error log thread
    // determine the name of the driver that an I/O request is/was bound.
    //

    UNICODE_STRING DriverName;

    //
    // The following section is for registry support.  This is a pointer
    // to the path to the hardware information in the registry
    //

    PUNICODE_STRING HardwareDatabase;

    //
    // The following section contains the optional pointer to an array of
    // alternate entry points to a driver for "fast I/O" support.  Fast I/O
    // is performed by invoking the driver routine directly with separate
    // parameters, rather than using the standard IRP call mechanism.  Note
    // that these functions may only be used for synchronous I/O, and when
    // the file is cached.
    //

    PFAST_IO_DISPATCH FastIoDispatch;

    //
    // The following section describes the entry points to this particular
    // driver.  Note that the major function dispatch table must be the last
    // field in the object so that it remains extensible.
    //

    PDRIVER_INITIALIZE DriverInit;
    PDRIVER_STARTIO DriverStartIo;
    PDRIVER_UNLOAD DriverUnload;
    PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];

} DRIVER_OBJECT;
typedef struct _DRIVER_OBJECT *PDRIVER_OBJECT; 

        这是WDK里面跟进去看到的驱动对象。学过C语言的一定看得懂,其实就是个结构体。虽然在winodws内核中是用纯C写的,但是却用到了面向对象的思想。只用C语言的结构体,一样可以实现面向对象编程。这个对象是什么时候生成的呢?这也是我在学习驱动时候的好奇。后来我想明白了,在我们想要加载我们的驱动程序的时候,操作系统其实为我们做好了初始化,为我们创建一个驱动对象。如果创建成功将把这个驱动对象的地址返回给我们,因此我们就能拿着这个地址对这个驱动对象做很多事了。接下来介绍第二个参数RegistryPathName。这个参数指的是在注册表中的位置。返回给我们的也是一个指针,告诉我们它在注册表何处。接着我们用内核提供的"printf"打印一句话--Hello Driver!!。到这里和C语言相同的地方我们就讲的差不多了。下面还有一些区别于C语言的东西。

        现在我们知道驱动程序加载是在内核空间的。内核空间不像用户空间那么随意,有些东西想关就关,想开就开。稍不留神可能就造成电脑蓝屏。因此我们可能还需要提供一个卸载的功能,否则这个驱动将无法卸载。但是其实这个也不是必要的。很多商用的软件并不想别人把它关掉,自然不会提供这么一个卸载功能。这就像为啥有些杀软删不掉的原因(懂得都懂)。但是我们在做测试过程中,还是要提供这么一个卸载函数。这个函数是我们自己写的。驱动对象中会记录这个回调函数。可以参考下上面驱动对象中有一个成员叫DriverUnload,这就是指向我们提供的卸载函数的指针。因此通过驱动对象指针能访问到它。将它设置为我们提供的卸载函数。在我们提供的卸载函数中,我们目前也只提供打印一句话的功能。卸载驱动的过程是操作系统为我们隐式做的。当系统检测到驱动对象中有这么个卸载函数,将去执行我们提供的回调函数。并把我们的驱动从内核中卸载。大致了解这个过程就OK了。因此,到目前为止,我们的第一个驱动程序就完成了。我们在XP下试验一下:

我们在测试工具中看到了Hello Driver,后面两句不用管那是后续实现的功能。我们再看卸载后的状态:

我们看到停止驱动后,系统会调用我们提供的卸载回调函数,并打印了一句Unload。证明我们的驱动是没问题的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Windows驱动开发(一)第一个驱动程序 的相关文章

随机推荐

  • clion file and code templates

    主要用于 文件命名为 my class 时 生成的类为 MyClass c class header 和 都是被分割的对象 parse C File Header h ifndef INCLUDE GUARD define INCLUDE
  • L1-039 古风排版(C语言)(测试点2)

    题目 L1 039 古风排版 分数 20 作者 陈越 单位 浙江大学 中国的古人写文字 是从右向左竖向排版的 本题就请你编写程序 把一段文字按古风排版 输入格式 输入在第一行给出一个正整数N lt 100 是每一列的字符数 第二行给出一个长
  • Flickr30k图像标注数据集下载及使用方法

    Flickr30k数据集的下载 可在http shannon cs illinois edu DenotationGraph 中填表 获取下载地址http shannon cs illinois edu DenotationGraph da
  • 【深入理解C++】转发、完美转发、std::forward

    文章目录 1 转发 2 完美转发 3 std forward和std move的区别 4 参考资料 前置知识 万能引用 引用折叠 1 转发 include
  • SIM卡相关知识总结

    主题 SIM基本功能和原理 简介 参考 SIM卡工作原理总结 SIM卡插入检测原理 SIM卡的识卡机制 SIM卡基础技术规范 2 3 4 5G UE和网络鉴权流程的演进 作者 ybb 时间 2021年9月9日 1 初识SIM卡 SIM卡的物
  • JDBC 的简单封装,以sqlite数据库为例

    小驼峰会自动转下划线 Column 注解会覆盖其他策略 就取name名字作为字段名 Table 注解可以重新标记表名 1 主要方法就是save和list两个方法 调用实例如下 package xin tom esui db import o
  • 判断用户设备是移动端还是pc端

    判断用户设备是移动端还是pc端 解决办法 mounted if this isMobile 手机端 else pc端 this getRecordH methods 手机端和pc端 isMobile let flag navigator u
  • webpack5 学习(十二)—— webpack 和 TypeScript 进行集成

    基础配置 首先 安装 TypeScript compiler 和 loader npm install save dev typescript ts loader 安装完成 tsconfig json 设置一个基本的配置来支持 JSX 并将
  • C/C++创建和删除文件夹操作

    一 创建文件夹 1 调用Windows API函数 CreateDirectory 头文件
  • [工具

    Graphviz Graphviz 是一个开源的图形可视化工具 它使用 DOT 语言描述图形结构和属性 并将其转换为图形形式 你可以在 Markdown 中使用 Graphviz 的语法来创建有向图 无向图 流程图等 dot语言官方地址 示
  • 获取360画报图片

    在使用360画报时 有时看到喜欢的图片想保存下来 却不知道怎么保存 今天跟大家分享一下方法 1 360画报文件都在C Users Administrator AppData Roaming 360browser bkinfo这里面 但是不是
  • 前端实现导出excel表格(合并表头)

    需求 勾选行导出为excel表格 合并表头 一 安装插件 npm install save file saver xlsx 运行项目报如下警告的话 运行npm install xlsx 0 16 0 save 来降低版本号 最初我安装的版本
  • hive的3种数据存储格式

    hive有textFile SequenceFile RCFile三种文件格式 其中textfile为默认格式 建表时不指定默认为这个格式 导入数据时会直接把数据文件拷贝到hdfs上不进行处理 SequenceFile RCFile格式的表
  • 数据库列名无效原因

    1 原因可能是列名没有加中括号 2 可能是SQL语句中的列名与建表时的列名不一致 3 看看是不是
  • 出现使用选项-std=c99或-std=gnu99来编译您的代码错误或者如何让Linux内核编译驱动模块时支持C99

    最近在Linux系统下编译嵌入式代码时出现问题 问题如下图 然后发现问题出在这 use option std c99 or std gnu99 to compile your code 上面的意思是 使用选项 std c99或 std gn
  • Notepad++ 列操作

    在网上找到一篇关于socket编程的文章 想把其中的代码直接拷贝下来运行测试 但是人家网站做的不够人性化 每行的开头都有行号 直接拷贝就要一行行的删除 甚是麻烦 想到linux下的vi编辑器可以完成列操作 windows下应该也有相关软件可
  • kali之vulhub,medium_socnet

    目录 一 部署环境 二 进行主机发现 arp scan进行扫描 三 使用nmap扫描发现的靶机IP 1 全端口扫描 看服务 2 查看端口所用服务版本 3 登录ip地址看一下 四 扫目录 dirsearch脚本 和windous御剑类似 1
  • Linux进程间通信--使用命名管道

    Linux进程间通信 使用命名管道 一 什么是命名管道 命名管道也被称为FIFO文件 它是一种特殊类型的文件 它在文件系统中以文件名的形式存在 但是它的行为却和之前所讲的没有名字的管道 匿名管道 类似 由于Linux中所有的事物都可被视为文
  • OpenCV-Python中的标量Scalar是什么

    前往老猿Python博客 https blog csdn net LaoYuanPython 一 标量的常规含义 在百度百科中标量是这样定义的 标量 scalar 亦称 无向量 有些物理量 只具有数值大小 而没有方向 部分有正负之分 物理学
  • Windows驱动开发(一)第一个驱动程序

    首先我们需要了解 在操作系统中 是分两种权限的 一种是内核态 我们也称为0环 一种是用户态 称之为3环 而在我们的电脑中 驱动程序是运行在内核态的 这意味着和操作系统内核是在同一权限的 而普通的应用程序的权限是最低的 高权限谁不想拥有呢 因