对于NT式驱动来说,主要的函数是DriverEntry例程、卸载例程及各个IRP的派遣例程。

一、驱动加载过程与驱动入口函数(DriverEntry)

和编写普通应用程序一样,驱动程序有个入口函数,也就是首先被执行的函数。这个函数通常被命名为DriverEntry。该函数的原型为:

NTSTATUS DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)

DriverEntry主要是对驱动程序进行初始化工作,它是由系统进程所调用的。在Windows中有个特殊的进程叫做系统进程,打开进程管理器,里面有个名为System的进程就是系统进程。系统进程在系统启动的时候,就已经被创建了。

驱动加载的时候,系统进程启动新的线程,调用执行体组件中的对象管理器,创建一个驱动对象。这个驱动对象是一个DRIVER_OBJECT的结构体。另外,系统进程调用执行体组件中的配置管理程序,查询此驱动程序对应的注册表中的项。

系统线程调用驱动程序的DriverEntry例程时,同时传进两个参数,分别是DriverObject和RegisreyPath。其中,一个是指向刚才被创建驱动对象的指针,另外一个是指向设备服务键的键名字符串的指针。在DriverEntry中,主要功能是对系统进程创建的驱动对象进行初始化。另外,设备服务键的键名有时候需要保存下来,因为这个字符串不是长期存在的(函数返回后可能消失)。如果以后想使用这个UNICODE字符串就必须先把它复制到安全的地方。

这个字符串的内容一般是Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\服务名。在驱动程序中,字符串用INICODE字符串来表示。UNICODE是宽字符集,每个字符用16位表示。

typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
#ifdef MIDL_PASS
[size_is(MaximumLength / 2), length_is((Length) / 2) ] USHORT * Buffer;
#else // MIDL_PASS
_Field_size_bytes_part_opt_(MaximumLength, Length) PWCH Buffer;
#endif // MIDL_PASS
}

》Length:记录这个字符串用多少字节记录。如果字符串有N个字符,那么Length将会是N的2倍。

》MaximumLength:记录buffer的大小,也就是这个结构最大能记录的字节数。MaximumLength要大于或等于Length。

》Buffer:记录字符串的指针。与ASCII字符串不同,这里的字符串每个字符否是16位。

在驱动中可以使用KdPrint打印UNICODE的信息。其语法是:

KdPrint(("%S\n", RegistryPath->Buffer));

KdPrint(("%ws", RegistryPath->Buffer));

DriverEntry返回值是NTSTATUS的数据,NTSTATUS是被定义为32位的无符号长整形。在驱动程序开发中,人们习惯用NTSTATUS返回状态。其中0~0X7FFFFFFF,被认为是正确的状态,而0X80000000~0XFFFFFFFF,被认为是错误的状态。有个非常有用的宏——NT_SUCCESS,被用来检测状态是否正确。常用的NTSTATUS值有STATUS_SUCCESS。

DriverEntry的返回值如果表示成功,则意味着加载驱动成功,否则意味着加载驱动失败,调用对象管理程序销毁驱动对象。

最后需要说明的是DriverEntry参数的修饰“IN”。“IN”、“OUT”、“INOUT”在DDK中被定义成空串,它们的功能类似于程序注释,当看到一个“IN”参数时,应该认定该参数是纯粹用于输入目的。“OUT”参数代表这个参数仅用于函数的输出函数。“INOUT”用于既可以输入又可以输出的参数。例如DriverEntry例程,它的DriverObject指针是IN参数,即使用者不能改变这个指针本身,但完全可以改变它指向的对象。

二、创建设备对象

在NT式的驱动中,创建设备对象是由IoCreateDevice内核函数完成的。

NTSTATUS IoCreateDevice{
IN PDRIVER_OBJECT DriverObject,
IN ULONG DeviceExtensionSize,
IN PUNICODE_STRING DeviceName OPTIONAL,
IN DEVICE_TYPE DeviceType,
IN ULONG DeviceCharacteristics,
IN BOOLEAN Exclusive,
OUT PDEVICE_OBJECT *DeviceObject
};

》DriverObject:输入参数,每个驱动程序中。会有唯一的驱动对象与之对应,但每个驱动对象会有若干个设备对象。DriverObject指向的就是驱动对象的指针。

》DeviceExtensionSize:输入参数,指定设备扩展的大小,I/O管理器会根据这个大小,在内存中创建设备扩展,并与驱动对象关联。

》DeviceName:输入参数,设置设备对象的特征。

》Exclusive:输入参数,设置设备对象是否为内核模式下使用,一般设置为TRUE。

》DeviceObject:输出参数,I/O管理器负责创建这个设备对象,并返回设备对象的地址。

》返回值:返回此函数的调用状态。

设备名称用UNICODE字符串指定,并且字符串必须是“\Device\[设备名]”的形式。在Windows下的所有设备都是以类似名字命名的,例如,磁盘分区的C盘、D盘、E盘、F盘就是被命名为“\Device\Harddisk Volume1”、“\Device\HarddiskVolume2”、“\Device\HarddiskVolume3”、“\Device\HarddiskVolume4”.

当然也可以不指定设备名字,如果在IoCreateDevice中没有指定设备对象的名字,I/O管理器会自动分配一个数字作为设备的设备名,例如,“\Device\00000001”、“\Device\00000002”、“\Device\00000003”.

如果指定了设备名,只能被内核模式下的其他驱动所识别。但是在用户模式下的应用程序无法识别这个设备。让用户模式下的应用程序能识别设备有两种办法,第一种是通过符号链接找到设备,第二种是通过设备接口找到设备。设备接口的办法在NT驱动中很少使用。

符号链接可以理解为设备对象起了一个“别名”。设备对象的名称只能被内核模式的驱动识别,而别名也可以被用户模式下的应用程序十倍。例如,常说的C盘、D盘就是符号链接。所谓的C盘,指的是名为“C:”的符号链接,其真正的设备对象是“\Device\HarddiskVolume1”,创建符号链接的函数是IoCreateSymbolicLink,其函数声明如下。

121页。

C++第四十篇 -- 研究一下Windows驱动开发(三)-- NT式驱动的基本结构的更多相关文章

  1. 用Visual Studio 2015 编译张帆的第一个NT式驱动,并且成功安装到Windows XP里面!!!

    开发工具:Visual Studio 2015 企业版 目 标 机:Windows XP X86 前提:我们已经成功安装了Visual Studio 2015以及WDK,而且更重要一点是一定要SDK版 ...

  2. C++第三十八篇 -- 研究一下Windows驱动开发(二)--WDM式驱动的加载

    基于Windows驱动开发技术详解这本书 一.简单的INF文件剖析 INF文件是一个文本文件,由若干个节(Section)组成.每个节的名称用一个方括号指示,紧接着方括号后面的就是节内容.每一行就是一 ...

  3. C++第三十三篇 -- 研究一下Windows驱动开发(一)内部构造介绍

    因为工作原因,需要做一些与网卡有关的测试,其中涉及到了驱动这一块的知识,虽然程序可以运行,但是不搞清楚,心里总是不安,觉得没理解清楚.因此想看一下驱动开发.查了很多资料,看到有人推荐Windows驱动 ...

  4. C++第三十九篇 -- 研究一下Windows驱动开发(二)-- 驱动程序中重要的数据结构

    数据结构是计算机程序的核心,I/O管理器定义了一些数据结构,这些数据结构是编写驱动程序时所必须掌握的.驱动程序经常要创建和维护这些数据结构的实例. 一.驱动对象(DRIVER_OBJECT) 每个驱动 ...

  5. Python之路(第四十篇)进程池

    一.进程池 进程池也是通过事先划分一块系统资源区域,这组资源区域在服务器启动时就已经创建和初始化,用户如果想创建新的进程,可以直接取得资源,从而避免了动态分配资源(这是很耗时的). 线程池内子进程的数 ...

  6. 第四十篇:Vue的生命周期(一)

    好家伙,军训结束了,回归 Vue实例的生命周期 1.什么是生命周期? 从Vue实例创建,运行到销毁期间总是伴随着各种各样的事件,这些事件,统称为生命周期. 2.什么是生命周期钩子? 生命周期函数的别称 ...

  7. Android UI开发第四十篇——ScrollTricks介绍

    ScrollTricks是一个开源控件,实现了两个简单功能: 1.Quick Return:向上滑动时,View也向上滑动并且消失,当向下滑动时,View马上出现.例如Google Now的搜索功能. ...

  8. 第四十篇-private,public,protected的区别

    1.public: public表明该数据成员.成员函数是对所有用户开放的,所有用户都可以直接进行调用 2.private: private表示私有,私有的意思就是除了class自己之外,任何人都不可 ...

  9. 第四十篇 Python之设计模式总结-简单工厂、工厂方法、抽象工厂、单例模式

    一. 简单工厂 简单工厂模式(Simple Factory Pattern):是通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类. 简单工厂的用处不大,主要就是一个if... ...

随机推荐

  1. 你有一份经典SQL语句大全,请注意查收

    一.基础部分 1.创建数据库 CREATE DATABASE dbname 2.删除数据库 DROP DATABASE dbname 3.创建新表 CREATE TABLE tabname(col1 ...

  2. 大家看看大佬对Maven仓库的讲解,有何高明之处?

    Maven在某个统一的位置存储所有项目的共享的构件,这个统一的位置,我们就称之为仓库.(仓库就是存放依赖和插件的地方). 分类 maven的仓库只有两大类:1.本地仓库 2.远程仓库,在远程仓库中又分 ...

  3. SpringCloud+Docker+Jenkins+GitLab+Maven实现自动化构建与部署实战

    1.前言与初衷 本文章会涉及Docker常见命令基础知识点结合不同场景实操一起使用. 本文章会涉及结合工作过程中部署不同环境服务器的项目案例场景为初心进行实际细讲. 本文章主要讲述Docker.Jen ...

  4. jenkins pipeline构建后发送邮件通知

    jenkins pipeline构建后发送邮件通知 mail配置 进入系统配置 找到最下边的邮件通知 创建任务 Pipeline片段 post { always { bat "" ...

  5. 第5章:资源编排(YAML)

    5.1 编写YAML注意事项 YAML 是一种简洁的非标记语言. 语法格式: 缩进表示层级关系 不支持制表符"tab"缩进,使用空格缩进 通常开头缩进 2 个空格 字符后缩进 1 ...

  6. ECS实例中的应用偶尔出现丢包现象并且内核日志(dmesg)存在“kernel: nf_conntrack: table full, dropping packet”的报错信息

    问题描述 连接ECS实例中的应用时偶尔出现丢包现象.经排查,ECS实例的外围网络正常,但内核日志(dmesg)中存在"kernel: nf_conntrack: table full, dr ...

  7. Docker搭建Gogs代码仓库——代码自动化运维部署平台(一)

    一.准备工作 1.Gogs 概念: Gogs 是一款极易搭建的自助 Git 服务. 目的: Gogs 的目标是打造一个最简单.最快速和最轻松的方式搭建自助 Git 服务.使用 Go 语言开发使得 Go ...

  8. CentOS-常用命令(版本:7.x)

    常用命令 注:centos命令不定期持续更新,希望能够帮到你~ 修改主机名 $ hostnamectl set-hostname xxx 查看IP $ vim /etc/sysconfig/netwo ...

  9. Springboot:@RequestMapping注解及属性详解

    @RequestMapping 注解: @RequestMapping 是 Spring Web 应用程序中最常被用到的注解之一.这个注解会将 HTTP 请求映射到 MVC 和 REST 控制器的处理 ...

  10. 学习/etc/group /etc/passwd 和 /etc/shadow

    /etc/passwd 管理用户信息的系统文件 /etc/shadow 管理用户密码信息的系统文件 /etc/group 管理用户组信息的系统文件 1./etc/group 将用户分组是Linux系统 ...