zoukankan      html  css  js  c++  java
  • C++第四十篇 -- 研究一下Windows驱动开发(三)-- NT式驱动的基本结构

    对于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管理器会自动分配一个数字作为设备的设备名,例如,“Device0000001”、“Device0000002”、“Device0000003”.

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

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

    121页。

  • 相关阅读:
    ScrollView反弹效果的实现
    Unity 3D本地公布WebPlayer版时"Failed to download data file"解决方式
    win7休眠的开启与关闭方法命令行操作和图文结合的鼠标操作
    使用Javascript D3创建属于你的涂鸦作品
    android获取自己定义控件位置坐标,屏幕尺寸,标题栏,状态栏高度
    [Python]Use Flask-Admin with PostgreSQL
    [LeetCode] Best Time to Buy and Sell Stock
    spring实战五之Bean的自动检测
    FireBug使用总结
    javascript的window.onload()方法和jQuery的$(document).ready()的对比
  • 原文地址:https://www.cnblogs.com/smart-zihan/p/11790474.html
Copyright © 2011-2022 走看看