转载) VPB的来龙去脉(一)

http://blog.dynox.cn/?p=601

VPB是Windows I/O Manager子系统的一个重要的数据结构,全称为 Volume Parameter Block。它的任务是绑定卷设备(如磁盘分区或虚拟磁盘)和接管此卷设备的文件系统(如FastFat,NTFS)。Windows系统上的挂载点(Mount Point,如盘符C:)只能定位至卷设备(如\Device\HarddiskVolume1),Windows Object Manager(对象管理器)在解析路径名时(即Name Parsing过程)还要通过卷设备的VPB信息进一步定位至接管此卷设备的文件系统驱动上。

Relationship of VPB, Storage Volume Device and File System Logical Volume Device

图一

VPB结构说明:

定义于WDK头文件inc\ddk\wdm.h中

typedef struct _VPB { 
    CSHORT Type; 
    CSHORT Size; 
    USHORT Flags; 
    USHORT VolumeLabelLength; // in bytes 
    struct _DEVICE_OBJECT *DeviceObject; 
    struct _DEVICE_OBJECT *RealDevice; 
    ULONG SerialNumber; 
    ULONG ReferenceCount; 
    WCHAR VolumeLabel[MAXIMUM_VOLUME_LABEL_LENGTH / sizeof(WCHAR)]; 
} VPB, *PVPB

成员说明:

    • Type:  魔数标志 IO_TYPE_VPB
    • Size:   sizeof(VPB) 
      Flags:  标志位,相关标志描述如下: 
                  VPB_MOUNTED (1<<0):       此卷已被文件系统识别并已挂载 
                  VPB_LOCKED (1<<1):           此卷已被文件系统锁定,锁定操作由请求FSCTL_LOCK_VOLUME完成 
                  VPB_PERSISTENT (1<<2):   将VPB一直保留在内存中(不释放),即使此VPB引用计数为0 
                  VPB_REMOVE_PENDING (1<<3): 表示此存储设备即将被卸载/删除。此标志由Pnp Manager(即插 
                                                                    即用管理器)管理和使用。此标志在可在处理Pnp请求 
                                                                     IRP_MN_CANCEL_REMOVE_DEVICE时清除 
                   VPB_RAW_MOUNT (1<<4): 指定此卷仅由系统RAW文件系统接管
    • VolumeLabelLength:  卷标长度(in bytes)
    • DeviceObject:           未命名的文件系统逻辑卷对象(unnamed logical volume)
    • RealDevice:               物理卷设备(如\Device\HarddiskVolume1)
    • SerialNumber:           卷序列号
    • ReferenceCount:      VPB的引用计数,用以控制VPB的生命周期
    • VolumeLabel:             卷标,最长32个双字节 
                                        #define MAXIMUM_VOLUME_LABEL_LENGTH (32 * sizeof(WCHAR))

      VPB的操作(包括初始化)是由I/O Manager及文件系统二者共同完成的,所以多头管理正是VPB的复杂性所在,好在正常情况下,VPB的申请和释放均由I/O Manager实施,此时VPB从创建到释放的生命周期中基本上是单线式发展的。但当遇到Force Dismount及Remount特殊情况时,文件系统会直接干预VPB的创建及释放,致使VPB的单线式的发展周期一分为二,致使VPB的管理变得更加复杂。

      我们先从正常情况开始介绍,先介绍几个VPB相关的内部函数:

      0: kd> x nt!*Vpb* 
      fffff800`01d62940 nt!IovpBuildDriverObjectList = <no type information> 
      fffff800`0189f150 nt!IopDereferenceVpbAndFree = <no type information> 
      fffff800`01c54400 nt!IopCreateVpb = <no type information> 
      fffff800`01968830 nt!IopReferenceVerifyVpb = <no type information> 
      fffff800`01865d0c nt!IopMountInitializeVpb = <no type information> 
      fffff800`01865db0 nt!IoAcquireVpbSpinLock = <no type information> 
      fffff800`01af7640 nt!IopVpbSpinLock = <no type information> 
      fffff800`01903ab0 nt!IopCheckVpbMounted = <no type information> 
      fffff800`01865a48 nt!IopQueryVpbFlagsSafe = <no type information> 
      fffff800`01c827b0 nt!HvpBuildMapAndCopy = <no type information> 
      fffff800`01865de0 nt!IoReleaseVpbSpinLock = <no type information> 
      fffff800`01c82ca0 nt!HvpBuildMap = <no type information>

      IoAcquireVpbSinLock()及IoReleaseVpbSpinLock()为系统支持函数(kernel support routine)。Windows I/O Manager在内核中维护一个全局的spinlock锁,以保护所有对Vpb结构体的访问。文件系统和I/O Manager本身在访问Vpb时都必须调用IoAcquireVpbSpinLock(),访问结束后再调用IoReleaseVpbSpinLock()释放全局自销锁。

      IopCreateVpb(): 从NonPagedPool中申请VPB,然后简单初始化VPB(type,size)并与RealDevice一起绑定

      IopCheckVpbMounted(): 检测DeviceObject是不是已经挂载,若没有则会调用IopMountVolume尝试去挂载

      IopMountInitializeVpb(): 设置新挂载卷设备的Vpb(设置VPB_MOUNTED标记,增加引用计数等),由 
                                              IopMountVolume挂载成功后调用

      IopQueryVpbFlagsSafe(): 获取设备DeviceObject的VPB的标志位信息,即DeviceObject->Vpb->Flags。如 
                                                果DeviceObject->Vpb为NULL,则返回0值。(较新Windows版本才引入的函数) 
      IopReferenceVerifyVpb(): 如果卷设备已被挂载,则返回其VPB及对应的文件系统逻辑卷设备对象,此函数 
                                                增加VPB的引用计数

      IopDereferenceVpbAndFree():顾名即知其义,此函数会减少VPB的引用计数,如果引用计数归0则会释放 
                                                       此VPB至NonPagedPool。(特例VPB_PERSISTENT除外)

      VPB的创建及初始化:

      VPB真正是由IoCreateDevice()在创建新设备对象时分配的,当然IoCreateDevice()是调用的 IopCreateVpb()来做的VPB的分配及最初的初始化。

      IoCreateDevice()不是对所有的设备对象都分配VPB,而是只针对4种设备:

      • FILE_DEVICE_DISK (0×07)
      • FILE_DEVICE_TAPE (0x1f)
      • FILE_DEVICE_CD_ROM (0×02)
      • FILE_DEVICE_VIRTUAL_DISK (0×24)

      VPB的初始化并不复杂,因为VPB的结构体并不庞大,但是,其初始化操作相当分散,由下面的3个过程共同完成:

      • IoCreateDevice()/IopCreateVpb()
      • IopParseDevice()/IopCheckVpbMounted()/IopMountVolume()/File system IPR_MN_MOUNT_VOLUME 
        handler(以FastFat为例:FatMountVolume)
      • IopParseDevice()/IopCheckVpbMounted()/IopMountVolume()/IopMountInitializeVpb()

      具体每一部分都做了什么并不难发掘,读者不妨用windbg作个简单实验。

      下面以FastFat (Windows 7 X64)为例介绍一个已加载卷的VPB内容,实验时我们插入一个FAT32格式的SD卡,系统会自动挂载。下面来枚举FastFat文件系统队列中所有的逻辑卷:

      0: kd> ?? fastfat!FatData 
      struct _FAT_DATA 
         +0×000 NodeTypeCode     : 0×500 
         +0×002 NodeByteSize     : 304 
         +0×008 LazyWriteThread  : (null) 
         +0×010 VcbQueue         : _LIST_ENTRY [ 0xfffffa80`047a8b68 - 0xfffffa80`047a8b68 ] 
         +0×020 DriverObject     : 0xfffffa80`05809e70 _DRIVER_OBJECT 
         +0×028 DiskFileSystemDeviceObject : 0xfffffa80`04842880 
         +0×030 CdromFileSystemDeviceObject : 0xfffffa80`05bf6890 
         +0×038 Resource         : _ERESOURCE 
         +0x0a0 OurProcess       : 0xfffffa80`036edb30 _KPROCESS 
         +0x0a8 NumberProcessors : 2 
         +0x0ac ChicagoMode      : 0y1 
         +0x0ac FujitsuFMR       : 0y0 
         +0x0ac AsyncCloseActive : 0y0 
         +0x0ac ShutdownStarted  : 0y0 
         +0x0ac CodePageInvariant : 0y1 
         +0x0ac HighAsync        : 0y0 
         +0x0ac HighDelayed      : 0y0 
         +0x0b0 AsyncCloseCount  : 0 
         +0x0b8 AsyncCloseList   : _LIST_ENTRY [ 0xfffff880`070e3578 - 0xfffff880`070e3578 ] 
         +0x0c8 DelayedCloseCount : 1 
         +0x0d0 DelayedCloseList : _LIST_ENTRY [ 0xfffff8a0`0b4315b0 - 0xfffff8a0`0b4315b0 ] 
         +0x0e0 FatCloseItem     : 0xfffffa80`057e8480 _IO_WORKITEM 
         +0x0e8 GeneralSpinLock  : 0 
         +0x0f0 CacheManagerCallbacks : _CACHE_MANAGER_CALLBACKS 
         +0×110 CacheManagerNoOpCallbacks : _CACHE_MANAGER_CALLBACKS

      0: kd> !list "-t nt!_LIST_ENTRY.Flink -e -x \"dd @$extret l4;  dt fastfat!_VCB @$extret-0×58\"0xfffffa80`047a8b68
      dd @$extret l4;  dt fastfat!_VCB @$extret-0×58 
      fffffa80`047a8b68  070e34d0 fffff880 070e34d0 fffff880 
         +0×000 VolumeFileHeader : _FSRTL_ADVANCED_FCB_HEADER 
         +0×058 VcbLinks         : _LIST_ENTRY [ 0xfffff880`070e34d0 - 0xfffff880`070e34d0 ] 
         +0×068 TargetDeviceObject : 0xfffffa80`053c7040 _DEVICE_OBJECT 
         +0×070 Vpb              : 0xfffffa80`047e8830 _VPB 
         +0×078 VcbState         : 0×101002 
         +0x07c VcbCondition     : 1 ( VcbGood ) 
         +0×080 RootDcb          : 0xfffffa80`0480b940 _FCB 
         +0×088 NumberOfWindows  : 2 
         +0×090 Windows          : 0xfffff8a0`0b4b8530 _FAT_WINDOW 
         +0×098 CurrentWindow    : 0xfffff8a0`0b4b853c _FAT_WINDOW 
         +0x0a0 DirectAccessOpenCount : 0 
         +0x0a4 ShareAccess      : _SHARE_ACCESS 
         +0x0c0 OpenFileCount    : 1 
         +0x0c4 ReadOnlyCount    : 1 
         +0x0c8 InternalOpenCount : 2 
         +0x0cc ResidualOpenCount : 2 
         +0x0d0 Bpb              : BIOS_PARAMETER_BLOCK 
         +0×100 First0x24BytesOfBootSector : (null) 
         +0×108 AllocationSupport : <unnamed-tag> 
         +0×128 DirtyFatMcb      : _LARGE_MCB 
         +0×148 BadBlockMcb      : _LARGE_MCB 
         +0×168 FreeClusterBitMap : _RTL_BITMAP 
         +0×178 FreeClusterBitMapMutex : _FAST_MUTEX 
         +0x1b0 Resource         : _ERESOURCE 
         +0×218 ChangeBitMapResource : _ERESOURCE 
         +0×280 VirtualVolumeFile : 0xfffffa80`0480aa10 _FILE_OBJECT 
         +0×288 SectionObjectPointers : _SECTION_OBJECT_POINTERS 
         +0x2a0 ClusterHint      : 2 
         +0x2a8 CurrentDevice    : 0xfffffa80`047e8cd0 _DEVICE_OBJECT 
         +0x2b0 VirtualEaFile    : (null) 
         +0x2b8 EaFcb            : (null) 
         +0x2c0 FileObjectWithVcbLocked : (null) 
         +0x2c8 DirNotifyList    : _LIST_ENTRY [ 0xfffffa80`047a8dd8 - 0xfffffa80`047a8dd8 ] 
         +0x2d8 NotifySync       : 0xfffffa80`056fec30 
         +0x2e0 DirectoryFileCreationMutex : _FAST_MUTEX 
         +0×318 VerifyThread     : (null) 
         +0×320 CleanVolumeDpc   : _KDPC 
         +0×360 CleanVolumeTimer : _KTIMER 
         +0x3a0 LastFatMarkVolumeDirtyCall : _LARGE_INTEGER 0×0 
         +0x3a8 Statistics       : 0xfffffa80`05b5ec80 _FILE_SYSTEM_STATISTICS 
         +0x3b0 Tunnel           : TUNNEL 
         +0×408 ChangeCount      : 0 
         +0×410 SwapVpb          : 0xfffffa80`05b5ec10 _VPB 
         +0×418 AsyncCloseList   : _LIST_ENTRY [ 0xfffffa80`047a8f28 - 0xfffffa80`047a8f28 ] 
         +0×428 DelayedCloseList : _LIST_ENTRY [ 0xfffff8a0`0b4315c0 - 0xfffff8a0`0b4315c0 ] 
         +0×438 AdvancedFcbHeaderMutex : _FAST_MUTEX 
         +0×470 CloseContext     : 0xfffff8a0`0b553a90 CLOSE_CONTEXT 
         +0×478 CloseContextCount : 1

      当前系统中只有一个FAT32卷,即我们刚插入的SD卡。下面打印出VPB的内容:

      0: kd> dt _VPB 0xfffffa80`047e8830 
      nt!_VPB 
         +0×000 Type             : 10 
         +0×002 Size             : 96 
         +0×004 Flags            : 1 
         +0×006 VolumeLabelLength : 0×10 
         +0×008 DeviceObject     : 0xfffffa80`047a8970 _DEVICE_OBJECT 
         +0×010 RealDevice       : 0xfffffa80`047e8cd0 _DEVICE_OBJECT 
         +0×018 SerialNumber     : 0x51e712c6 
         +0x01c ReferenceCount   : 2 
         +0×020 VolumeLabel      : [32]  "CANON_DC"

      Vpb->Flags显示卷是已挂载的,即标志位VBP_MOUNTED已被设置。从Vpb内容可知,此SD卡的卷标为"CANON_DC",实际上它就是CANON相机的存储卡。DeviceObject,即文件系统逻辑卷设备为0xfffffa80`047a8970;RealDevice,即存储栈上最底层的设备为0xfffffa80`047e8cd0

      0: kd> !devobj 0xfffffa80`047e8cd0 
      Device object (fffffa80047e8cd0) is for: 
      HarddiskVolume17 \Driver\volmgr DriverObject fffffa8004086e70 
      Current Irp 00000000 RefCount 2 Type 00000007 Flags 00003050 
      Vpb fffffa80047e8830 Dacl fffff9a109ae12b0 DevExt fffffa80047e8e20 DevObjExt fffffa80047e8f88 Dope fffffa800480c840 DevNode fffffa80047e8a00 
      ExtensionFlags (0000000000)  
      AttachedDevice (Upper) fffffa80047e5ac0 \Driver\fvevol 
      Device queue is not busy.

      0: kd> !devobj 0xfffffa80`047a8970 
      Device object (fffffa80047a8970) is for: 
        \FileSystem\fastfat DriverObject fffffa8005809e70 
      Current Irp 00000000 RefCount 0 Type 00000008 Flags 00000000 
      DevExt fffffa80047a8ac0 DevObjExt fffffa80047a8f90 
      ExtensionFlags (0×00000800)  
                                   Unknown flags 0×00000800

      0: kd> dt _DEVICE_OBJECT 0xfffffa80`047e8cd0 
      nt!_DEVICE_OBJECT 
         +0×000 Type             : 3 
         +0×002 Size             : 0x2b8 
         +0×004 ReferenceCount   : 2 
         +0×008 DriverObject     : 0xfffffa80`04086e70 _DRIVER_OBJECT 
         +0×010 NextDevice       : 0xfffffa80`045ed440 _DEVICE_OBJECT 
         +0×018 AttachedDevice   : 0xfffffa80`047e5ac0 _DEVICE_OBJECT 
         +0×020 CurrentIrp       : (null) 
         +0×028 Timer            : (null) 
         +0×030 Flags            : 0×3050 
         +0×034 Characteristics  : 1 
         +0×038 Vpb              : 0xfffffa80`047e8830 _VPB 
         +0×040 DeviceExtension  : 0xfffffa80`047e8e20 
         +0×048 DeviceType       : 7 
         +0x04c StackSize        : 9 ” 
         +0×050 Queue            : <unnamed-tag> 
         +0×098 AlignmentRequirement : 0 
         +0x0a0 DeviceQueue      : _KDEVICE_QUEUE 
         +0x0c8 Dpc              : _KDPC 
         +0×108 ActiveThreadCount : 0 
         +0×110 SecurityDescriptor : 0xfffff8a0`099c1210 
         +0×118 DeviceLock       : _KEVENT 
         +0×130 SectorSize       : 0×200 
         +0×132 Spare1           : 1 
         +0×138 DeviceObjectExtension : 0xfffffa80`047e8f88 _DEVOBJ_EXTENSION 
         +0×140 Reserved         : (null)

      0: kd> dt _DEVICE_OBJECT 0xfffffa80`047a8970 
      nt!_DEVICE_OBJECT 
         +0×000 Type             : 3 
         +0×002 Size             : 0×620 
         +0×004 ReferenceCount   : 0 
         +0×008 DriverObject     : 0xfffffa80`05809e70 _DRIVER_OBJECT 
         +0×010 NextDevice       : 0xfffffa80`05bf6890 _DEVICE_OBJECT 
         +0×018 AttachedDevice   : 0xfffffa80`0480ab50 _DEVICE_OBJECT 
         +0×020 CurrentIrp       : (null) 
         +0×028 Timer            : (null) 
         +0×030 Flags            : 0 
         +0×034 Characteristics  : 0 
         +0×038 Vpb              : (null) 
         +0×040 DeviceExtension  : 0xfffffa80`047a8ac0 
         +0×048 DeviceType       : 8 
         +0x04c StackSize        : 12 ” 
         +0×050 Queue            : <unnamed-tag> 
         +0×098 AlignmentRequirement : 0 
         +0x0a0 DeviceQueue      : _KDEVICE_QUEUE 
         +0x0c8 Dpc              : _KDPC 
         +0×108 ActiveThreadCount : 0 
         +0×110 SecurityDescriptor : (null) 
         +0×118 DeviceLock       : _KEVENT 
         +0×130 SectorSize       : 0×200 
         +0×132 Spare1           : 1 
         +0×138 DeviceObjectExtension : 0xfffffa80`047a8f90 _DEVOBJ_EXTENSION 
         +0×140 Reserved         : (null)

      Vcb->Vpb 与Vcb->Vbp->RealDevice->Vpb是一致的。下面再来研究一下存储设备栈的最上层的DeviceObject,即Vcb->TargetDeviceObject (0xfffffa80`053c7040):

      1: kd> !devobj 0xfffffa80`053c7040 
      Device object (fffffa80053c7040) is for: 
        \Driver\volsnap DriverObject fffffa80045c79b0 
      Current Irp 00000000 RefCount 0 Type 00000007 Flags 00002010 
      DevExt fffffa80053c7190 DevObjExt fffffa80053c7d38 
      ExtensionFlags (0×00000800)  
                                   Unknown flags 0×00000800 
      AttachedTo (Lower) fffffa80047e5ac0 \Driver\fvevol 
      Device queue is not busy. 
      1: kd> dt _DEVICE_OBJECT 0xfffffa80`053c7040 
      nt!_DEVICE_OBJECT 
         +0×000 Type             : 3 
         +0×002 Size             : 0xcf8 
         +0×004 ReferenceCount   : 0 
         +0×008 DriverObject     : 0xfffffa80`045c79b0 _DRIVER_OBJECT 
         +0×010 NextDevice       : 0xfffffa80`04649060 _DEVICE_OBJECT 
         +0×018 AttachedDevice   : (null) 
         +0×020 CurrentIrp       : (null) 
         +0×028 Timer            : (null) 
         +0×030 Flags            : 0×2010 
         +0×034 Characteristics  : 0×101 
         +0×038 Vpb              : (null) 
         +0×040 DeviceExtension  : 0xfffffa80`053c7190 
         +0×048 DeviceType       : 7 
         +0x04c StackSize        : 11 ” 
         +0×050 Queue            : <unnamed-tag> 
         +0×098 AlignmentRequirement : 0 
         +0x0a0 DeviceQueue      : _KDEVICE_QUEUE 
         +0x0c8 Dpc              : _KDPC 
         +0×108 ActiveThreadCount : 0 
         +0×110 SecurityDescriptor : (null) 
         +0×118 DeviceLock       : _KEVENT 
         +0×130 SectorSize       : 0×200 
         +0×132 Spare1           : 0 
         +0×138 DeviceObjectExtension : 0xfffffa80`053c7d38 _DEVOBJ_EXTENSION 
         +0×140 Reserved         : (null)

      有没有觉察出有什么异常状况?TargetDeviceObject的DeviceType为FILE_DEVICE_DISK (0×07),但它的Vpb项却是空的。根据上面对IoCreateDevice的分析,对所有4种设备类型,在创建设备时都会分配VPB,难道是被释放了?

      经过研究才发现其中另有蹊跷。TargetDeviceObject是由VolSnap创建的,VolSnap是Windows Volume Shadow Copy Service的核心组件,VolSnap为每个卷设备都创建Fdo设备并将其加入设备栈中。但蹊跷的地方是,在创建设备对象时,VolSnap使用的DeviceType却是FILE_DEVICE_UNKNOWN (0×22),并在将设备加入存储设备栈后,再将TargetDeviceObject的DeviceType从FILE_DEVICE_UNKOWN改为FILE_DEVICE_DISK的,所以TargetDeviceObject的Vpb项为空。

      有兴趣地话可以反汇编一下volsnap!VolSnapAddDevice的代码。

posted @ 2013-12-11 11:23  kkindof  阅读(2472)  评论(0)    收藏  举报