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"Devicedev_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"DosDevicedev_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结构体指针两个参数. 那么在用户层中传递过来的参数在哪里传递?
系统其实已经将这些参数保存在了IRP 和IO_STACK_LOCALTION 结构中.
因此,IRP实际就是一个用户保存用户层传递进来的参数. 这些参数有多种,而且,对于不同的IO请求,会有不同的参数,而无论什么IO请求,多少个参数,都只能通过此结构体来保存,因此这个结构体比较庞大.
在MSDN的文档中,专门有介绍不同的IO请求下,参数保存在IRP结构体中哪个字段:


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 )
(编辑:威海站长网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|