VPB的来龙去脉(一)

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 (0x07)
    • FILE_DEVICE_TAPE (0x1f)
    • FILE_DEVICE_CD_ROM (0x02)
    • FILE_DEVICE_VIRTUAL_DISK (0x24)

    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
       +0x000 NodeTypeCode     : 0x500
       +0x002 NodeByteSize     : 304
       +0x008 LazyWriteThread  : (null)
       +0x010 VcbQueue         : _LIST_ENTRY [ 0xfffffa80`047a8b68 - 0xfffffa80`047a8b68 ]
       +0x020 DriverObject     : 0xfffffa80`05809e70 _DRIVER_OBJECT
       +0x028 DiskFileSystemDeviceObject : 0xfffffa80`04842880
       +0x030 CdromFileSystemDeviceObject : 0xfffffa80`05bf6890
       +0x038 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
       +0x110 CacheManagerNoOpCallbacks : _CACHE_MANAGER_CALLBACKS

    0: kd> !list "-t nt!_LIST_ENTRY.Flink -e -x \"dd @$extret l4;  dt fastfat!_VCB @$extret-0x58\" 0xfffffa80`047a8b68"
    dd @$extret l4;  dt fastfat!_VCB @$extret-0x58
    fffffa80`047a8b68  070e34d0 fffff880 070e34d0 fffff880
       +0x000 VolumeFileHeader : _FSRTL_ADVANCED_FCB_HEADER
       +0x058 VcbLinks         : _LIST_ENTRY [ 0xfffff880`070e34d0 - 0xfffff880`070e34d0 ]
       +0x068 TargetDeviceObject : 0xfffffa80`053c7040 _DEVICE_OBJECT
       +0x070 Vpb              : 0xfffffa80`047e8830 _VPB
       +0x078 VcbState         : 0x101002
       +0x07c VcbCondition     : 1 ( VcbGood )
       +0x080 RootDcb          : 0xfffffa80`0480b940 _FCB
       +0x088 NumberOfWindows  : 2
       +0x090 Windows          : 0xfffff8a0`0b4b8530 _FAT_WINDOW
       +0x098 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
       +0x100 First0x24BytesOfBootSector : (null)
       +0x108 AllocationSupport : <unnamed-tag>
       +0x128 DirtyFatMcb      : _LARGE_MCB
       +0x148 BadBlockMcb      : _LARGE_MCB
       +0x168 FreeClusterBitMap : _RTL_BITMAP
       +0x178 FreeClusterBitMapMutex : _FAST_MUTEX
       +0x1b0 Resource         : _ERESOURCE
       +0x218 ChangeBitMapResource : _ERESOURCE
       +0x280 VirtualVolumeFile : 0xfffffa80`0480aa10 _FILE_OBJECT
       +0x288 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
       +0x318 VerifyThread     : (null)
       +0x320 CleanVolumeDpc   : _KDPC
       +0x360 CleanVolumeTimer : _KTIMER
       +0x3a0 LastFatMarkVolumeDirtyCall : _LARGE_INTEGER 0x0
       +0x3a8 Statistics       : 0xfffffa80`05b5ec80 _FILE_SYSTEM_STATISTICS
       +0x3b0 Tunnel           : TUNNEL
       +0x408 ChangeCount      : 0
       +0x410 SwapVpb          : 0xfffffa80`05b5ec10 _VPB
       +0x418 AsyncCloseList   : _LIST_ENTRY [ 0xfffffa80`047a8f28 - 0xfffffa80`047a8f28 ]
       +0x428 DelayedCloseList : _LIST_ENTRY [ 0xfffff8a0`0b4315c0 - 0xfffff8a0`0b4315c0 ]
       +0x438 AdvancedFcbHeaderMutex : _FAST_MUTEX
       +0x470 CloseContext     : 0xfffff8a0`0b553a90 CLOSE_CONTEXT
       +0x478 CloseContextCount : 1

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

    0: kd> dt _VPB 0xfffffa80`047e8830
    nt!_VPB
       +0x000 Type             : 10
       +0x002 Size             : 96
       +0x004 Flags            : 1
       +0x006 VolumeLabelLength : 0x10
       +0x008 DeviceObject     : 0xfffffa80`047a8970 _DEVICE_OBJECT
       +0x010 RealDevice       : 0xfffffa80`047e8cd0 _DEVICE_OBJECT
       +0x018 SerialNumber     : 0x51e712c6
       +0x01c ReferenceCount   : 2
       +0x020 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 (0x00000800) 
                                 Unknown flags 0x00000800

    0: kd> dt _DEVICE_OBJECT 0xfffffa80`047e8cd0
    nt!_DEVICE_OBJECT
       +0x000 Type             : 3
       +0x002 Size             : 0x2b8
       +0x004 ReferenceCount   : 2
       +0x008 DriverObject     : 0xfffffa80`04086e70 _DRIVER_OBJECT
       +0x010 NextDevice       : 0xfffffa80`045ed440 _DEVICE_OBJECT
       +0x018 AttachedDevice   : 0xfffffa80`047e5ac0 _DEVICE_OBJECT
       +0x020 CurrentIrp       : (null)
       +0x028 Timer            : (null)
       +0x030 Flags            : 0x3050
       +0x034 Characteristics  : 1
       +0x038 Vpb              : 0xfffffa80`047e8830 _VPB
       +0x040 DeviceExtension  : 0xfffffa80`047e8e20
       +0x048 DeviceType       : 7
       +0x04c StackSize        : 9 ''
       +0x050 Queue            : <unnamed-tag>
       +0x098 AlignmentRequirement : 0
       +0x0a0 DeviceQueue      : _KDEVICE_QUEUE
       +0x0c8 Dpc              : _KDPC
       +0x108 ActiveThreadCount : 0
       +0x110 SecurityDescriptor : 0xfffff8a0`099c1210
       +0x118 DeviceLock       : _KEVENT
       +0x130 SectorSize       : 0x200
       +0x132 Spare1           : 1
       +0x138 DeviceObjectExtension : 0xfffffa80`047e8f88 _DEVOBJ_EXTENSION
       +0x140 Reserved         : (null)

    0: kd> dt _DEVICE_OBJECT 0xfffffa80`047a8970
    nt!_DEVICE_OBJECT
       +0x000 Type             : 3
       +0x002 Size             : 0x620
       +0x004 ReferenceCount   : 0
       +0x008 DriverObject     : 0xfffffa80`05809e70 _DRIVER_OBJECT
       +0x010 NextDevice       : 0xfffffa80`05bf6890 _DEVICE_OBJECT
       +0x018 AttachedDevice   : 0xfffffa80`0480ab50 _DEVICE_OBJECT
       +0x020 CurrentIrp       : (null)
       +0x028 Timer            : (null)
       +0x030 Flags            : 0
       +0x034 Characteristics  : 0
       +0x038 Vpb              : (null)
       +0x040 DeviceExtension  : 0xfffffa80`047a8ac0
       +0x048 DeviceType       : 8
       +0x04c StackSize        : 12 ''
       +0x050 Queue            : <unnamed-tag>
       +0x098 AlignmentRequirement : 0
       +0x0a0 DeviceQueue      : _KDEVICE_QUEUE
       +0x0c8 Dpc              : _KDPC
       +0x108 ActiveThreadCount : 0
       +0x110 SecurityDescriptor : (null)
       +0x118 DeviceLock       : _KEVENT
       +0x130 SectorSize       : 0x200
       +0x132 Spare1           : 1
       +0x138 DeviceObjectExtension : 0xfffffa80`047a8f90 _DEVOBJ_EXTENSION
       +0x140 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 (0x00000800) 
                                 Unknown flags 0x00000800
    AttachedTo (Lower) fffffa80047e5ac0 \Driver\fvevol
    Device queue is not busy.
    1: kd> dt _DEVICE_OBJECT 0xfffffa80`053c7040
    nt!_DEVICE_OBJECT
       +0x000 Type             : 3
       +0x002 Size             : 0xcf8
       +0x004 ReferenceCount   : 0
       +0x008 DriverObject     : 0xfffffa80`045c79b0 _DRIVER_OBJECT
       +0x010 NextDevice       : 0xfffffa80`04649060 _DEVICE_OBJECT
       +0x018 AttachedDevice   : (null)
       +0x020 CurrentIrp       : (null)
       +0x028 Timer            : (null)
       +0x030 Flags            : 0x2010
       +0x034 Characteristics  : 0x101
       +0x038 Vpb              : (null)
       +0x040 DeviceExtension  : 0xfffffa80`053c7190
       +0x048 DeviceType       : 7
       +0x04c StackSize        : 11 ''
       +0x050 Queue            : <unnamed-tag>
       +0x098 AlignmentRequirement : 0
       +0x0a0 DeviceQueue      : _KDEVICE_QUEUE
       +0x0c8 Dpc              : _KDPC
       +0x108 ActiveThreadCount : 0
       +0x110 SecurityDescriptor : (null)
       +0x118 DeviceLock       : _KEVENT
       +0x130 SectorSize       : 0x200
       +0x132 Spare1           : 0
       +0x138 DeviceObjectExtension : 0xfffffa80`053c7d38 _DEVOBJ_EXTENSION
       +0x140 Reserved         : (null)

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

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

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

    (to be continued)

  • 3 条评论

    1. Alright gamers, 7f777gameapk is where it’s at for mobile fun. Downloaded the app and have been hooked since. Easy to use and the games are pretty dope. Check out 7f777gameapk if you’re bored, you won’t regret it.

    2. Yo, I’ve been messing around on 7h77game and it’s got some cool stuff. Def a good place to kill some time. Worth a look, especially if you’re into trying new games. See for yourself with 7h77game.

    发表回复

    您的邮箱地址不会被公开。 必填项已用 * 标注