阅读:8343回复:27
如何判断过滤、分层驱动程序收到的IRP是否是发给自己的?
设有“设备驱动程序A - 过滤、分层驱动程序B - 过滤、分层驱动程序C ”分层形式。A、B、C 都要分别和 WIN32 应用程序交换数据。当然,A、B、C 都暴露了设备名。
毫无疑问,A 和 B 的IRP都被 C 接收了,那么,当“过滤、分层驱动程序B”收到一个IRP,比如是IRP_MJ_READ,如何知道此IRP是发给自己的还是发给“设备驱动程序A ”的? [编辑 - 4/1/02 作者: laser36] |
|
沙发#
发布于:2002-03-29 12:15
既然B是A的上层过滤驱动,则任何应用程序或内部驱动程序发送到A的请求必须先经过B。
要判断一个IRP是发到目标驱动程序还是发到过滤驱程序,其实很简单,你只要在你的设备扩展数据结构中加一个标志位,以示你所接到的请求是发到自己还是拦截到的本发到目标驱动程序的IRP。 比如说: enum WHERE_IRP { ToMyselfs, TargetDevice } typedef struct _DEVICEE_EXTENSION { ...... WHERE_IRP whom; ...... } 然后你需要用你的驱程序创建不只一个设备对象,一个用于和Win32进行通信,这个不叫过滤设备对象,因为过滤设备对象一定是有设备名的,也不可能有符号连接名。在这个设备对象里面,你设置设备扩展中的扩展为ToMyself;另外你还需要创建一个过滤设备对象挂接在目标驱动程序的设备栈顶端,以截获所有到目标设备对象的IRP。在这个设备扩展里,你设置标志为ToTargetDevice即可。 然后在每个分发例程里,先取得设备对象的设备扩展,判断这个标志就可以知道源IRP到底是想发到谁。 Best regards ! |
|
|
板凳#
发布于:2002-03-29 12:19
sorry sorry,打漏了字:过滤设备对象一定是没有设备名的,也不可能有符号链接名。
|
|
|
地板#
发布于:2002-03-29 16:44
Tom_lyd:
据我最近K书心得,过滤驱动程序应该也可以有自己的设备名和符号链接名。 而设备扩展是驱动程序私有的,用于存放一些状态,而IRP是管理程序发来,用于说明操作的信息,应该和设备扩展没什么关系,那么,过滤驱动程序又怎么能根据自己的设备扩展来判断,它收到的IRP是发给谁的。 |
|
地下室#
发布于:2002-03-29 17:48
Tom_lyd: 我想谈一下我驻Filter Driver的认识,以澄清对概念的误解。 首先,不管什么驱动程序,如果不创建设备对象,那这个驱动程序就失去了意义,这只要根据IoCreateDevice和IoCreateSymbolicLink两个例程就可以看出来。你所说的过滤驱程序也可以有自己设备名和符号连接名,这句话是正确的,但并不等于过过滤设备对象可以有名字和符号连接名,\"设备驱动程序\"和\"设备对象\"两个之间的差距还是很大的。你所说的驱动程序可有以设备对象名和符号连接名,是指驱动程序创建的设备对象可以有设备名。设备驱动程序之所以要/可以创建一个命名的设备对象,是因为它不仅仅是拦截到目标设备对象的IRP,还需要一些用户态(win32)态提供的设置信息。这个时候,它必须创建一个对Win32可见的符号连接名。也就需要一个设备对象名。但请记住,这个设备对象(用IoCreateDevice创建的、有名字的、和Win32通信以接收用户设置的设备对象)绝对不是过滤器设备对象(Filteror),而是一个普通的设备对象、和你的目标驱动程序创建的设备对象没有等级之分。 然而,如果你想要用这个设备对象(有名字的)去截获本想发到目标驱动程序的IRP时,那是在做梦!因为过滤设备对象(不是过滤驱动程序)一定是没命名的设备对象,只有符合这样的规范的设备对象才被允许来IoAttackToDeviceStack到目标设备对象上。并且这个设备对象是绝对不会接收也没有机会接收用户的设置,因为它不可能提供给用户一个符号连接名。 以上是第一点你所认识的误区:驱动程序和设备对象。 [quoto] 而设备扩展是驱动程序私有的,用于存放一些状态,而IRP是管理程序发来,用于说明操作的信息,应该和设备扩展没什么关系,那么,过滤驱动程序又怎么能根据自己的设备扩展来判断,它收到的IRP是发给谁的。 [/quote] 我想,第二点你应该明白了吧!设备扩展是驱动程序私有的,我想你的意思可能是说在拦截的过程中获得的设备对象中设备扩展的信息怎么会神奇地记载着IRP是发给谁的。我来给你解释! PDEVICE_EXTENSION 里面有一个可见域就是DeviceExtension,它是一个指针,设备对象创建者必须在IoCreateDevice例程是指出他所定义的设备扩展的大小(这样系统才知道给你分配多少空间来保存你的扩展信息啊!),至于这个里面的信息内容,嘿嘿,请千万记住,不是系统填写的,是由你自己来填写的。所以你总会看IoCreateDevice调用后跟着这样的语句: PDEVICE_EXTENSION pDevExt; pDevExt=(PDEVICE_EXTENSION)DeviceObject->DeviceExtension; pDevExt->info1=...... pDevExt->info2=...... ... ... 微软给的这个白便宜真是好占!!!:) 第二点:你不是过滤吗?你不是创建了过滤设备对象吗?什么叫做过滤设备对象? 所谓的过滤设备对象是指先不管罗卜白菜的IRP或者FastIO统统地挡过来看看。千万明白是设备对象在做这些事情,就象C++中的类创建的对象一样,并且这个设备对象是我自己的驱动程序对象创建的,凭什么不能记载自己的信息?不错啊,是私有的啊?当然如果你想记载一些信息到目标设备对象(别人的驱动程序对象创建的设备对象)的扩展中写一些信息的话,那机器就爆炸了。你哪来的权利?别人开的驱动程序竟被你动态修改了。很少,不管是FastIo例程还是IRP请求,只有两个来源,一个是win32应用程序发出来的、一个是内部其他的组件发来的(真可怜,它们本想直接到目标驱动程序,没想到被我先占了便宜)。既然是经过我自己创建的设备对象,我当然有权利查看你过来的信息(否则我怎么知道你是不是带着炸弹来的?)。很好,我有一个enum WHERE whom变量, if(whom==ToMyself) { it must come from win32,the user must want to set up some information; } else if(whom==ToTargerDevice) { 哦,原来你是想暗度陈仓啊,哈哈,逮住了,先让俺爷们收拾收拾再说:); if(我对这个东西感兴趣) 想到那里去,先留下买路钱; else(我对它不感兴趣) 去吧去吧,看你这可怜相; } 以上是我对过滤驱动程序的一点总结,我最近也正在做过滤驱动,希望多交流。 最后一点总结:驱动程序就象C++中的类,如果不生成对象(IoCreateDevice),那么它所提供的所有函数将无法被调用。 |
|
|
5楼#
发布于:2002-03-29 23:11
每个设备可以有DeviceExtension也可以没有关键在于你在创建这个设备的时候有没有为这个DeviceExtension提供了DeviceExtension大小,因为在调用IoCreateDevice的时候第二个参数就是指定DeviceExtension的大小,
如果你想在DeviceExtension里面做一个标记来判断这个IRP是否是发给自己的,那是不可能的,不要忘了需要考虑同步 因为当两个IRP同时来的时候你就会出错 你在驱动程序里面创建的设备对象要么就是过滤设备,要么就是目标设备,不可能一个设备负责两个功能的,不过你可以这样,就是当有需要的时候,应用程序发一个请求给过滤设备,当过滤设备得到了这个请求之后再构造一个IRP发给目标设备。 过滤设备对象也可以有名称和提供给应用程序的符号连接 |
|
6楼#
发布于:2002-03-30 10:09
To guardee大侠:
对我发表的回答作如下解释: 在你的回答中你说道: \"你在驱动程序里面创建的设备对象要么就是过滤设备,要么就是目标设备,不可能一个设备负责两个功能的\" 这句话并无异议,但并不和我所说的矛盾。因为我的驱动程序不但要过滤到目标设备对象的IRP,还要接收来自用户的可能的设置信息,不错,一个设备对象不能同时完成这两个任务,所以我们才需要创建不只一个设备对象(至少两个),一个用来给用户设置信息(这一个不是过滤设备对象,而是可以被称为你所说的\"目标设备对象\"),它是有设备对象名和符号连接名的;另一个设备对象则用来过滤到真正的目标设备对象的IRP请求,这个设备对象一定是没有命名的,也不可能有符号连接名。 另外一点,在设备扩展中的变量的同步问题。 不知道guardee兄弟有没有研究过File monitor的源码,在那里你会发现,它正是用这样的一种机制来判断被过滤设备对象拦截到的请求本是到目标设备对象的还是直接发给自己的。对于同步问题,我似乎没有考虑过设备扩展访问同步的问题,只考虑过全局变量的同步问题。我认为设备扩展中的变量是不需要考虑同步的。这有两点原因: 1.我翻遍了我身边的所有关于驱动程序开发的书以及网上下载的一些源代码,没有发现一个考虑了设备扩展中的变量的同步。他们都只考虑了全局变量的同步。 2.不知你有没有下载IFSKIT,这里面有一篇关于过滤驱动程序的源码,它同样是在设备扩展中加一个标记变量以示IRP是给谁的。 当然,上面有些内容我并没有看到哪本书上有这些内容的直接描述,比如说设备扩展的同步,而是自己在工作过程中根据分析一些源代码的分析得出的结论,所以可能有错误之处,但有些东西我是敢肯定的。 最后,非常感谢guardee兄弟发表的观点,并且请论坛各位同仁发表观点,关注此话题。 |
|
|
7楼#
发布于:2002-03-30 12:34
很精彩,希望大侠们多多发言,呵呵
|
|
|
8楼#
发布于:2002-03-30 13:43
小的佩服!
|
|
|
9楼#
发布于:2002-03-30 16:34
不对
过滤设备对象也可以有设备对象名和符号连接 |
|
10楼#
发布于:2002-03-30 17:01
< BACK NEXT >
[oR] Writing Filter Drivers A filter driver is a special type of intermediate driver. Filter drivers perform their work surreptitiously. They sit on top of some other driver and intercept requests directed at the lower driver\'s Device objects. Users of the lower driver are completely unaware that their requests are being preprocessed or intercepted by the filter driver. Some examples of the use of filter drivers include the following: Filters allow modification of some aspect of an existing driver\'s behavior without rewriting the entire driver. SCSI filter drivers work this way. Filters make it easier to hide the limitations of lower-level device drivers. For example, a filter could split large transfers into smaller pieces before passing them on to a driver with transfer size limits. Filters allow the addition of new features like compression or encryption to a device without modifying the underlying device driver or the programs that use the device. Filters allow the addition or removal of expensive behavior (like performance monitoring) a driver may not perform at all times. The disk performance monitoring tools in Windows 2000 work this way. The remainder of this section explains how to write filter drivers. Bear in mind that driver-allocated IRPs and I/O Completion routines work the same in a filter driver as they do in a regular layered driver. How Filter Drivers Work The main distinction between filter drivers and other layered drivers is in the Device objects they create. Whereas a layered driver exposes Device objects with their own unique names, filter drivers\' Device objects have no names at all. Filter drivers work by attaching one of these nameless Device objects to a Device object created by some lower-level driver. Figure 15.2 illustrates this relationship. Figure 15.2. Filter driver operation. In the diagram, FLTDRIVER has attached a filter Device object to FD0, one of FDODRIVER\'s Device objects. Any IRPs sent to FD0 are automatically rerouted to the Dispatch routines in FLTDRIVER. It works as follows: The AddDevice routine in the filter driver creates an invisible Device object and attaches it to a named Device object belonging to a driver beneath it. A client of the lower-level driver opens a connection to FD0. This is typically done using the Win32 CreateFile method to obtain a handle, or a kernel-mode client can use IoGetDeviceObjectPointer. Regardless, the I/O Manager actually opens a connection between the client and the filter driver\'s invisible Device object. When the client sends an I/O request to FD0, the I/O Manager sends it to the filter driver\'s unnamed Device object instead. The I/O Manager uses the MajorFunction table of the filter\'s Driver object to select an appropriate Dispatch routine. The Dispatch routines in the filter driver either process the IRP on their own and complete immediately, or they send the IRP down to FD0 with IoCallDriver. If the filter driver needs to regain control of the IRP when a lower-level driver completes it, the filter can associate an I/O Completion routine with the IRP. Filters can also be layered above other filters. Attaching a new filter to an already filtered Device object results in the new filter simply getting layered on top of the highest existing filter. Essentially, any number of filter layers can exist for a single Device object. Initialization and Cleanup in Filter Drivers Like every other kernel-mode driver, a filter driver must have a main entry point called DriverEntry. Like other WDM drivers, it must export an AddDevice, RemoveDevice, and Unload routine. The following sections describe what these routines must do. AddDevice routine The initialization sequence in a filter driver for the PnP request to add a device is straightforward. The filter calls IoCreateDevice to create a filter Device object for this target device. The filter Device object has no internal name, nor does it have a symbolic link name. It calls IoAttachDeviceToDeviceStack (as usual) to stack itself on top of the lower driver and obtain a pointer to the target Device object. It stores the address of the target device object in the Device Extension of the filter Device object. Other parts of the filter driver use this pointer to call the target driver. Next, AddDevice copies the DeviceType and Characteristics fields from the target Device object to the filter Device object. It also copies the DO_DIRECT_IO, DO_BUFFERED_IO, DO_POWER_INRUSH, and DO_POWER_PAGABLE bits from the target Device object\'s Flags field. This guarantees that the filter looks the same and has the same buffering strategy as the target driver. RemoveDevice Routine A filter driver\'s RemoveDevice routine must disconnect the filter and target Device objects. It does this by calling IoDetachDevice and passing a pointer to the target Device object. Once the filter Device object has been detached, the RemoveDevice routine calls IoDeleteDevice to delete the unnamed object. Making the Attachment Transparent Once a filter has attached itself to the target driver, any I/O requests sent to the target must pass through the Dispatch routines of the filter driver first. If the MajorFunction table of the filter Driver object does not support the same set of IRP_MJ_XXX codes as the target driver, clients of the target may experience problems when the filter is attached. Specifically, some types of requests that work without the filter are rejected as illegal operations when the filter is in place. To avoid this inconsistency, the filter driver\'s MajorFunction table must contain a Dispatch routine for every IRP_MJ_XXX function supported by the target driver. Even if the filter is not interested in modifying a particular MajorFunction code, it still must supply the Dispatch routine that simply passes the IRP on to the target driver. The most straightforward way for the filter driver to avoid the inconsistency problem is to provide a pass-through Dispatch routine for every slot within the MajorFunction table of the filter Driver object. For each MajorFunction entry that the filter wishes to override, a non-pass-through function is provided. The sample driver in the next section demonstrates this technique. < BACK NEXT > guardee兄弟: 你可以保留你的观点,或许你的观点是对的,因为毕竟WDM不是我们创造的,概念也不是我们创造的。我之所以说过滤设备对象是无设备对象名和符号连接名是有证据的。 以上的文章是从Art Baker、 Jerry Lozano著的《Windows 20000设备驱动程序设计指南》中第15章\"分层驱动程序的设计\"摘出来的一段。 另外,不知你有没有看DDK文档中关于IoCreateDevice函数中关于DeviceName参数的说明,如下: DeviceName Optionally points to a buffer containing a zero-terminated Unicode string that names the device object. The string must be a full path name. Typically, only Physical Device Objects (PDOs), which are created by PnP bus drivers, are named. PnP function drivers and filter drivers should not specify a DeviceName for a Functional Device Object (FDO) or filter device object (filter DO). Naming an FDO or filter DO bypasses the PnP Manager\'s security. If a user-mode component needs a symbolic link to the device, the function or filter driver should register a device interface (see IoRegisterDeviceInterface ). If a kernel-mode component needs a legacy device name, the driver must name the FDO, but naming is not recommended. |
|
|
11楼#
发布于:2002-03-30 19:06
我就写过给过滤设备命名并且提供符号连接的驱动程序,可能这个微软不赞成这么做罢了!
|
|
12楼#
发布于:2002-03-31 16:38
Tom_lyd:
在设备扩展数据结构中加一个标志位enum WHERE whom变量,以及可以自己来填入信息我都没有异议,可是,就如同你所说的“里面的信息内容,不是系统填写的,是由你自己来填写的”,那么过滤程序收到IRP后,系统并不会自动修改whom变量,那么又如何根据此whom变量来判断IPP是发给谁的?而且,IRP本身应该也不携带有whom变量信息。所以,对于为什么能用enum WHERE whom变量来判断IRP地目的地,我实在还是有些胡涂,另外,你提到的File monitor的源码和IFSKIT 能否发给我,我好仔细研究研究。 邮箱: laser369@263.net ,(此信箱可收8M附件,可放12M数据) [编辑 - 3/31/02 作者: laser36] [编辑 - 4/1/02 作者: laser36] |
|
13楼#
发布于:2002-03-31 20:37
|
|
|
14楼#
发布于:2002-03-31 21:05
Laser36兄弟:
对于你的如下解答: 1.我在附件里附了两副图,是讲解Filter的原理的,有助于理解设备对象的用途。 2.你需要的File monitor的源码在www.systeminternals.com网站有下,是Free的。 3.Win2000驱动程序是包驱动的,包即IRP。IRP是由I/O Manager系统组件构造的,它构造IRP并不是凭空的,而是依赖于应用程序或内部其它驱动程序传递来的请求参数。打个比方来方应用程序app.exe需要调用NTFS.sys提供的文件服务进行文件操作,它发出请求到I/O Manager,I/O Manager接到请求以后它能立即分析出这些内容:\"app.exe某人请求我去跟NTFS.sys大人打个报告,叫他老大人高抬贵手让我进行一些文件操作\"。NTFS.sys自身当然是死的,但是他的替身NTFS文件系统的设备对象(只有Win2000运行时才被创建)接管所有的这些东西。这里我所需要注意的是I/O Manager组件一定能根据app.exe传来的参数判断出app.exe需要的是NTFS.sys(严格地说是它创建的设备对象)提供的服务,而不是FAT32.sys(严格地说是FAT32创建的设备对象)。所以这是第一点,应用程序的具体要求它自己会说清楚,不会麻烦I/O Manager老大人,否则 的话I/O Manager一定会气得翘起胡子骂道:\"连自己要干什么都不知道就跑到我这来瞎胡闹,去去去\"。第二点,我们再回到你创建的设备对象上来。 你至少需要创建不少于两个设备对象,一个用来供用户设备信息,这个设备对象的WHERE==ToMyself,只有那些你的设备请求会被传递到这里来,到目标驱动程序的IRP一定不会光顾它。此外,你还需要创建一个Filteror(过滤设备对象),是用来截获到目标程序的IRP的,这个WHERE==ToTargetDevice,任何本想发到目标驱动程序的IRP都会光顾这里,可别忘记了,你在创建这个DeviceObject成功后,一定有IoAttachDeviceToDeviceStack(PSOURCEDEVICE,PTARGETDEVICE)或类似的调用,只有这样,你才能过滤人家呀!并且,只有原本想到PTARGETDEVICE的IRP请求才会到(一定会到)你创建的Filteror里,其它的一般来说不会过来。至少I/O Manager没有那么糊涂。 设备对象一经创建后,里面的内容是相对固定的,而IRP却是活的,它就象Windows2000的循环系统,流淌着鲜活的血液,维系着Windows2000的运行。IRP被发送到谁不是由它自己决定的,它的命运被牢牢着掌握在I/O Manager系统组件手里,至于I/O Manager要将IRP的命运转交给谁去驾驭,就看是谁要求I/O Manager去构建这个IRP。 我不知道我这样说你能否理解清楚。如果还不能理解清楚,请仔细阅读File monitor的源码。 best regards ! |
|
|
15楼#
发布于:2002-04-01 08:25
精彩,这样的讨论多多益善!
|
|
|
16楼#
发布于:2002-04-01 11:15
1. 即使是普通的命名的设备对象,哪怕连DosDeviceName也有
也是可以通过IoAttachToDeviceStack连接到某个设备对象上的, 只要先通过设备名用IoGetDeviceObjectPointer获得要被连接 设备对象指针 2. 判断是否发给自己的能否通过IoGetCurrentProcess( )来获 得呢? |
|
|
17楼#
发布于:2002-04-01 11:27
还有啊
你为什么要判断是否发给自己的呢? 其实把自己的应用可能发给自己的设备对象的指令都定义成Io Control Code,用DeviceIoControl传下去不可以吗? 这样也就不需要判断了,只要把感兴趣的东东截获就可以了 |
|
|
18楼#
发布于:2002-04-01 14:45
To laser36:
laser36:我仔细想了一下这些问题,发现我的回答存在一些不确切之处,特此更正,并表示道歉! 其实你的问题的实质是反映了一个多层过滤的问题。在设备A的顶端存在着B、C两相过滤器,原本直接到A的请求会先后经过C和B。A、B、C都暴露了设备名是为了和Win32建立连接关系以和它进行通信,满足用户的一些设置要求。 这里A是天经地义的,因为它不是过滤驱动程序,肯定有设备名。而B、C在这里也有设备名是因为它在创建了一个过滤设备对象以外,还创建了一个处理来自Win32用户设置的设备对象,这个设备对象不是过滤设备对象,当然它可以有设备名,并且它跟普通的目标设备对象的地位是平等的。 设备对象栈构成以后,IRP的处理流程便形成以来。到A的IRP无一遗漏地会依次经过C和B,并处理这些IRP的必定是C和B建立的过滤设备对象,而是不那个有设备对象名的和Win32通信的设备对象。分开来说,B创建的过滤设备对象接受到的IRP必定是传到A的,而不可能是想传到自己的,而传到B创建的普通设备对象(有设备对象名)的IRP必定是来自Win32的用户设置,这个IRP不可能是发到A的;同样,如果C也是A的过滤设备对象,则所有到A的请求都会无一遗漏地经过C创建的过滤设备对象,而所有对C的有关设备IRP(来自Win32)都会经过C创建的普通设备对象(有设备名的)。说到这里也许我还并没有真正解决你的问题,那么请看下面: 假如B、C都创建了两个设备对象,一个有名字,一个无名字。有名字的不是过滤器,它负责接收来自Win32的设置,无名字的负责过滤到目标驱动程序的IRP。则当B的用户端发送一个IRP到有名的设备对象时,它的IRP不会经过C,也不会下传到A。因为C是A的过滤,C在挂接到A时(我默认所有的过滤器都是上层过滤),并不是直接挂接到A的设备对象本身,而是通过一系列的调用得到A可能已有的上层过滤驱动。这里就是B。因此,C便将自己创建的设备对象(无名的)挂接在B创建的过滤设备对象上方。正因为这样,到A的请求才会依次经过C和B。但是,C不是B的过滤(严格地说C不是B创建的有名的设备对象的过滤,所以从B的客户程序传来的请求不会经过C,而是经由I/O Manager直接传给B。C在注册过滤设备对象时尽管连接到的目标设备对象是A的最上层过滤设备对象B,但是B创建了两个设备对象,它只是连接到了其中的一个。所以我估计,这一点就是laser36兄弟最大的疑惑。 我特用一个图形表示这个流程,见附近! 另外,我的错误在于在DeviceExtension中作标记标志IRP的去向,这是错误的, 在此我特地更正,也向laser36及各位兄弟道歉。 我之所以认为这样是因为看到File monitor的源代码,后经仔细分析后,FM设置此项的理由是它创建的多个设备对象共享Dispatch例程和Fast I/O 例程,然后在Dispatch和Fast I/O 例程中根据设备扩展中的此项来判断接受到的IRP是到哪个设备对象的。这只是一个处理技巧,不管怎么说还是值得借鉴。 |
|
|
19楼#
发布于:2002-04-01 16:28
我不这样认为
B、C并不需要分别创建2个设备对象 发向A的IRP必然经过B、C,而发向B的IRP必然经过C 因为IO管理器发送IRP的目标是设备栈而不是设备 IO管理器只把IRP发到某个设备栈的顶端 向下传的动作是由设备驱动来做的 所以在设备栈的任何一层都可能并且可以完成该IRP并返回 |
|
|
上一页
下一页