对于NT式驱动来讲,主要的函数是DriverEntry例程、卸载例程及各个IRP的派遣例程。安全
1、驱动加载过程与驱动入口函数(DriverEntry)函数
和编写普通应用程序同样,驱动程序有个入口函数,也就是首先被执行的函数。这个函数一般被命名为DriverEntry。该函数的原型为:spa
NTSTATUS DriverEntry(
_In_ PDRIVER_OBJECT DriverObject,
_In_ PUNICODE_STRING RegistryPath
)
DriverEntry主要是对驱动程序进行初始化工做,它是由系统进程所调用的。在Windows中有个特殊的进程叫作系统进程,打开进程管理器,里面有个名为System的进程就是系统进程。系统进程在系统启动的时候,就已经被建立了。线程
驱动加载的时候,系统进程启动新的线程,调用执行体组件中的对象管理器,建立一个驱动对象。这个驱动对象是一个DRIVER_OBJECT的结构体。另外,系统进程调用执行体组件中的配置管理程序,查询此驱动程序对应的注册表中的项。指针
系统线程调用驱动程序的DriverEntry例程时,同时传进两个参数,分别是DriverObject和RegisreyPath。其中,一个是指向刚才被建立驱动对象的指针,另一个是指向设备服务键的键名字符串的指针。在DriverEntry中,主要功能是对系统进程建立的驱动对象进行初始化。另外,设备服务键的键名有时候须要保存下来,由于这个字符串不是长期存在的(函数返回后可能消失)。若是之后想使用这个UNICODE字符串就必须先把它复制到安全的地方。code
这个字符串的内容通常是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倍。blog
》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参数,即便用者不能改变这个指针自己,但彻底能够改变它指向的对象。
2、建立设备对象
在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页。