对于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. Fedora 34成哑巴了?

    原由 前几天刚更新了Fedora34,完全沉浸在Gnome40的喜悦中.但是今天用耳机听Apple Music的时候完全傻了,音量控制旋钮调了半天也没有声音,难道声卡坏了?于是,我试探性的用Parro ...

  2. Java IO学习笔记六:NIO到多路复用

    作者:Grey 原文地址:Java IO学习笔记六:NIO到多路复用 虽然NIO性能上比BIO要好,参考:Java IO学习笔记五:BIO到NIO 但是NIO也有问题,NIO服务端的示例代码中往往会包 ...

  3. Git 高级用法,喜欢就拿去用

    如果你觉得 git 很迷惑人,那么这份小抄正是为你准备的! 请注意我有意跳过了 git commit.git pull/push 之类的基本命令,这份小抄的主题是 git 的一些「高级」用法. 导航 ...

  4. 《Docker基础与实战,看这一篇就够了》

    什么是Docker? Docker 使用 Google 公司推出的 Go 语言 进行开发实现,基于 Linux 内核的 cgroup,namespace,以及 AUFS 类的 Union FS 等技术 ...

  5. 【题解】ball 数论

    题目 题目描述: 众所周知的是Dr.Bai 穷困潦倒负债累累,最近还因邦邦的出现被班上的男孩子们几乎打入冷宫,所以Dr.Bai 决定去打工赚钱. Dr.Bai 决定做玩♂球的工作,工作内容如下. 老板 ...

  6. 关于equals()和hashcode()的一些约定

    本文章主要讨论和回答一下几个问题: equals()的四大特性 equals()和hashcode()之间的关系,为什么我们经常说这两个方法要么都重写,要么都不重写? HashMap.HashSet等 ...

  7. spring boot @Async异步注解上下文透传

    上一篇文章说到,之前使用了@Async注解,子线程无法获取到上下文信息,导致流量无法打到灰度,然后改成 线程池的方式,每次调用异步调用的时候都手动透传 上下文(硬编码)解决了问题. 后面查阅了资料,找 ...

  8. Terraform插件Provider管理,搜索、定义、下载

    我最新最全的文章都在南瓜慢说 www.pkslow.com,欢迎大家来喝茶! 简介 最近工作中用到了Terraform,权当学习记录一下,希望能帮助到其它人. Terraform系列文章如下: Ter ...

  9. Unity 不能添加脚本组件,脚本类可能丢失

    报错: 把脚本拖到游戏对象,显示如下 原因: 你可能修改了脚本名称,但此时Unity没有找到那个对应脚本名称的类

  10. 07 修改JumpServer网页信息

    1.7.修改JumpServer网页信息 注意:在修改相关配置文件之前要先进行备份,防止文件修改错误无法恢复. 1.Luna图标: /opt/luna/static/imgs/logo.png 2.j ...