设为首页 - 加入收藏 ASP站长网(Aspzz.Cn)- 科技、建站、经验、云计算、5G、大数据,站长网!
热搜: 创业者 手机 数据
当前位置: 首页 > 服务器 > 系统 > 正文

04内核-驱动对象(2)

发布时间:2021-03-04 21:45 所属栏目:52 来源:网络整理
导读:NT 设备名 - 设备名一般格式为 "\Device\自定义设备名" ,此格式的名字一般是用于传递函数 IoCreateDevice 所要求给出的设备名. 这个设备名可以在内核下使用,但是用户层程序无法使用 DOS设备名 - 设备名一般格式为:

NT 设备名 - 设备名一般格式为"\Device\自定义设备名",此格式的名字一般是用于传递函数IoCreateDevice所要求给出的设备名. 这个设备名可以在内核下使用,但是用户层程序无法使用

  • DOS设备名 - 设备名一般格式为:"\DosDevices\自定义设备名",此格式的名字一般用户传递给函数IoCreateSymbolLinkName 的参数,后面这个函数的功能是为一个NT设备名创建一个用户层能够使用的符号链接名.

    符号链接的创建和销毁

  • IoCreateSymbolicLink - 为一个NT设备名链接到一个DOS设备名,DOS设备名可供用户层程序使用.

  • IoDeleteSymbolicLink - 删除一个DOS设备名.

  • 在用户层中打开设备

    当驱动对象创建了设备对象,并且设备对象也建立了DOS符号链接:

    // 驱动程序入口函数
    NTSTATUS DriverEntry(DRIVER_OBJECT* driver,UNICODE_STRING* path) {
    ??? driver->DriverUnload = NULL;
    ?
    ??? UNICODE_STRING ntDeviceName;
    ??? RtlInitUnicodeString(&ntDeviceName,L"\\Device\\dev_test_1");
    ??? DEVICE_OBJECT* device;
    ?
    ??? NTSTATUS ret;
    ?
    ??? // 创建设备对象
    ??? ret = IoCreateDevice(driver,/*用于创建设备对象的驱动对象*/
    ???????????????????????? 0,/*扩展数据大小*/
    ???????????????????????? &ntDeviceName,? /*设备对象的NT设备名*/
    ???????????????????????? FILE_DEVICE_UNKNOWN,? /*设备对象的类型*/
    ???????????????????????? 0,? /**/
    ???????????????????????? 0,/**/
    ???????????????????????? &device/*被创建出来的设备对象*/);
    ??? if (!NT_SUCCESS(ret)) {
    ??????? return ret;
    ?? }
    ?
    ??? UNICODE_STRING dosDeviceName;
    ??? RtlInitUnicodeString(&dosDeviceName,L"\\DosDevice\\dev_test_1");
    ??? // 为NT设备名创建一个DOS符号链接名.
    ??? IoCreateSymbolicLink(&dosDeviceName,&ntDeviceName);
    }

    上面的代码创建的DOS设备名为:dev_test_1,(\\DosDevice\\只是一个前缀). 那么在三环中,可以通过以下方式打开此DOS设备:

    file = CreateFileW(L"\\\\.\\dev_test_1",/*设备名*/
    ?????????????????? GENERIC READ | GENERIC WRITE,/*设备的打开后的操作权限*/
    ?????????????????? 0,
    ?????????????????? NULL,
    ?????????????????? OPEN_EXISTING,
    ?????????????????? 0,
    ?????????????????? NULL);

    派遣函数和IRP

    派遣函数实则就是当设备接收到了IO请求之后被调用来处理IO请求的函数.

    例如:

    file = CreateFileW(L"\\\\.\\dev_test_1",
    ?????????????????? NULL);

    上述代码打开了一个设备对象,然后保存在驱动对象中派遣函数数组MajorFunction的第IRP_MJ_CREATE项就会被调用,如果这个元素被设置为NULL,则不会被调用,CreateFile也将调用失败. 调用此函数后,系统会将CreateFile传递的参数传递给派遣函数. 但派遣函数的原型(参数列表)中却没有这些形参:

    typedef
    NTSTATUS DRIVER_DISPATCH (
    ?? _In_ struct _DEVICE_OBJECT *DeviceObject,/*设备对象*/
    ?? _Inout_ struct _IRP *Irp /*IRP*/
    ?? );

    上面的代码就是所有派遣函数的原型,只有一个设备对象,和IRP结构体指针两个参数. 那么在用户层中传递过来的参数在哪里传递?

    系统其实已经将这些参数保存在了IRPIO_STACK_LOCALTION结构中.

    因此,IRP实际就是一个用户保存用户层传递进来的参数. 这些参数有多种,而且,对于不同的IO请求,会有不同的参数,而无论什么IO请求,多少个参数,都只能通过此结构体来保存,因此这个结构体比较庞大.

    在MSDN的文档中,专门有介绍不同的IO请求下,参数保存在IRP结构体中哪个字段:

    04内核-驱动对象

    04内核-驱动对象

    IO_STACK_LOCATION

    任何内核模式程序在创建一个IRP时,同时还创建了一个与之关联的IO_STACK_LOCATION结构数组:数组中的每个堆栈单元都对应一个将处理该IRP的驱动程序。 IRP的头部有一个当前IO_STACK_LOCATION的数组索引,同时也有一个指向该IO_STACK_LOCATION的指针。索引是从1开始,没有0。当驱动程序准备向次低层驱动程序传递IRP时可以调用IoCallDriver例程,它其中的一个工作是递减当前IO_STACK_LOCATION的索引,使之与下一层的驱动程序匹配。但该索引不会设置成0,如果设置成0,系统将会崩溃。就是说,最底层的驱动程序不会调用IoCallDriver例程。

    这个数组一般是紧随IRP结构体之后. 通过IoGetCurrentIrpStackLocation 函数就能过获取到当前设备的IO栈.

    IRP处理过程

    IRP处理概览(例子)

    下图是在用户层中打开文件时的过程(例如打开D:\\1.txt)

    04内核-驱动对象

    1. 在用户层通过子系统调用I/O系统服务来打开命名文件

    2. 进入到内核层. 由IO管理器调用对象管理器去查找和解析文件对象的符号链接,它会调用安全引用监视器来检查子系统是否具有正确的访问权限来打开文件对象.

    3. (编辑:ASP站长网)

    网友评论
    推荐文章
      热点阅读