Filesystem Driver Stack

 Filesystem Driver Stack

作者:tiamo
来源:白细胞

写这个文章的初衷是想知道究竟一个读写文件的irp都是怎样被处理的.....

大家都知道这样的一个读写文件irp是发送给file system的driver的
file system把这个irp交给了下层的device

这个device叫logical volume device,它由device的vbp里面的realdevice指针指出(不一定就会是这个device,而应该是这个device所在的stack的最上层的device).那这个device是个什么东西呢?它是怎么来的呢?按照道理,这个irp应该被发送到disk的驱动去才对?那个这个device是disk的驱动创建的么?如果不是,那中间都有些什么步骤呢?

本文就是回答上面这些问题的.
用softice看看就知道file system转发的irp并不是vpb指向的那个device,而是另外一个叫volsnap的driver创建的device.
再跟踪看看,volsnap创建的device是跟vpb的device在同一个stack里面,而且volsnap创建的device的下一个device就是vpb的device.

好了.至少知道了确实file system driver要把irp转发到vpb指向的device去.那接下来呢?irp又会到哪里?

再跟踪下去.irp到了一个由partmgr.sys创建的device里,看看这个partmgr...它是一个disk driver 的filter(多用注册表,当初我在这里花了好长时间才发现这个东西是一个filter),所以partmgr把这个irp转发到了disk driver.disk driver接着发给了acpi.sys,然后acpi.sys转发给了atapi.sys创建的一个device去,看看这个device的stack,已经是1了,irp就到尽头了,接下来的就不是irp的流程了,而是所谓的port/miniport流程了.

大致的把整个的结构过了一次,里面有很多的疑问,下面要一一解释,最好是能用device tree看看这个过程,看看哪些是pdo哪些是fdo,看看每个device的stack值都是多少,自己模范下看看能不能了解irp的流程.

上面的整个irp的流程是很大的.顶层device的stack达到了8,首先你就会奇怪这么庞大一个device stack是怎么建立起来的?

先说最上面的部分
file system在受到一个mount的fs control的时候,创建了一个无名的device,并且保存了vpb 的 realdevice所在的stack的最上层的device,以后所有的irp都转发到这个保存的指针上面.注意这里并没有attach到那个device的stack上面,而是直接使用指针call driver的.这里会有一个问题,在file system mount以后,再attach到volume device的stack上的device 收不到 file system传递过来的irp,至于这个问题怎么解决,msdn里面有说,把这个filter driver设置成启动时加载,至于为什么这样作了就能行,这个在后面会有解释.

在我的机器上,file system保存的device是由volsnap创建的一个filter device,在这个filter device下面是vbp指向的device,它有名字叫HarddiskVolume1(名字不一定就是这个),这个device是一个pdo,注意它是一个pdo,按照常规 pdo的创建不是由系统主导的,而是由driver主导了,它一般不在adddevice里面创建,而是由driver在需要的时候手动创建的.为什么说到这个细节呢?因为我们没有这个driver(名字叫ftdisk.sys)的源代码,在反汇编的时候,明白了这个以后比较有目标,这个ftdisk.sys的代码是非常简单的,ida反汇编一下就发现harddiskvolumeX这个device并没有attach到什么stack上面(也算是pdo的一般做法),但是这个pdo却保存了两个指针,一个指向另外一个pdo(70h),一个(74h)指向这个pdo的stack的最顶的device,而且以后的irp它都发给了这个device,这个device也就是由partmgr创建的device了.

partmgr其实只是一个filter,挂接到disk driver上面的,那么接下来的stack创建就比较简单了.用device tree向前看,partmgr创建的是一个filter,它attach的device叫dr0,是一个fdo,它对应的pdo叫idedevicepotolo-3,这个部分的过程就根据的容易了,disk driver是有源代码的,仔细看看就能明白其中的创建关系.看清楚了,这个pdo的stack值是1,所以irp到这里就算是尽头了.

再向下又是一个fdo,ideport(atapi.sys),它创建了上面的那个pdo,ideport我没有详细的反汇编分析,不过可以推测得出来,它对应得pdo是ide0channal0,而这个pdo又是由pciide创建的,你可能会注意到中间多了一个filter acpi.sys创建的一个device,也许你会注意到这个acpi.sys到处创建filter.

再往下就是pci,acpi_hal这些device了,他们的行为都很正常.不详细说了(其实是我没有详细的研究,只有些想当然的想法,不敢拿出来说)

到此,回顾下整个device stack,可以看到这里涉及到好多的device stack,irp并没有按照一个stack 走到底.

最上面的是file system的device
|fs filter| <-比如是filemon
|fs volume| <-一般没有名字,由filesystem在mount的时候创建

fs的volume保存有新的stack(partmgr device的stack? 叫这个名字么?)的最上层device的指针,irp转移到这个stack
|volsnap 创建的无名device |
|ftcontrol创建的storage volume device|

这个device有保存有另外一个stack顶层device的指针,irp再次转移
|partmgr创建的无名filter device|
|disk创建的DR0 fdo |
|acpi创建的filter |
|atapi创建的pdo |

这个就是irp的全流程,它经历了3个device stack.

呼呼
吐口气吧.......

本来这个文章写到这里就算完成了....
但是我还有些心得要写出来.这些属于是无心插柳的结果.只是在我跟踪上面这些结果的时候额外的收获.

先看device tree里面,pnp方式查看,你会注意到有很多的pdo属于pnpmanager,但是他们却没有对应的fdo.很奇怪吧,再看这些pdo的名字,你会发现partmgr赫然在目.奇怪了,它怎么有个fdo呢?

首先要明确的就是凡是有填充adddevice域的driver都会对应一个pdo,这个是必然的,因为adddevice传递进来的参数就有一个是pdo,还有一个事情就是在某种情况下,即使不填充adddevice域,pnpmanager还是会创建一个pdo.你所看到的大多少没有fdo的pdo都是这样来的.那什么时候pnpmanager创建这些pdo呢?答案就在系统启动的时候.

你也应该要知道,有些驱动并不是由ntoskrnl加载的,有很多的driver是由loader加载的,win2000 把这些叫做 boot driver.

ntldr加载必要的driver以后,转移控制权给ntoskrnl的入口函数(kiinitsystem是这样么?),经过一定的步骤以后来到了ioinitsystem函数,这个函数作一些初始化以后开始了pnp系统,这里ntoskrnl创建一个pnpmanager的root device,并且start了这个device,然后发送query bus relation,这个时候pnp manager读取注册表一一创建每个需要创建的service的pdo,构造device tree,然后调用每个boot driver的driver entry函数,然后是他的add device函数(实际情况比我上面描述的要复杂得多).

然后pnp就开始了众所周知的enum 工作,这个是委托给worker线程完成的,大部分情况是使用workitem进行的,这个部分显得非常的复杂,跟踪调试非常的不方便(恕我也没有完全的弄明白,先跳过去,以后有机会再详细的讨论这个部分).

跳过了xxx的步骤以后,pnp enum到了主板的南桥芯片(我的是ich4)设备,创建对应的fdo(没有名字,属于pci这个driver),继续enum,得到ich4里面的mass storage controller,创建pdo也是属于pci的,创建对应的fdo,pciide(中间有个acpi的filter加进来,主要进行电源管理),这个fdo再enum,创建两个ide channal的pdo,再创建对应的fdo,ideport(由atapi提供,还是有acpi的filter加进来),ideport再enum机器上的硬盘pdo,继续加载acpi的filter,加载硬盘的fdo,也就是disk.sys了,再加载upperfilter,partmgr.sys.

接下来的事情关键了,硬盘的fdo告诉pnp它的bus relations要更新(可以查看disk.sys的源代码观察这个部分进行的事情),然后pnp发送一个 query bus relations的irp给硬盘pdo所在的stack,这个stack的最上部分并不是硬盘的pdo,也不是acpi的filter,也不是硬盘的fdo,而是partmgr.sys这个driver,它接受到这个irp的时候,安装一个complete routing,然后把irp向下传递,等到控制权再回到手上的时候,partmgr.sys作了一系列的事情,最最重要的就是它发送了一个IO Control irp到另外的一个driver.

接手这个irp的是ftdisk.sys这个driver(指basic volume的情况,如果是dynamic volume的话呢,接受的是dmio.sys这个driver),这个driver然后创建自己的pdo,并且把这个pdo关联(不是attach)到了partmgr.sys所在的stack,实现了上面两个stack之间的关联.这个pdo也加载了自己的fdo,一个叫volsnap.sys提供.

这个irp完成以后,partmgr完成原来的query bus relations irp,可以看到disk.sys创建了若干个pdo,attach到了自己的fdo上面,再这以后,disk所在的stack的top device就不是partmgr.sys了,而是最后一个由disk创建的pdo.而partmgr在系统里面注册了通知消息,它能在以后的volume change的时候得到通知,然后会发送相应的io control irp到ftdisk,然后ftdisk完成更新.

接下来事情又变得简单了,file system driver加载,在遇到第一次访问磁盘的时候,mount到由ftdisk创建的pdo上面去,再次实现两个stack的关联.

上面就是整个过程的大概流程.

明白了这个过程,要实现一个虚拟xx的就容易了.
说说daemon-tools一类的实现方式吧.

他们基本都提供一个bus driver(d343bus.sys),作为一个 boot driver 由ntldr加载到内存,ntoskrnl在适当的时候会调用它的driverentry以及adddevice函数,这两个函数里面分别加载了fdo和创建了自己的pdo,这个pdo的inf文件里面表示它要加载的driver是一个叫d343port.sys的driver,这个driver再在scsiport的帮助下create了一个pdo.这个pdo返回的id很有趣,SCSI\CDRom,SCSI\RAW,这样的话,windows就把它当作了一个cdrom来处理.再向上的fdo,filter都交给windows来操作了.

如果是一个虚拟硬盘呢?你马上就反映过来了,报告pdo的id为Gendisk一类的就ok.

当然实际的实现远不是这么简单的,bus driver,mini port driver都有很多要注意的地方.
(以上大部分属于想象推测,没有经过严格的认证,有错的地方请包涵).

到这里基本上这个文章又结束了.
也许有人要问这些东西是怎么来的?当然是用soft ice + 2000代码 + ida来的了,唯一要修改的就是要让softice在所有的外部driver的driverentry调用前正常工作.这个方法在驱网的论坛上能找到

你可能感兴趣的:(汇编,File,tree,filter,System,disk)