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

摘要:
对于NT风格的驱动程序,主要功能是DriverEntry例程、卸载例程和IRP调度例程。在DriverEntry中,主要功能是初始化系统进程创建的驱动程序对象。在驱动程序开发中,人们习惯于使用NTSTATUS返回状态。如果DriverEntry的返回值指示成功,则表示驱动程序已成功加载。否则,这意味着驱动程序加载失败。调用对象管理程序以销毁驱动程序对象。将有一个唯一的驱动对象与之相对应,但每个驱动对象都有多个设备对象。DriverObject是指驱动程序对象的指针。

对于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字符串就必须先把它复制到安全的地方。

这个字符串的内容一般是ComputerHKEY_LOCAL_MACHINESYSTEMCurrentControlSetServices服务名。在驱动程序中,字符串用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
", 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盘就是被命名为“DeviceHarddisk Volume1”、“DeviceHarddiskVolume2”、“DeviceHarddiskVolume3”、“DeviceHarddiskVolume4”.

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

免责声明:内容来源于网络,仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Java之协程(quasar)个人技术总结——postman的接口请求下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Microsoft SQL Server 2000 的国际化功能(1)

简介 Microsoft? SQL Server? 2000 包括各种支持国际化操作和环境的强大功能。扩展的多种语言功能使 SQL Server 2000 成为一种引人注目的数据库产品和应用程序平台。本文将完整地概述在全球范围内使用这些功能的方法。 除了列出一系列功能外,本文还将解释国际化/多种语言要求会怎样影响项目的各个方面。 什么是...

re库的使用

一、re库的调用 import re 二、正则表达式类型 raw string类型(原生字符串类型,即不包括转义符类型):r'text' string类型,更繁琐。 三、Re库主要功能函数 函数              说明 re.search()        在一个字符串中搜索匹配正则表达式的第一个位置,然后返回match对象 re.match(...

FreeMarker操作符

操作字符串函数 1. substring(start,end)从一个字符串中截取子串 start:截取子串开始的索引,start必须大于等于0,小于等于end end: 截取子串的长度,end必须大于等于0,小于等于字符串长度,如果省略该参数,默认为字符串长度。 例子: ${‘str’?substring(0)} 结果为str ${‘str’?substr...

scanf函数的使用

函数原型编辑 1 intscanf( constchar*format, ... ); scanf()函数是格式化输入函数,它从标准输入设备(键盘) 读取输入的信息。 其调用格式为: scanf("<格式化字符串>",<地址表>); 函数 scanf() 是从标准输入流 stdio 中读内容的通用子程序,可以读入全部固...

[转]SQLserver字符串分割函数

一、按指定符号分割字符串,返回分割后的元素个数,方法很简单,就是看字符串中存在多少个分隔符号,然后再加一,就是要求的结果。 CREATE functionGet_StrArrayLength ( @str varchar(1024), --要分割的字符串 @split varchar(10) --分隔符号 ) returns int as b...

JS 数据转换

转换成字符串类型 toString() var num = 5;console.log(num.toString()); String() String()函数存在的意义:有些值没有toString(),这个时候可以使用String()。比如:undefined和null 拼接字符串方式 num + "",当 + 两边一个操作符是字符串类型,一...