zoukankan      html  css  js  c++  java
  • 驱动学习4

    1.驱动对象.

    typedef struct _DRIVER_OBJECT {
        CSHORT Type;
        CSHORT Size;
    
        //
        // The following links all of the devices created by a single driver
        // together on a list, and the Flags word provides an extensible flag
        // location for driver objects.
        //
    
        PDEVICE_OBJECT DeviceObject;
        ULONG Flags;
    
        //
        // The following section describes where the driver is loaded.  The count
        // field is used to count the number of times the driver has had its
        // registered reinitialization routine invoked.
        //
    
        PVOID DriverStart;
        ULONG DriverSize;
        PVOID DriverSection;
        PDRIVER_EXTENSION DriverExtension;
    
        //
        // The driver name field is used by the error log thread
        // determine the name of the driver that an I/O request is/was bound.
        //
    
        UNICODE_STRING DriverName;
    
        //
        // The following section is for registry support.  Thise is a pointer
        // to the path to the hardware information in the registry
        //
    
        PUNICODE_STRING HardwareDatabase;
    
        //
        // The following section contains the optional pointer to an array of
        // alternate entry points to a driver for "fast I/O" support.  Fast I/O
        // is performed by invoking the driver routine directly with separate
        // parameters, rather than using the standard IRP call mechanism.  Note
        // that these functions may only be used for synchronous I/O, and when
        // the file is cached.
        //
    
        PFAST_IO_DISPATCH FastIoDispatch;
    
        //
        // The following section describes the entry points to this particular
        // driver.  Note that the major function dispatch table must be the last
        // field in the object so that it remains extensible.
        //
    
        PDRIVER_INITIALIZE DriverInit;
        PDRIVER_STARTIO DriverStartIo;
        PDRIVER_UNLOAD DriverUnload;
        PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];
    
    } DRIVER_OBJECT;

    驱动对象用DRIVER_OBJECT数据结构表示,它做为驱动的一个实例被内核加载,并且内核对一个驱动只加载一个实例,也就是一个驱动最多只有一个驱动对象

    更确切的说,是由内核中的I/O管理器负责加载的,由DriverEntry进行初始化

    PDEVICE_OBJECT DeviceObject;

    每个驱动程序都会有一个或多个设备对象,其中,每个设备对象都有一个指针指向下一个设备对象,最后一个设备对象指向空

     UNICODE_STRING DriverName;

    记录了驱动程序的名字,一般为Driver[驱动程序名称],

     PDRIVER_STARTIO DriverStartIo;

    记录StartIO的函数地址,用于串行化操作

     PDRIVER_UNLOAD DriverUnload;

    指定驱动卸载时所用的回调函数地址

        PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];

    MajorFunction记录的是一个函数指针数组,函数是处理IRP的派遣函数

    PFAST_IO_DISPATCH FastIoDispatch;

    文件驱动用到的派遣函数.

    设备对象

    typedef struct DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) _DEVICE_OBJECT {
        CSHORT Type;
        USHORT Size;
        LONG ReferenceCount;
        struct _DRIVER_OBJECT *DriverObject;
        struct _DEVICE_OBJECT *NextDevice;
        struct _DEVICE_OBJECT *AttachedDevice;
        struct _IRP *CurrentIrp;
        PIO_TIMER Timer;
        ULONG Flags;                                // See above:  DO_...
        ULONG Characteristics;                      // See ntioapi:  FILE_...
        __volatile PVPB Vpb;
        PVOID DeviceExtension;
        DEVICE_TYPE DeviceType;
        CCHAR StackSize;
        union {
            LIST_ENTRY ListEntry;
            WAIT_CONTEXT_BLOCK Wcb;
        } Queue;
        ULONG AlignmentRequirement;
        KDEVICE_QUEUE DeviceQueue;
        KDPC Dpc;
    
        //
        //  The following field is for exclusive use by the filesystem to keep
        //  track of the number of Fsp threads currently using the device
        //
    
        ULONG ActiveThreadCount;
        PSECURITY_DESCRIPTOR SecurityDescriptor;
        KEVENT DeviceLock;
    
        USHORT SectorSize;
        USHORT Spare1;
    
        struct _DEVOBJ_EXTENSION  *DeviceObjectExtension;
        PVOID  Reserved;
    
    } DEVICE_OBJECT;
    struct _DRIVER_OBJECT *DriverObject;

    这个是驱动对象的指针,DriverEntry和AddDevice参数中都是同一个驱动对象指针

    PDEVICE_OBJECT NextDevice 
    

    指向由同一个驱动创建的下个设备对象,驱动要unload时,必须先通过它来遍历设备列表,并删除它们


    4.2.1

    驱动程序有个入口函数,这个函数通常被命名为DriverEntry,它由系统进程(system)调用,负责对驱动程序的初始化工作,

    extern "C" NTSTATUS DriverEntry (
    			IN PDRIVER_OBJECT pDriverObject,
    			IN PUNICODE_STRING pRegistryPath	) 
    驱动被加载时,系统进程启动新的线程,调用对象管理器,创建一个驱动对象(DRIVER_OBJECT),另外,系统执行配置管理程序,查询此驱动程序对应的注册表中的项

    extern "C" NTSTATUS DriverEntry (
    			IN PDRIVER_OBJECT pDriverObject,
    			IN PUNICODE_STRING pRegistryPath	) 
    {
    	NTSTATUS status = STATUS_SUCCESS;
    	KdPrint(("Enter DriverEntry
    "));
    	KdPrint(("%S
    ", pRegistryPath->Buffer));
    
    	//注册其他驱动调用函数入口
    	pDriverObject->DriverUnload = HelloDDKUnload;
    	KdPrint(("DriverEntry end
    "));
    	return status;
    }

    VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject) 
    {
    	KdPrint(("Enter DriverUnload
    "));
    }

    以下为LOG:

    00000000	0.00000000	Enter DriverEntry	
    00000001	0.00001047	REGISTRYMACHINESYSTEMControlSet001ServicesHelloDDK	
    00000002	0.00001570	DriverEntry end	
    00000003	27.51406479	Enter DriverUnload	
    更仔细的跟进,发现system进程在创建上述驱动对象的同事,在
    HKEY_LOCAL_MACHINESYSTEMControlSet001ServicesHelloDDK(也就是pRegistryPath指向)创建了注册表子项HellDDK(原来在注册表中是没有HelloDDK的),

    导出这一项为txt:

    值  3
      名称:            ImagePath
      类型:            REG_EXPAND_SZ
      数据:            ??C:Documents and SettingsAdministrator桌面HelloDDK.sys
    
    值  4
      名称:            DisplayName
      类型:            REG_SZ
      数据:            HelloDDK
    可以看到,注册表信息中包含了sys的全路径,显示的名字等信息

    一般来说,DriverEntry函数中,用于设置卸载函数和IRP的派遣函数,另外还有一部分代码负责创建设备对象


    4.22

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

    其中它的第三个参数DeviceName用于设定设备对象的名字,设备名称用UNICODE字符串指定,并且字符串必须是“Device[设备名]”这种形式,如果不以此命名,如:

    \Device1\MyDDKDevice,则会返回错误:

    // The path %hs does not exist.
    //
    #define STATUS_OBJECT_PATH_NOT_FOUND     ((NTSTATUS)0xC000003AL)

    在Windows下所有设备都是以类似名字命名的,如,C盘就是被命名为DeviceHarddiskVolume1当然也可以不指定设备名字,这时I/O管理器会自动分配一个数字作为设备的设备名:如“/Device/00000001”。

    但是如果指定了设备名,也只能被内核模式下的其他驱动所识别,在用户模式下的应用程序仍无法识别这个设备,

    怎么让用户模式下的应用程序识别设备呢:

    1.通过符号链接找到设备(通用),相当于给设备对象起个别名,别名就可以被用户模式下的应用程序识别了,如C盘,D盘都是符号链接

    所谓C盘,指的是名为"C:"的符号链接,其真正的设备对象名称是DeviceHarddiskVolume1

    创建符号链接的函数是

    IoCreateSymbolicLink(别名,设备对象名)

    在内核下,符号链接是以??开头(或DosDevices),如C盘是??C:,而在用户模式下,则是以\.开头的,如C盘就是\.C:
    驱动肯定是内核,所以符号链接必须以??开头,不然这个函数会调用失败,drivermonitor会返回123错误:(语法不正确)

    2.通过设备接口找到设备(少用)


    在使用IoCreateDevice创建设备对象时,一般指定设备类型为FILE_DEVICE_UNKNOWN,说明这个设备是常用设备以外的设备,一般虚拟设备常用这个类型

    (MSDN:If a type of hardware does not match any of the defined types, specify a value of either FILE_DEVICE_UNKNOWN, or a value within the range of 32768 through 65535.)


    4.2.3

    DriverUnLoad函数一般会在驱动卸载时被调用,一般负责删除在DriverEntry中创建的设备对象,并把设备对象所关联的符号链接删除

    pDriverObject->DriverUnload = HelloDDKUnload;
    typedef
    VOID
    DRIVER_UNLOAD (
        __in struct _DRIVER_OBJECT *DriverObject
        );
    
    typedef DRIVER_UNLOAD *PDRIVER_UNLOAD;



    4.2.4

    可以通过winobj来查看驱动对象和设备对象.


    4.2.5

    同样可以通过DeviceTree来查看.


    4.3 WDM式驱动的基本结构

    4.3.1

    在WDM模型中,完成一个设备的操作,至少需要有两个设备对象共同完成,一个是物理设备对象,简称PDO,另一个是功能设备对象FDO

    当PC插入某个设备时,PDO就会自动创建,系统会提示检测到新的设备,如下图:


    意思要求安装WDM驱动程序,要安装的WDM驱动程序负责创建FDO,并附加在PDO上,当一个FDO附加到PDO上时,PDO设备对象的子域AttachedDevice会记录FDO的位置,PDO被称为底层驱动,而FDO被称为高层驱动,如下图:


    WDM提示用户加载FDO,如果此设备已由微软提供,则会自动进行安装,


    4.3.2

    WDM的入口同样是DriverEntry,而是被放在AddDevice函数中,同时,需要设置对IRP_MJ_PNP处理的派遣函数


    不同点:

    1.增加了对AddDevice函数的设置,NT驱动是主动加载设备的,即驱动一旦加载就创建设备,而WDM驱动是被动加载设备的,操作系统必须加载PDO以后,调用驱动的AddDevice函数,AddDevice函数负责创建FDO,并附加到PDO之上

    2.设备对象在AddDevice函数中创建

    3.必须加入IRP_MJ_PNP的派遣回调函数,主要是负责计算机中的即插即用的处理


    4.3.3

    和DriverEntry不同,AddDevice函数名字可以任意命名,分以下步骤:

    1.通过IoCreateDevice函数,创建FDO设备对象,保存FDO的地址。

    2.FDO地址保存在设备扩展中

    3.驱动程序把创建的FDO通过IoAttachDeviceToDeviceStack附加到PDO上

    PDEVICE_OBJECT 
      IoAttachDeviceToDeviceStack(
        IN PDEVICE_OBJECT  SourceDevice,
        IN PDEVICE_OBJECT  TargetDevice
        );
    
    SourceDevice:要附加在别的设备之上的设备,这里填的是FDO地址

    TargetDevice:被附加的设备,这里指的是PDO地址

    返回值:返回附加设备的下层设备,如果中间没有过滤驱动,返回值就是PDO,如果有过滤驱动,返回的是过滤驱动

    4.在附加操作完成后,需要设定符号链接,以便用户应用程序可以访问该设备

    5.设置fdo的Flags子域,定义为“缓冲内存设备”,把DO_DEVICE_INITIALIZING位清零,表示设备初始化完成,这步是必需的

    fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;
    	fdo->Flags &= ~DO_DEVICE_INITIALIZING;
    IRP一般由两个号码指定该IRP的具体意义,一个主IRP,一个辅IRP

    当设备需要被卸载时,如先后发出多个IRP_MJ_PNP,这个IRP的辅IRP号会有所不同,其中对设备的卸载是IRP_MN_REMOVE_DEVICE


    4.4.1

    驱动设备的创建顺序是,先创建底层PDO,再创建高层的FDO,PDO和FDO之间可能夹杂着各种过滤驱动,每次的设备对象由不同驱动程序所创建,有的驱动程序是系统自带的,有的需要程序员来编写,底层设备对象寻找上层的设备对象,是依靠底层设备对象的AttachedDevice寻找的,如果某一设备的AttachedDevice为空,说明已经到了设备堆栈的顶部,而高层设备找低一层的设备对象,只能能过设备扩展来记录,如下图:



    4.4.2












     


     

  • 相关阅读:
    swiper插件的使用demo
    可能要用的东西
    VIDEO
    vue上传图片加水印
    图片 base64 file blob 之间相互的转化
    vant 上传图片加水印
    JS 随机排序算法
    ubuntu16.04 下apache 搭建站点
    Unity常用目录对应的Android && iOS平台地址
    IOS 官方实现单例模式
  • 原文地址:https://www.cnblogs.com/hgy413/p/3693361.html
Copyright © 2011-2022 走看看