服务热线:010-65014696

安全研究

椒图关注 FOCUS ON JOWTO

Windows 存储设备栈分析

[2015-1-19]

通常为了实现某些与存储防护相关的功能,需要得到诸如volume/partition/class/port/miniport等设备,在客户端由于环境相对固定,所以可以假定驱动名为volmgr.sys(ftdisk.sys)/partmgr.sys/disk.sys/atapi.sys或scsiport.sys,但由于RAID、MPIO等应用和第三方厂商的介入,这种假设并不适用于Windows服务器,如symmpi.sys或是 Windows 2003 Server 后基于storport.sys 的 lsi_sas.sys 等。为了在Windows 服务器上保持良好的兼容性,就需要找到一种不依赖“驱动名称”和“设备名称”,可动态识别和定位的方法,同时要能够定位在“存储栈”中任意位置的存储设备,也就是需要从文件系统开始,依次定位“卷设备->分区设备->类驱动设备->小端口设备”,这需要跨越多个存储设备栈,我们的方法是跟踪DEVICE_OBJECT中StackSize的变化,这个值得变化表明一个IRP在存储设备栈中的走向和整体流程,此值为1表明IRP消亡,即再往下层设备传递时,不再以IRP形式发送。


 


这里以Windows 2008 R2为测试环境进行调试。通过DeviceTree观察,发现从NTFS文件系统开始其DeviceObject->StackSize为8 (如果上面还有Upper Filter Driver 此值还会更大),再往下查看发现除了设备的AttachedDevice域可以确认有上层设备(Upper Filter Driver)之外无法查看到其低层设备(Lower Device)同时也无法完成全部的跟踪,中间的StackSize总有不连续的地方。也就是说DeviceTree提供的信息不够详尽,无法以此写出符合要求的代码。


 


在这种情况下我尝试翻阅WDK,看看能否找到些线索,终于让我找到了一个内核API: IoGetDiskDeviceObject(),从说明中可以看到此函数返回的磁盘设备对象是一个“卷设备”,那么它是如何通过文件系统设备对象获得的?这也正是DeviceTree没有给出的信息。


 


通过WINDBG对此函数进行逆向分析:


 


kd> uf IoGetDiskDeviceObject


nt!IoGetDiskDeviceObject:


fffff800`0178c810 48895c2410      mov     qword ptr [rsp+10h],rbx


fffff800`0178c815 4889742418      mov     qword ptr [rsp+18h],rsi


fffff800`0178c81a 57              push    rdi


fffff800`0178c81b 4883ec20        sub     rsp,20h


fffff800`0178c81f 33db            xor     ebx,ebx


fffff800`0178c821 488bf2          mov     rsi,rdx


fffff800`0178c824 488bf9          mov     rdi,rcx    –>这里 rcx 指向文件系统的设备对象,rdi = DeviceObject


fffff800`0178c827 48395938        cmp     qword ptr [rcx+38h],rbx  –> 判断 VPB 是否有效


fffff800`0178c82b 7407            je      nt!IoGetDiskDeviceObject+0x24 (fffff800`0178c834)  –>如果与DEVICE_OBJECT->VPB中的值不相等,则有误,通常为NULL。


 


nt!IoGetDiskDeviceObject+0x1d:


fffff800`0178c82d b80d0000c0      mov     eax,0C000000Dh  –>返回错误,直接退出


fffff800`0178c832 eb4a            jmp     nt!IoGetDiskDeviceObject+0x6e (fffff800`0178c87e)


 


nt!IoGetDiskDeviceObject+0x24:


fffff800`0178c834 488d4c2430      lea     rcx,[rsp+30h]


fffff800`0178c839 e8f2c8edff      call    nt!IoAcquireVpbSpinLock (fffff800`01669130)


fffff800`0178c83e 4c8b9f38010000  mov     r11,qword ptr [rdi+138h] –>获得“设备对象扩展” DEVOBJ_EXTENSION 注意:不是“设备扩展”


fffff800`0178c845 498b4b48        mov     rcx,qword ptr [r11+48h]  –>现在的 rcx 指向 “设备对象扩展”中的VPB(DEVOBJ_EXTENSION->VPB)注意:与“设备对象”中的(DEVICE_OBJECT->VPB)不是一个


fffff800`0178c849 483bcb          cmp     rcx,rbx  –>判断“设备对象扩展”中VPB是否为NULL


fffff800`0178c84c 7507            jne     nt!IoGetDiskDeviceObject+0x45 (fffff800`0178c855)  –>VPB有效则跳转继续判断


 


nt!IoGetDiskDeviceObject+0x3e:


fffff800`0178c84e bb0d0000c0      mov     ebx,0C000000Dh  –>返回错误,直接退出


fffff800`0178c853 eb1e            jmp     nt!IoGetDiskDeviceObject+0x63 (fffff800`0178c873)


 


nt!IoGetDiskDeviceObject+0x45:


fffff800`0178c855 39591c          cmp     dword ptr [rcx+1Ch],ebx  –> 判断 VPB 的引用计数


fffff800`0178c858 7507            jne     nt!IoGetDiskDeviceObject+0x51 (fffff800`0178c861)


 


nt!IoGetDiskDeviceObject+0x4a:


fffff800`0178c85a bb6e0200c0      mov     ebx,0C000026Eh  –>返回错误,直接退出


fffff800`0178c85f eb12            jmp     nt!IoGetDiskDeviceObject+0x63 (fffff800`0178c873)


 


nt!IoGetDiskDeviceObject+0x51:


fffff800`0178c861 f6410401        test    byte ptr [rcx+4],1  –> 判断 VPB 标志


fffff800`0178c865 74f3            je      nt!IoGetDiskDeviceObject+0x4a (fffff800`0178c85a)


 


nt!IoGetDiskDeviceObject+0x57:


fffff800`0178c867 488b4910        mov     rcx,qword ptr [rcx+10h]  –> 从 VPB 取 RealDevice 成员,即“卷设备对象”


fffff800`0178c86b 48890e          mov     qword ptr [rsi],rcx


fffff800`0178c86e e87db6f4ff      call    nt!ObfReferenceObject (fffff800`016d7ef0)  –>这里会增加“卷设备对象”的引用计数


 


nt!IoGetDiskDeviceObject+0x63:


fffff800`0178c873 8a4c2430        mov     cl,byte ptr [rsp+30h]


fffff800`0178c877 e8e4c8edff      call    nt!IoReleaseVpbSpinLock (fffff800`01669160)


fffff800`0178c87c 8bc3            mov     eax,ebx


 


nt!IoGetDiskDeviceObject+0x6e:


fffff800`0178c87e 488b5c2438      mov     rbx,qword ptr [rsp+38h]


fffff800`0178c883 488b742440      mov     rsi,qword ptr [rsp+40h]


fffff800`0178c888 4883c420        add     rsp,20h


fffff800`0178c88c 5f              pop     rdi


fffff800`0178c88d c3              ret


 


从上面的逆向分析可以印证 IoGetDiskDeviceObject() 内核API返回的是“卷设备”的设备对象,即 volmgr.sys 的设备对象(xp/2003上是ftdisk.sys),实现方法是通过 DEVICE_OBJECT 中所指向的 DEVOBJ_EXTENSION (注意:“设备对象扩展”不是“设备扩展”,此结构式是“未公开”结构,但可以通过WINDBG命令来获得)中的VPB->RealDevice 来取得的,对于文件系统而言,它既是下层的“卷设备”也是文件系统IRP要发往的设备(这点可以通过观察DeviceObject->StackSize 来确认,通常在volmgr.sys 的 DEVICE_OBJECT->AttachedDevice上会有一个 UPPER FILTER 叫 volsnap.sys,所以通过WINDBG 的 dt 和 !devobj、!devstack等命令直接查看从文件系统到“卷设备”的StackSize变化时是 7->8->5->6,因当IRP发送到ntfs.sys / volmgr.sys时发现其AttachedDevice不为空,会先发送到FltMgr.sys / volsnap.sys创建的设备对象上)。


 


DEVOBJ_EXTENSION结构如下:


 


kd> dt _DEVOBJ_EXTENSION


nt!_DEVOBJ_EXTENSION


+0x000 Type             : Int2B


+0x002 Size             : Uint2B


+0x008 DeviceObject     : Ptr64 _DEVICE_OBJECT


+0x010 PowerFlags       : Uint4B


+0x018 Dope             : Ptr64 _DEVICE_OBJECT_POWER_EXTENSION


+0x020 ExtensionFlags   : Uint4B


+0x028 DeviceNode       : Ptr64 Void


+0x030 AttachedTo       : Ptr64 _DEVICE_OBJECT


+0x038 StartIoCount     : Int4B


+0x03c StartIoKey       : Int4B


+0x040 StartIoFlags     : Uint4B


+0x048 Vpb              : Ptr64 _VPB


+0x050 DependentList    : _LIST_ENTRY


+0x060 ProviderList     : _LIST_ENTRY


 


VPB 结构如下:


 


kd> dt _VPB


nt!_VPB


+0x000 Type             : Int2B


+0x002 Size             : Int2B


+0x004 Flags            : Uint2B


+0x006 VolumeLabelLength : Uint2B


+0x008 DeviceObject     : Ptr64 _DEVICE_OBJECT


+0x010 RealDevice       : Ptr64 _DEVICE_OBJECT


+0x018 SerialNumber     : Uint4B


+0x01c ReferenceCount   : Uint4B


+0x020 VolumeLabel      : [32] Wchar


 


至此,我们知道如何从“文件系统”定位到“卷设备”,那么接下来的问题是IRP如何从“卷设备”走下去的?这个问题同样无法使用DeviceTree查看到细节,通过翻阅WDK文档,找到了IoGetLowerDeviceObject / IoGetDeviceAttachmentBaseRef这两个内核API。从文档描述看来,这两个API整体功能上的实现方法是一样的,都是可以返回低层设备(Lower Device)。下面以IoGetLowerDeviceObject为例,了解下具体的实现方式。


 


 


kd> uf IoGetLowerDeviceObject


nt! ?? ::FNODOBFM::`string’+0x1a030:


fffff800`0165e1c4 f641200e        test    byte ptr [rcx+20h],0Eh  –>再次判断 ExtensionFlags 扩展标志,以确定当前设备的状态


 


fffff800`0165e1c8 0f85b95a0100    jne     nt!IoGetLowerDeviceObject+0x37 (fffff800`01673c87)  –>如状态异常则直接返回


 


nt! ?? ::FNODOBFM::`string’+0x1a03a:


fffff800`0165e1ce 48395930        cmp     qword ptr [rcx+30h],rbx  –>判断“低层设备对象”即:DEVOBJ_EXTENSION->AttachedTo 是否与当前传入的“设备对象”相等


fffff800`0165e1d2 0f84af5a0100    je      nt!IoGetLowerDeviceObject+0x37  (fffff800`01673c87)  –>如相等则无需获取,直接返回


 


nt! ?? ::FNODOBFM::`string’+0x1a044:


fffff800`0165e1d8 e9a05a0100      jmp     nt!IoGetLowerDeviceObject+0x2d (fffff800`01673c7d)  –>如不相等则说明存在“低层设备对象”,跳转继续判断


 


nt! ?? ::FNODOBFM::`string’+0x1a049:


fffff800`0165e1dd 488b5930        mov     rbx,qword ptr [rcx+30h]  — >获取“低层设备对象”,即:rbx = DEVOBJ_EXTENSION->AttahcedTo指向的“设备对象”


fffff800`0165e1e1 488bcb          mov     rcx,rbx


fffff800`0165e1e4 e8074d0800      call    nt!ObfReferenceObject (fffff800`016e2ef0)  –>为返回的“低层设备对象”增加引用计数


fffff800`0165e1e9 90              nop


fffff800`0165e1ea e9985a0100      jmp     nt!IoGetLowerDeviceObject+0x37 (fffff800`01673c87)  –>


 


nt!IoGetLowerDeviceObject:  –>函数入口


fffff800`01673c50 48895c2408      mov     qword ptr [rsp+8],rbx


fffff800`01673c55 57              push    rdi


fffff800`01673c56 4883ec20        sub     rsp,20h


fffff800`01673c5a 488bd9          mov     rbx,rcx  –>这里的rbx是 DEVICE_OBJECT


fffff800`01673c5d b90a000000      mov     ecx,0Ah


fffff800`01673c62 e849f00600      call    nt!KeAcquireQueuedSpinLock (fffff800`016e2cb0)


fffff800`01673c67 488b8b38010000  mov     rcx,qword ptr [rbx+138h]  –>得到“设备对象扩展”即:DEVOBJ_EXTENSION


fffff800`01673c6e 33db            xor     ebx,ebx


fffff800`01673c70 f641200f        test    byte ptr [rcx+20h],0Fh  –>判断 ExtensionFlags 扩展标志,以确定当前设备的状态,如:删除、卸载等


fffff800`01673c74 408af8          mov     dil,al


fffff800`01673c77 0f8547a5feff    jne     nt! ?? ::FNODOBFM::`string’+0x1a030 (fffff800`0165e1c4)  –>还需向上跳转,进一步判断状态


 


nt!IoGetLowerDeviceObject+0x2d:  –>状态正常从这里继续执行


fffff800`01673c7d 48395930        cmp     qword ptr [rcx+30h],rbx  –>判断“低层设备对象”即:DEVOBJ_EXTENSION->AttachedTo 是否与当前传入的“设备对象”相等


fffff800`01673c81 0f8556a5feff    jne     nt! ?? ::FNODOBFM::`string’+0x1a049 (fffff800`0165e1dd)  –>不相等,则跳转获取“低层设备对象”


 


nt!IoGetLowerDeviceObject+0x37:


fffff800`01673c87 408ad7          mov     dl,dil


fffff800`01673c8a b90a000000      mov     ecx,0Ah


fffff800`01673c8f e84c9a0800      call    nt!KeReleaseQueuedSpinLock (fffff800`016fd6e0)


fffff800`01673c94 488bc3          mov     rax,rbx  –>返回“低层设备对象”


fffff800`01673c97 488b5c2430      mov     rbx,qword ptr [rsp+30h]


fffff800`01673c9c 4883c420        add     rsp,20h


fffff800`01673ca0 5f              pop     rdi


fffff800`01673ca1 c3              ret


 


 


通过逆向分析得知“低层设备对象”是通过DEVICE_OBJECT->DEVOBJ_EXTENSION->AttachedTo 得到的,那么按理说“卷设备”的IRP走向也应该顺这个路径一直走下去,直到其消亡为止。那么到底是不是这样?这里先不急于论证,以此为一个段落,先验证之前的思路,通过WINDBG跟踪文件系统->“卷设备”的IRP流程:


 


首先查看 NTFS 文件驱动创建的设备:


 


kd> !drvobj ntfs


Driver object (fffffa800d1897c0) is for:


\FileSystem\Ntfs


Driver Extension List: (id , addr)


 


Device Object list:


fffffa800d7ab030  fffffa800d18a790  –>以第一个设备对象 fffffa800d7ab030 为基础跟踪


查看“文件系统设备栈”情况


 


kd> !devstack fffffa800d7ab030


!DevObj   !DrvObj            !DevExt   ObjectName


fffffa800d2ba300  \FileSystem\FltMgr fffffa800d2ba450  –>发现有一个UPPER FILTER驱动


> fffffa800d7ab030  \FileSystem\Ntfs   fffffa800d7ab180


 


查看这个UPPER FILTER设备对象详细信息(这个UPPER FILTER是W2K后新引进的框架,即minifilter所使用的框架)


 


kd> dt _DEVICE_OBJECT fffffa800d2ba300


ntdll!_DEVICE_OBJECT


+0x000 Type             : 0n3


+0x002 Size             : 0x1a8


+0x004 ReferenceCount   : 0n0


+0x008 DriverObject     : 0xfffffa80`0d1837c0 _DRIVER_OBJECT


+0x010 NextDevice       : 0xfffffa80`0d29e300 _DEVICE_OBJECT


+0x018 AttachedDevice   : (null)   –>已经没有附加设备,即不存在UPPER FILTER


+0x020 CurrentIrp       : (null)


+0x028 Timer            : (null)


+0x030 Flags            : 0x40000


+0x034 Characteristics  : 0


+0x038 Vpb              : (null)


+0x040 DeviceExtension  : 0xfffffa80`0d2ba450 Void


+0x048 DeviceType       : 8


+0x04c StackSize        : 8 ”  –>注意这个值,设备栈最顶层的设备


+0x050 Queue            :


+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`0d2ba4a8 _DEVOBJ_EXTENSION  –>“设备对象扩展”


+0x140 Reserved         : (null)


 


继续跟踪“设备对象扩展”内容


 


kd> dt _DEVOBJ_EXTENSION 0xfffffa80`0d2ba4a8


ntdll!_DEVOBJ_EXTENSION


+0x000 Type             : 0n13


+0x002 Size             : 0


+0x008 DeviceObject     : 0xfffffa80`0d2ba300 _DEVICE_OBJECT


+0x010 PowerFlags       : 0


+0x018 Dope             : (null)


+0x020 ExtensionFlags   : 0x800


+0x028 DeviceNode       : (null)


+0x030 AttachedTo       : 0xfffffa80`0d7ab030 _DEVICE_OBJECT  –>正好是Ntfs 驱动所创建的设备


+0x038 StartIoCount     : 0n0


+0x03c StartIoKey       : 0n0


+0x040 StartIoFlags     : 0


+0x048 Vpb              : (null)   –>因为还是文件系统设备栈,所以VPB为空是正确的


+0x050 DependentList    : _LIST_ENTRY [ 0xfffffa80`0d2ba4f8 – 0xfffffa80`0d2ba4f8 ]


+0x060 ProviderList     : _LIST_ENTRY [ 0xfffffa80`0d2ba508 – 0xfffffa80`0d2ba508 ]


 


验证“设备对象扩展”DEVOBJ_EXTENSION->AttachedTo 中“低层设备对象”的值


 


kd> dt _DEVICE_OBJECT 0xfffffa80`0d7ab030


ntdll!_DEVICE_OBJECT


+0x000 Type             : 0n3


+0x002 Size             : 0x1aa0


+0x004 ReferenceCount   : 0n1


+0x008 DriverObject     : 0xfffffa80`0d1897c0 _DRIVER_OBJECT


+0x010 NextDevice       : 0xfffffa80`0d18a790 _DEVICE_OBJECT


+0x018 AttachedDevice   : 0xfffffa80`0d2ba300 _DEVICE_OBJECT  –>正是刚才FltMgr驱动创建的设备


+0x020 CurrentIrp       : (null)


+0x028 Timer            : (null)


+0x030 Flags            : 0x40000


+0x034 Characteristics  : 0


+0x038 Vpb              : (null)


+0x040 DeviceExtension  : 0xfffffa80`0d7ab180 Void


+0x048 DeviceType       : 8


+0x04c StackSize        : 7 ”  –>注意这个值得变化,从8到7,标明了IRP的流向


+0x050 Queue            :


+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`0d7acad0 _DEVOBJ_EXTENSION


+0x140 Reserved         : (null)


 


到现在来看,符合刚才对IoGetLowerDeviceObject内核API的分析,链接UPPER FILTER与LOWER FILTER的分别是“设备对象”中的 AttachedDevice 与 “设备对象扩展”中的 AttahcedTo;接下来继续验证从文件系统到“卷设备”的跨“设备栈”的情况。


 


查看“设备对象扩展”情况


 


kd> dt _DEVOBJ_EXTENSION 0xfffffa80`0d7acad0


ntdll!_DEVOBJ_EXTENSION


+0x000 Type             : 0n13


+0x002 Size             : 0


+0x008 DeviceObject     : 0xfffffa80`0d7ab030 _DEVICE_OBJECT


+0x010 PowerFlags       : 0


+0x018 Dope             : (null)


+0x020 ExtensionFlags   : 0x800


+0x028 DeviceNode       : (null)


+0x030 AttachedTo       : (null)   –>为NULL,表示与“卷设备”的链接并不是以LOWER DEVICE方式进行的


+0x038 StartIoCount     : 0n0


+0x03c StartIoKey       : 0n0


+0x040 StartIoFlags     : 0


+0x048 Vpb              : 0xfffffa80`0cf91410 _VPB  –>与“卷设备”关联结构


+0x050 DependentList    : _LIST_ENTRY [ 0xfffffa80`0d7acb20 – 0xfffffa80`0d7acb20 ]


+0x060 ProviderList     : _LIST_ENTRY [ 0xfffffa80`0d7acb30 – 0xfffffa80`0d7acb30 ]


 


进一步观察 VPB 结构


 


kd> dt _VPB 0xfffffa80`0cf91410


ntdll!_VPB


+0x000 Type             : 0n10


+0x002 Size             : 0n96


+0x004 Flags            : 1


+0x006 VolumeLabelLength : 0x1c


+0x008 DeviceObject     : 0xfffffa80`0d7ab030 _DEVICE_OBJECT  –>看这个地址,很明显是刚才的文件系统的设备对象


+0x010 RealDevice       : 0xfffffa80`0d3494c0 _DEVICE_OBJECT  –>这个才是真正的“卷设备对象”


+0x018 SerialNumber     : 0x9ce133fc  –>“卷设备”序列号,即:逻辑磁盘序列号


+0x01c ReferenceCount   : 0x924


+0x020 VolumeLabel      : [32]  “SYSTEM_2008_R2″  –>“卷标”


 


查看ReadlDevice设备信息,此“卷设备”是由 volmgr.sys 驱动创建的,可以看到熟悉的 HarddiskVolumeX。


 


kd> !devobj 0xfffffa80`0d3494c0


Device object (fffffa800d3494c0) is for:


HarddiskVolume1 \Driver\volmgr DriverObject fffffa800cf1b1c0


Current Irp 00000000 RefCount 2340 Type 00000007 Flags 00201150


Vpb fffffa800cf91410 Dacl fffff9a1002a65d0 DevExt fffffa800d349610 DevObjExt fffffa800d349778 Dope fffffa800cf913a0 DevNode fffffa800d2ac2b0


ExtensionFlags (0x00000800)


 


查看“卷设备栈”情况


 


kd> !devstack 0xfffffa80`0d3494c0


!DevObj   !DrvObj            !DevExt   ObjectName


fffffa800d6fe340  \Driver\volsnap    fffffa800d6fe490


> fffffa800d3494c0  \Driver\volmgr     fffffa800d349610  HarddiskVolume1


!DevNode fffffa800d2ac2b0 :


DeviceInst is “STORAGE\Volume\{ec6b27cd-2225-11e2-92be-806e6f6e6963}#0000000000100000″


ServiceName is “volsnap”


 


通过上面的分析,可以得到与IoGetDiskDeviceObject 函数一致的结论,文件系统与“卷设备”不是以常规的LOWER DEVICE (DEVOBJ_EXTENSION->AttachedTo)方式进行关联的,而是通过“设备对象扩展”中的“卷参数块”(DEVOBJ_EXTENSION->VPB->RealDevice)完成连接的。这也说明如果想从文件系统定位到“卷设备”,使用IoGetLowerDeviceObject或IoGetDeviceAttachmentBaseRef 是行不通的,这也就难怪Windows为何要单独提供一个内核API来完成。到现在所有工作都还算顺利,按照之前的逆向分析,通过WINDBG完成了IRP从文件系统到“卷设备”的跨“设备栈”跟踪,但也仅是完成了一个“设备栈”的跨越,下面继续跟踪IRP在“卷设备”的走向:


 


查看“卷设备”的详细信息


 


kd> dt _DEVICE_OBJECT 0xfffffa80`0d3494c0


ntdll!_DEVICE_OBJECT


+0x000 Type             : 0n3


+0x002 Size             : 0x2b8


+0x004 ReferenceCount   : 0n2340


+0x008 DriverObject     : 0xfffffa80`0cf1b1c0 _DRIVER_OBJECT


+0x010 NextDevice       : 0xfffffa80`0d216ce0 _DEVICE_OBJECT


+0x018 AttachedDevice   : 0xfffffa80`0d6fe340 _DEVICE_OBJECT  –>这个UPPER FILTER是由volsnap.sys 驱动创建的,IRP到这里会先检测UPPER FILTER由这个设备先进行处理


+0x020 CurrentIrp       : (null)


+0x028 Timer            : (null)


+0x030 Flags            : 0x201150


+0x034 Characteristics  : 0


+0x038 Vpb              : 0xfffffa80`0cf91410 _VPB  –>在“卷设备”中,它的VPB与文件系统的DEVICE_OBJECT->DEVOBJ_EXTENSION->VPB是同一个,没错!他们共用一个VPB,这样既节约资源,使用起来也方便


+0x040 DeviceExtension  : 0xfffffa80`0d349610 Void


+0x048 DeviceType       : 7


+0x04c StackSize        : 5 ”  –>还记得上面提到这个值得变化么?也就是说由volsnap.sys创建的设备其 StackSize 为6


+0x050 Queue            :


+0x098 AlignmentRequirement : 0


+0x0a0 DeviceQueue      : _KDEVICE_QUEUE


+0x0c8 Dpc              : _KDPC


+0x108 ActiveThreadCount : 0


+0x110 SecurityDescriptor : 0xfffff8a0`00186530 Void


+0x118 DeviceLock       : _KEVENT


+0x130 SectorSize       : 0x200


+0x132 Spare1           : 1


+0x138 DeviceObjectExtension : 0xfffffa80`0d349778 _DEVOBJ_EXTENSION


+0x140 Reserved         : (null)


 


查看由 volsnap.sys 创建的UPPER FILTER 设备


 


kd> dt _DEVICE_OBJECT 0xfffffa80`0d6fe340


ntdll!_DEVICE_OBJECT


+0x000 Type             : 0n3


+0x002 Size             : 0xc48


+0x004 ReferenceCount   : 0n0


+0x008 DriverObject     : 0xfffffa80`0d28a390 _DRIVER_OBJECT


+0x010 NextDevice       : (null)


+0x018 AttachedDevice   : (null)   –>上面已不存在UPPER FILTER 设备,在“卷设备”的角度来说,它算是此“设备栈”的最上层设备


+0x020 CurrentIrp       : (null)


+0x028 Timer            : (null)


+0x030 Flags            : 0x10


+0x034 Characteristics  : 0


+0x038 Vpb              : (null)


+0x040 DeviceExtension  : 0xfffffa80`0d6fe490 Void


+0x048 DeviceType       : 7


+0x04c StackSize        : 6 ”  –>这个值,验证了我们的想法


+0x050 Queue            :


+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`0d6fef88 _DEVOBJ_EXTENSION


+0x140 Reserved         : (null)


 


 


验证完 StackSize 的值(即IRP到达设备的顺序),接着以之前的思路继续推进。


 


查看“卷设备”的“设备对象扩展”


 


kd> dt _DEVOBJ_EXTENSION 0xfffffa80`0d349778


ntdll!_DEVOBJ_EXTENSION


+0x000 Type             : 0n13


+0x002 Size             : 0


+0x008 DeviceObject     : 0xfffffa80`0d3494c0 _DEVICE_OBJECT


+0x010 PowerFlags       : 0x10


+0x018 Dope             : 0xfffffa80`0cf913a0 _DEVICE_OBJECT_POWER_EXTENSION


+0x020 ExtensionFlags   : 0x800


+0x028 DeviceNode       : 0xfffffa80`0d2ac2b0 Void


+0x030 AttachedTo       : (null)   –>没有LOWER DEVICE “低层设备”


+0x038 StartIoCount     : 0n0


+0x03c StartIoKey       : 0n0


+0x040 StartIoFlags     : 0


+0x048 Vpb              : (null)   –>其VPB也无效,本身VPB也仅属于“卷设备”特有的,“低层设备”不再是“卷设备”,所以这里无效也是正确的


+0x050 DependentList    : _LIST_ENTRY [ 0xfffffa80`0d3497c8 – 0xfffffa80`0d3497c8 ]


+0x060 ProviderList     : _LIST_ENTRY [ 0xfffffa80`0d3497d8 – 0xfffffa80`0d3497d8 ]


 


 


通过以上信息可以得知“卷设备”与“低层设备”的关联并不是通过之前已熟知的方式,即不是DEVOBJ_EXTENSION->AttachedTo 也不是 DEVOBJ_EXTENSION->VPB,而其“设备对象”(DEVICE_OBJECT)中的其他域也没有何直观的信息可供参考,整体流程在这里被堵死了,现在唯有一个办法就是在逆向volmgr.sys驱动,找到它是如何关联到“低层设备”上的,也就是IRP要发往的设备到底放在了哪里?


 


首先查看下卷设备驱动volmgr.sys的派发表(Dispatch routines)


 


kd> !drvobj volmgr 2


Driver object (fffffa800cf1b1c0) is for:


\Driver\volmgr


DriverEntry:   fffff880011e906c   volmgr!GsDriverEntry


DriverStartIo: 00000000


DriverUnload:  fffff880011e30a0  volmgr!VmUnload


AddDevice:     00000000


 


Dispatch routines:


[00] IRP_MJ_CREATE                      fffff880011d9280       volmgr!VmCreate


[01] IRP_MJ_CREATE_NAMED_PIPE           fffff800016cb1d4     nt!IopInvalidDeviceRequest


[02] IRP_MJ_CLOSE                       fffff800016cb1d4        nt!IopInvalidDeviceRequest


[03] IRP_MJ_READ                        fffff880011d9070       volmgr!VmReadWrite


[04] IRP_MJ_WRITE                       fffff880011d9070       volmgr!VmReadWrite


[05] IRP_MJ_QUERY_INFORMATION           fffff800016cb1d4    nt!IopInvalidDeviceRequest


[06] IRP_MJ_SET_INFORMATION             fffff800016cb1d4      nt!IopInvalidDeviceRequest


[07] IRP_MJ_QUERY_EA                    fffff800016cb1d4      nt!IopInvalidDeviceRequest


[08] IRP_MJ_SET_EA                      fffff800016cb1d4        nt!IopInvalidDeviceRequest


[09] IRP_MJ_FLUSH_BUFFERS               fffff880011da110       volmgr!VmFlushBuffers


[0a] IRP_MJ_QUERY_VOLUME_INFORMATION    fffff800016cb1d4 nt!IopInvalidDeviceRequest


[0b] IRP_MJ_SET_VOLUME_INFORMATION      fffff800016cb1d4   nt!IopInvalidDeviceRequest


[0c] IRP_MJ_DIRECTORY_CONTROL           fffff800016cb1d4     nt!IopInvalidDeviceRequest


[0d] IRP_MJ_FILE_SYSTEM_CONTROL         fffff800016cb1d4       nt!IopInvalidDeviceRequest


[0e] IRP_MJ_DEVICE_CONTROL              fffff880011d9330     volmgr!VmDeviceControl


[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL     fffff880011dd0e0      volmgr!VmInternalDeviceControl


[10] IRP_MJ_SHUTDOWN                    fffff880011dcc40    volmgr!VmShutdown


[11] IRP_MJ_LOCK_CONTROL                fffff800016cb1d4     nt!IopInvalidDeviceRequest


[12] IRP_MJ_CLEANUP                     fffff880011d9230      volmgr!VmCleanup


[13] IRP_MJ_CREATE_MAILSLOT             fffff800016cb1d4       nt!IopInvalidDeviceRequest


[14] IRP_MJ_QUERY_SECURITY              fffff800016cb1d4       nt!IopInvalidDeviceRequest


[15] IRP_MJ_SET_SECURITY                fffff800016cb1d4         nt!IopInvalidDeviceRequest


[16] IRP_MJ_POWER                       fffff880011dc4d0      volmgr!VmPower


[17] IRP_MJ_SYSTEM_CONTROL              fffff880011dc3f0      volmgr!VmWmi


[18] IRP_MJ_DEVICE_CHANGE               fffff800016cb1d4     nt!IopInvalidDeviceRequest


[19] IRP_MJ_QUERY_QUOTA                 fffff800016cb1d4     nt!IopInvalidDeviceRequest


[1a] IRP_MJ_SET_QUOTA                   fffff800016cb1d4       nt!IopInvalidDeviceRequest


[1b] IRP_MJ_PNP                         fffff880011e19c0        volmgr!VmPnp


 


根据WDM驱动模型,上层设备想要将数据发送给“低层设备”,需要调用IofCallDriver进行传递。在夸“设备栈”的情况下,无非是用 IRP_MJ_READ/WRITE、IRP_MJ_DEVICE_CONTROL、IRP_MJ_INTERNAL_DEVICE_CONTROL 这几个派发函数,所以我们分别对三个派发函数设置断点,通过跟踪发现 VmInternalDeviceControl 没有被断下来,也没有 IofCallDriver 的调用。


 


下面对VmDeviceControl 与 VmReadWrite 的调用栈进行分析:


 


0: kd> g


Breakpoint 0 hit


volmgr!VmDeviceControl:


fffff880`011bc330 48895c2418      mov     qword ptr [rsp+18h],rbx


 


1: kd> kvn


# Child-SP          RetAddr           : Args to Child                                                           : Call Site


00 fffff880`06916668 fffff880`01b4e082 : 00000000`00000000 fffffa80`0d800290 00000000`00000000 00000000`00000000 : volmgr!VmDeviceControl


01 fffff880`06916670 fffff880`012e7ef0 : 00000000`00000000 00000000`00000000 fffffa80`0d800290 00000000`004d0008 : volsnap!VolSnapDeviceControl+0x72


02 fffff880`069166e0 fffff880`012d9a6b : 00000000`00000000 fffff8a0`01c665f0 00000000`00000000 00000000`000007ff : mountmgr!MountMgrSendDeviceControl+0x9c


03 fffff880`06916770 fffff880`012da248 : fffffa80`0e3f3a90 fffff880`06916918 fffffa80`0d19d660 fffffa80`0e7733f0 : mountmgr!QueryDeviceInformation+0x207


04 fffff880`069167f0 fffff880`012e6661 : fffffa80`0e3f3a90 00000000`00000000 fffffa80`0e7733f0 fffff880`069168b0 : mountmgr!FindDeviceInfo+0x38


05 fffff880`06916850 fffff880`012e7af8 : fffffa80`0e3f3a90 00000000`00000000 00000000`00000000 00000000`00000000 : mountmgr!MountMgrQueryDosVolumePaths+0x129


06 fffff880`06916900 fffff800`019faf97 : fffffa80`0e773508 fffffa80`0e3f3a90 fffff880`06916c60 fffffa80`0e7733f0 : mountmgr!MountMgrDeviceControl+0x170


07 fffff880`069169d0 fffff800`019fb7f6 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : nt!IopXxxControlFile+0x607


08 fffff880`06916b00 fffff800`016df8d3 : ffffffff`ffffffff 00000000`01dde948 00000000`01dde960 fffff800`00000004 : nt!NtDeviceIoControlFile+0x56


09 fffff880`06916b70 00000000`7777138a : 000007fe`fd76b939 00000000`01dde928 00000000`00000000 00000000`00000000 : nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ fffff880`06916be0)


0a 00000000`01dde9a8 000007fe`fd76b939 : 00000000`01dde928 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!NtDeviceIoControlFile+0xa


0b 00000000`01dde9b0 00000000`7755c2a9 : 00000000`002bc5c0 00000000`002bc5c0 00000000`00000003 00000000`009ffdff : KernelBase!DeviceIoControl+0x75


0c 00000000`01ddea20 000007fe`f9ac295d : 00000000`0018fe40 00000000`00ba7240 00000000`00ba03b0 00000000`00ba7240 : kernel32!GetVolumePathNamesForVolumeNameW+0x299


0d 00000000`01ddeab0 00000000`0018fe40 : 00000000`00ba7240 00000000`00ba03b0 00000000`00ba7240 00000001`000000ec : 0x7fe`f9ac295d


0e 00000000`01ddeab8 00000000`00ba7240 : 00000000`00ba03b0 00000000`00ba7240 00000001`000000ec 00000000`0051e299 : 0x18fe40


0f 00000000`01ddeac0 00000000`00ba03b0 : 00000000`00ba7240 00000001`000000ec 00000000`0051e299 00000000`00000000 : 0xba7240


10 00000000`01ddeac8 00000000`00ba7240 : 00000001`000000ec 00000000`0051e299 00000000`00000000 00000007`ec81c000 : 0xba03b0


11 00000000`01ddead0 00000001`000000ec : 00000000`0051e299 00000000`00000000 00000007`ec81c000 00000009`ffdff000 : 0xba7240


12 00000000`01ddead8 00000000`0051e299 : 00000000`00000000 00000007`ec81c000 00000009`ffdff000 00000000`01ddec50 : 0x1`000000ec


13 00000000`01ddeae0 00000000`00000000 : 00000007`ec81c000 00000009`ffdff000 00000000`01ddec50 00000000`00000028 : 0x51e299


 


通过VmDeviceControl 调用栈信息可以看到,是在获取卷名等相关信息的情况下通过mountmgr.sys间接调用到此函数,经过多次断点的比较,没有发现从文件系统下发的读/写操作,通过反汇编方法找到两处调用IofCallDriver 的地方:


 


第一处调用:


volmgr!VmDeviceControl+0x2e0:


fffff880`011bc610 488baf10010000  mov     rbp,qword ptr [rdi+110h]  –>这里的 rdi 指向 DEVICE_OBJECT->DeviceExtension “设备扩展”,注意:不是“设备对象扩展”


fffff880`011bc617 488bcd          mov     rcx,rbp


fffff880`011bc61a ff15004c0000    call    qword ptr [volmgr!_imp_ObfReferenceObject (fffff880`011c1220)]


fffff880`011bc620 41f644240202    test    byte ptr [r12+2],2


fffff880`011bc626 0f859c030000    jne     volmgr!VmDeviceControl+0x698 (fffff880`011bc9c8)


 


volmgr!VmDeviceControl+0x2fc:


fffff880`011bc62c 488b8eb8000000  mov     rcx,qword ptr [rsi+0B8h]


fffff880`011bc633 4c8d05c6fbffff  lea     r8,[volmgr!VmpRefCountCompletionRoutine (fffff880`011bc200)]


fffff880`011bc63a 488b01          mov     rax,qword ptr [rcx]


fffff880`011bc63d 488941b8        mov     qword ptr [rcx-48h],rax


fffff880`011bc641 488b4108        mov     rax,qword ptr [rcx+8]


fffff880`011bc645 488941c0        mov     qword ptr [rcx-40h],rax


fffff880`011bc649 488b4110        mov     rax,qword ptr [rcx+10h]


fffff880`011bc64d 488941c8        mov     qword ptr [rcx-38h],rax


fffff880`011bc651 488b4118        mov     rax,qword ptr [rcx+18h]


fffff880`011bc655 488941d0        mov     qword ptr [rcx-30h],rax


fffff880`011bc659 488b4120        mov     rax,qword ptr [rcx+20h]


fffff880`011bc65d 488941d8        mov     qword ptr [rcx-28h],rax


fffff880`011bc661 488b4128        mov     rax,qword ptr [rcx+28h]


fffff880`011bc665 488941e0        mov     qword ptr [rcx-20h],rax


fffff880`011bc669 488b4130        mov     rax,qword ptr [rcx+30h]


fffff880`011bc66d 488941e8        mov     qword ptr [rcx-18h],rax


fffff880`011bc671 c641bb00        mov     byte ptr [rcx-45h],0


fffff880`011bc675 488b96b8000000  mov     rdx,qword ptr [rsi+0B8h]


fffff880`011bc67c 418b442418      mov     eax,dword ptr [r12+18h]


fffff880`011bc681 4c8942f0        mov     qword ptr [rdx-10h],r8


fffff880`011bc685 c642bbe0        mov     byte ptr [rdx-45h],0E0h


fffff880`011bc689 488942f8        mov     qword ptr [rdx-8],rax


fffff880`011bc68d 488bd6          mov     rdx,rsi


 


volmgr!VmDeviceControl+0x360:


fffff880`011bc690 488bcd          mov     rcx,rbp  –>通过上面偏移110h处的赋值,现在正好rcx指向我们要发往的“低层设备”


fffff880`011bc693 ff15ff4b0000    call    qword ptr [volmgr!_imp_IofCallDriver  (fffff880`011c1298)] –>此函数第一个参数即“低层设备”,保存在rcx寄存器中


fffff880`011bc699 448be8          mov     r13d,eax


 


第二处调用:


volmgr!VmDeviceControl+0x671:


fffff880`011bc9a1 488b8f10010000  mov     rcx,qword ptr [rdi+110h]  –>同上,rcx=DEVICE_OBJECT->DeviceExtension+110h偏移处为“低层设备”


fffff880`011bc9a8 ff15ea480000    call    qword ptr [volmgr!_imp_IofCallDriver (fffff880`011c1298)]


fffff880`011bc9ae 448bf0          mov     r14d,eax


fffff880`011bc9b1 e90afbffff      jmp     volmgr!VmDeviceControl+0x190 (fffff880`011bc4c0)


 


验证rdi+110h处是否为要找的“低层设备”


 


0: kd> !devobj poi(@rdi+0x110)


Device object (fffffa800d2b9530) is for:


\Driver\partmgr DriverObject fffffa800d1517c0  –>此设备是名为partmgr.sys驱动创建的


Current Irp 00000000 RefCount 0 Type 00000007 Flags 00200180


DevExt fffffa800d2b9680 DevObjExt fffffa800d2b9778


ExtensionFlags (0x00000800)


Unknown flags 0x00000800


Device queue is not busy.


 


0: kd> dt _DEVICE_OBJECT poi(@rdi+0x110)


ntdll!_DEVICE_OBJECT


+0x000 Type             : 0n3


+0x002 Size             : 0x248


+0x004 ReferenceCount   : 0n0


+0x008 DriverObject     : 0xfffffa80`0d1517c0 _DRIVER_OBJECT


+0x010 NextDevice       : 0xfffffa80`0d2b7380 _DEVICE_OBJECT


+0x018 AttachedDevice   : (null)   –>注意这里为空,说明不是以UPPER FILTER 方式进行关联的


+0x020 CurrentIrp       : (null)


+0x028 Timer            : (null)


+0x030 Flags            : 0x200180


+0x034 Characteristics  : 0x100


+0x038 Vpb              : (null)


+0x040 DeviceExtension  : 0xfffffa80`0d2b9680 Void


+0x048 DeviceType       : 7


+0x04c StackSize        : 4 ”  –>注意这里值为4,表明此设备为volmgr.sys的低层设备


+0x050 Queue            :


+0x098 AlignmentRequirement : 0


+0x0a0 DeviceQueue      : _KDEVICE_QUEUE


+0x0c8 Dpc              : _KDPC


+0x108 ActiveThreadCount : 0


+0x110 SecurityDescriptor : (null)


+0x118 DeviceLock       : _KEVENT


+0x130 SectorSize       : 0


+0x132 Spare1           : 0


+0x138 DeviceObjectExtension : 0xfffffa80`0d2b9778 _DEVOBJ_EXTENSION


+0x140 Reserved         : (null)


 


现在通过StackSize的值(5->4)确认并找到了volmgr的“低层设备”,它存在于volmgr“设备扩展”DEVICE_EXTENSION+0x110h处,是由partmgr驱动所创建。但同时又带来了两个问题:


 


一、使用 !devobj 命令并没有列出partmgr的“低层设备”,难道我们还需要再逆向partmgr相关派发函数,通过调用栈分析出“低层设备”?我们知道“设备扩展”DeviceExtension 结构本就是留给开发者自己定义的,是私有的,每个DRIVER都有不同的结构和字段,在这里使用属迫不得已,因为没有稳定结构(DEVOBJ_EXTENSION)可用,还有就是经过多台服务器,安装不同的磁盘类安全及存储软件,没有发现在volmgr->partmgr流程中插入设备的,因为volmgr驱动功能简单,在封装一些操作后直接发往partmgr设备上。但partmgr与disk驱动之间或disk驱动与它的“低层设备”之间,多数磁盘类安全软件以和存储类型驱动都会插入自己的拦截设备,即使没有第三方驱动,在不同Windows服务器上同样会存在差异。我后面会给出的一个例子,在disk驱动的下层不是miniport driver而是在中间插入了MS自己的acpi驱动;在partmgr与disk之间(由于第三方磁盘类安全软件的存在)插入了名为DeepFrz的驱动。这也就意味着,如果一直要按照DeviceExtension方式定位下去,找到最终设备,需要分析多种可能出现在其间的DRIVER,这明显是不现实的。而且某些DRIVER的“设备扩展”不一定就包含了“低层设备”。可见继续根据DeviceExtension方法来盲目定位几乎是不可取的,也不符合通用性要求。


 


二、通过对VmDeviceControl调用栈的分析,只发现了对卷设备的IO CONTRL操作,如:删除、更新/设置属性、查询信息等操作。并没有发现来自文件系统的读/写操作,也就是说读/写操作不是走这个流程,可根据StackSize来看,在搭建存储栈的时候,IRP明明应该走向这里,而由partmgr创建的“低层设备”的UPPER FILTER与DEVOBJ_EXTENSION又为空。


 


刚进一步,这里又陷入了困局。暂时没有头绪的事,先不管它,想到了还有VmReadWrite,先看看它的调用栈。首先反汇编观察发现在VmReadWrite中只有一处调用了IofCallDriver,在volmgr!VmReadWrite+0x101处,在这里下断点查看调用栈:


 


0: kd> kvn


# Child-SP          RetAddr           : Args to Child                                                           : Call Site


00 fffff880`07345c60 fffff880`01895108 : fffff880`01fe8a00 fffffa80`0d6403e0 fffffa80`0e7f4010 00000000`00000000 : volmgr!VmReadWrite+0x101


01 fffff880`07345ca0 fffff880`0146739a : fffff880`01fe8a00 fffffa80`0cd42680 fffff880`01fe8928 00000000`00000000 : volsnap!VolsnapWriteFilter+0xf8


02 fffff880`07345cf0 fffff800`01697157 : 00000000`00000001 00000000`00000000 00000000`00000000 00000000`00000000 : Ntfs!NtfsStorageDriverCallout+0x16


03 fffff880`07345d20 fffff800`01697111 : 00000000`00000000 00000000`00000000 fffff880`07346000 fffff800`016ac242 : nt!KxSwitchKernelStackCallout+0x27 (TrapFrame @ fffff880`07345be0)


04 fffff880`01fe87f0 fffff800`016ac242 : fffff880`01fe89b0 00000000`00000002 fffff880`01fe8da0 fffffa80`0d22b2e0 : nt!KiSwitchKernelStackContinue


05 fffff880`01fe8810 fffff880`01466f09 : fffff880`01467384 00000000`03d22000 fffffa80`00001000 fffff800`00000000 : nt!KeExpandKernelStackAndCalloutEx+0x2a2


06 fffff880`01fe88f0 fffff880`014663d6 : fffffa80`0d2392a0 00000000`00000000 fffffa80`0d22b2e0 00000000`00000000 : Ntfs!NtfsMultipleAsync+0xa9


07 fffff880`01fe8960 fffff880`0146f224 : fffff880`01fe8da0 fffffa80`0e7f4010 00000000`00000000 00000000`00001000 : Ntfs!NtfsNonCachedIo+0x216


08 fffff880`01fe8b30 fffff880`0146a507 : fffff880`01fe8da0 fffffa80`0e7f4010 fffffa80`0d22b2e0 00000000`03d21000 : Ntfs!NtfsNonCachedUsaWrite+0x64


09 fffff880`01fe8bc0 fffff880`0146b1a3 : fffff880`01fe8da0 fffffa80`0e7f4010 fffff880`01fe8f00 fffff880`00001000 : Ntfs!NtfsCommonWrite+0x2ca4


0a fffff880`01fe8d70 fffff880`012ffbcf : fffffa80`0e7f42d8 fffffa80`0e7f4010 fffffa80`0eb71010 00000000`00000000 : Ntfs!NtfsFsdWrite+0x1c3 –>来自文件系统的写操作


0b fffff880`01fe8ff0 fffff880`012fe6df : fffffa80`0d249300 fffffa80`0d265440 fffffa80`0d249300 fffffa80`0e7f4010 : fltmgr!FltpLegacyProcessingAfterPreCallbacksCompleted+0x24f


0c fffff880`01fe9080 fffff800`0168556f : fffffa80`0e7f4010 fffff880`01fe9680 fffff880`01fe9280 fffff800`01811e80 : fltmgr!FltpDispatch+0xcf


0d fffff880`01fe90e0 fffff800`016e3660 : 00000000`00000000 fffff880`01fe9680 fffffa80`000ba3c0 00000000`00000000 : nt!IoSynchronousPageWrite+0x24f


0e fffff880`01fe9160 fffff800`016e1d38 : fffff8a0`002f8908 fffff8a0`002f8910 fffffa80`0d265380 fffffa80`0d265380 : nt!MiFlushSectionInternal+0xb30


0f fffff880`01fe9390 fffff800`016e10c9 : fffffa80`0d2132c0 00000000`00000000 00000000`00001000 fffff880`01fe9640 : nt!MmFlushSection+0x1f4


10 fffff880`01fe9450 fffff880`015196e1 : fffffa80`0d2132c0 fffff800`00000000 fffffa80`00001000 fffff880`00001000 : nt!CcFlushCache+0x5e9


11 fffff880`01fe9550 fffff880`0151c8db : fffff880`01fe9a70 fffffa80`0d7f2180 fffff880`01fe9a00 fffff880`0146f000 : Ntfs!NtfsCheckpointVolume+0xbc1


12 fffff880`01fe9950 fffff880`0151b27b : fffff880`01fe9a70 fffffa80`0d7f2180 fffffa80`0d7f2188 fffff880`01462020 : Ntfs!NtfsCheckpointAllVolumesWorker+0x4b


13 fffff880`01fe99a0 fffff880`0151d398 : fffff880`01fe9a70 00000000`00000000 fffff880`0151c890 fffff880`01fe9c78 : Ntfs!NtfsForEachVcb+0x167


14 fffff880`01fe9a40 fffff800`016a9a21 : fffffa80`0cd6ae00 fffff800`01995f00 fffff800`0189e100 00001ebb`00000001 : Ntfs!NtfsCheckpointAllVolumes+0xb8


15 fffff880`01fe9c70 fffff800`0193ccce : ce8b48d2`33c78b4d fffffa80`0cd42680 00000000`00000080 fffffa80`0cd25150 : nt!ExpWorkerThread+0x111


16 fffff880`01fe9d00 fffff800`01690fe6 : fffff880`009bf180 fffffa80`0cd42680 fffff880`009ca6c0 85894100`00013c86 : nt!PspSystemThreadStartup+0x5a


17 fffff880`01fe9d40 00000000`00000000 : fffff880`01fea000 fffff880`01fe4000 fffff880`01fe99a0 00000000`00000000 : nt!KxStartSystemThread+0x16


 


原来实际读/写操作走的是VmReadWrite,通过多次断点观察,VmReadWrite的调用次数要比VmDeviceControl频繁的多,看来这两个派发函数分别是负责卷的属性设置和读写操作。我们最关心是读/写流程的IRP是否与VmDeviceControl发往的是同一个“低层设备”,如果不是,它的StackSize到底是多少,它们之间到底是怎样的关系,是如何连接的?继续反汇编查看详情:


 


volmgr!VmReadWrite+0x101:


fffff880`01189171 48038b30010000  add     rcx,qword ptr [rbx+130h]


fffff880`01189178 48894ad0        mov     qword ptr [rdx-30h],rcx


fffff880`0118917c 488b8b20010000  mov     rcx,qword ptr [rbx+120h] –>获取“低层设备”,注意:这里明显与VmDeviceControl发往的不是同一个设备


fffff880`01189183 488bd6          mov     rdx,rsi –>这里的rdx为当前 IRP


fffff880`01189186 ff150c510000    call    qword ptr [volmgr!_imp_IofCallDriver (fffff880`0118e298)]


fffff880`0118918c 8be8            mov     ebp,eax


fffff880`0118918e 3d16000080      cmp     eax,80000016h


fffff880`01189193 0f84ab1e0000    je      volmgr! ?? ::FNODOBFM::`string’+0x69c (fffff880`0118b044)


 


当前rbx指向“设备扩展”DEVICE_EXTENSION。上文提到过rcx作为IofCallDriver的第一个参数即“低层设备”的DEVICE_OBJECT,这里明显可以看到使用的是偏移120h而不是110h,说明不是同一个“低层设备”。这里的rdx作为其第二个输入参数,即IRP;要注意,第二个参数类型为IN/OUT类型,当IoCallDriver调用成功后rsi指向返回的IRP,如果安装有“完成例程”CompletionRoutine则再次获得控制权。观察IRP沿设备栈返回的情况,可以看到“低层设备”


 


1: kd> !irp @rsi


Irp is active with 8 stacks 5 is current (= 0xfffffa800eb9b640)


Mdl=fffffa800d2c5390: No System Buffer: Thread fffffa800ea66b60:  Irp stack trace.


cmd  flg cl Device   File     Completion-Context


[  0, 0]   0  0 00000000 00000000 00000000-00000000


 


Args: 00000000 00000000 00000000 00000000


[  0, 0]   0  0 00000000 00000000 00000000-00000000


 


Args: 00000000 00000000 00000000 00000000


[  0, 0]   0  0 00000000 00000000 00000000-00000000


 


Args: 00000000 00000000 00000000 00000000


[  0, 0]   0  0 00000000 00000000 00000000-00000000


 


Args: 00000000 00000000 00000000 00000000


>[  3,34]  10 e0 fffffa800d632790 00000000 fffff88001163f00-00000000 Success Error Cancel


\Driver\Disk       partmgr!PmReadWriteCompletion  –>可以看到partmgr在“低层设备”上安装的“完成例程”,此设备由disk.sys驱动创建


Args: 00008000 00000000 e659e800 00000000


[  3, 0]  10 e0 fffffa800d2b7380 00000000 fffff88001189010-fffffa800d2bd610 Success Error Cancel


\Driver\partmgr         volmgr!VmpReadWriteCompletionRoutine –>可以看到volmgr在它的“低层设备”partmgr上安装的读/写“完成例程”


Args: 81f2c6186 00000000 e659e800 00000000


[  3, 0]   0 e0 fffffa800d2bd4c0 00000000 fffff88001467344-fffff880061765f0 Success Error Cancel


\Driver\volmgr  Ntfs!NtfsMasterIrpSyncCompletionRoutine


Args: 00008000 00000000 801c793b9 00000000


[  3, 0]   0  0 fffffa800d7f2030 fffffa800d503880 00000000-00000000


\FileSystem\Ntfs


Args: 00008000 00000000 00132800 00000000


 


通过上面的信息很清晰的看到了IRP流经的设备。再看看“设备对象扩展”DEVICE_EXTENSION偏移120h处到底是什么设备。(上面“[]”中的第一个数字代表是由哪个Dispatch Routines进行的处理,这里为3表明是IRP_MJ_READ操作)


 


1: kd> !devobj poi(@rbx+120)


Device object (fffffa800d2b7380) is for:


\Driver\partmgr DriverObject fffffa800d1547c0


Current Irp 00000000 RefCount 0 Type 00000007 Flags 00000010


DevExt fffffa800d2b74d0 DevObjExt fffffa800d2b7778


ExtensionFlags (0x00000800)


Unknown flags 0x00000800


AttachedTo (Lower) fffffa800d632790 \Driver\Disk  –>此设备才真正与“低层设备”进行关联


Device queue is not busy.


 


查看此设备的StackSize


 


0: kd> dt _DEVICE_OBJECT poi(@rbx+0x120)


ntdll!_DEVICE_OBJECT


+0x000 Type             : 0n3


+0x002 Size             : 0x3f8


+0x004 ReferenceCount   : 0n0


+0x008 DriverObject     : 0xfffffa80`0d0ba4f0 _DRIVER_OBJECT


+0x010 NextDevice       : 0xfffffa80`0d192060 _DEVICE_OBJECT


+0x018 AttachedDevice   : (null)


+0x020 CurrentIrp       : (null)


+0x028 Timer            : (null)


+0x030 Flags            : 0x10


+0x034 Characteristics  : 0x100


+0x038 Vpb              : (null)


+0x040 DeviceExtension  : 0xfffffa80`0d2b54d0 Void


+0x048 DeviceType       : 7


+0x04c StackSize        : 3 ”  –>注意:同是partmgr.sys创建的设备,此值不一样


+0x050 Queue            :


+0x098 AlignmentRequirement : 0


+0x0a0 DeviceQueue      : _KDEVICE_QUEUE


+0x0c8 Dpc              : _KDPC


+0x108 ActiveThreadCount : 0


+0x110 SecurityDescriptor : (null)


+0x118 DeviceLock       : _KEVENT


+0x130 SectorSize       : 0


+0x132 Spare1           : 0


+0x138 DeviceObjectExtension : 0xfffffa80`0d2b5778 _DEVOBJ_EXTENSION


+0x140 Reserved         : (null)


 


在我第一次看到这里时已完全打破了对“设备栈”链接的固有认识。在继续分析前,有必要先梳理下volmgr->partmgr的流程:首先可以确认的是volmgr.sys所创建的设备(此设备StackSize为5)没有使用系统结构方式(“设备对象扩展”DEVOBJ_EXTENSION中的任何域,如:VPB与AttahcedTo)与“低层设备”进行关联,而是通过“设备扩展”DEVICE_EXTENSION进行关联。其次所连接的“低层设备”有两个(由partmgr.sys驱动创建),偏移分别为110h(此设备StackSize为4)与120h(此设备StackSize为3),前者负责信息属性类的操作,后者负责读/写操作。当来自文件系统的读/写调用,volmgr.sys直接跳过了StackSize为4(偏移110h处)的设备对象,将IRP发送给StackSize为3(偏移120h处)的设备对象,这个设备才是真正与其“低层设备”disk.sys(类驱动)进行关联的设备。


 


到现在为止volmgr->partmgr的流程算是打通了,也定位到了partmgr与disk驱动打交道的具体设备。可从“设备栈”搭建的角度来说,partmgr创建的两个设备StackSize由4到3是存在着前后顺序与高低之分的,那么这两个设备之间是如何关联的?难道还要分析“设备扩展”DEVICE_EXTENSION?考虑入手点的同时,我又仔细的把两个设备的信息详细的过滤了一遍,发现偏移110h处设备(StackSize为4)的NextDevice所指向的地址“0xfffffa80`0d2b7380”正好就是偏移120h处的设备对象(StackSize为3)。也就是说两个设备之间通过NextDevice即可以定位,当进行信息属性类操作时(IO CONTROL),“卷设备”的IRP首先发送到110h 处的设备,再由此设备传递给120h处的设备。而正是因为这两个设备都是由partmgr同一驱动创建的,所以不以DEVICE_OBJECT->AttachedDevice和DEVOBJ_EXTENSION->AttahcedTo方式进行链接。想一下也合理,因为此种方式只适用于不同驱动所创建的FILTER DEVICE。一句话总结:两个(依分区数增加)设备同由partmgr创建,第一个设备(偏移10h)将第二个设备当做“低层设备”把IRP发给它。


 


接下来验证我们的设想,在partmgr!PmLegacyDeviceControl处找到IofCallDriver的位置0x1c4并在此处下断点,可以看到“卷设备”的IO CONTROL传递。


 


0: kd> kvn


# Child-SP          RetAddr           : Args to Child                                                           : Call Site


00 fffff880`06f9acb0 fffff880`011bc699 : fffffa80`0d2e6530 fffffa80`0e860c60 fffffa80`0e860e98 fffffa80`0e860c60 : partmgr!PmLegacyDeviceControl+0x1c4


01 fffff880`06f9ad00 fffff880`01a72082 : fffffa80`00000000 fffff880`06f9b420 fffffa80`0e860c60 fffff880`06f9b074 : volmgr!VmDeviceControl+0x369  –>“卷设备”属性相关操作


02 fffff880`06f9af10 fffff800`0194ebfa : fffffa80`0d80d290 fffffa80`0d80d290 fffff880`06f9b420 fffffa80`0da3e060 : volsnap!VolSnapDeviceControl+0x72


03 fffff880`06f9af80 fffff800`0194d28c : 00000000`00000000 00000000`00000000 fffffa80`0d87e070 fffffa80`0da3e0e8 : nt!TmpIsClusteredTransactionManager+0x9a


04 fffff880`06f9b010 fffff800`019578c6 : fffffa80`0da300f4 fffffa80`0da3e060 00000000`00000000 fffffa80`0da3e060 : nt!TmpCreateLogFile+0x148


05 fffff880`06f9b1a0 fffff800`01951a4f : fffffa80`0da3e060 fffff880`06f9b5f0 00000000`00000000 00000000`00000000 : nt!TmpCreateOrOpenLogTransactionManager+0x1e


06 fffff880`06f9b1e0 fffff800`019550da : fffff880`06f9b610 00000000`00000000 fffff880`06f9b420 fffff880`06f9b5f0 : nt!TmInitializeTransactionManager+0x1f7


07 fffff880`06f9b280 fffff800`016ca8d3 : fffffa80`0e5cc060 fffff880`06f9b558 fffff880`06f9b348 fffff800`017f61de : nt!NtCreateTransactionManager+0xea


08 fffff880`06f9b330 fffff800`016c6e70 : fffff800`019482f1 00000000`00000001 00000000`00000030 fffff8a0`6d524d43 : nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ fffff880`06f9b3a0)


09 fffff880`06f9b538 fffff800`019482f1 : 00000000`00000001 00000000`00000030 fffff8a0`6d524d43 00000000`000007ff : nt!KiServiceLinkage


0a fffff880`06f9b540 fffff800`01948fe2 : 00000000`00000000 fffff8a0`04f7c010 00000000`00000001 fffff800`018c9e40 : nt!CmpInitCmRM+0x4ed


0b fffff880`06f9b720 fffff800`01948a6b : 00000000`00000020 fffff880`06f9ba40 00000000`00000000 00000000`00000021 : nt!CmLoadKey+0x452


0c fffff880`06f9b910 fffff800`016ca8d3 : fffffa80`0e5c8060 00000000`00d6f038 fffffa80`00000000 00000000`00000d44 : nt!NtLoadKeyEx+0x4c5


0d fffff880`06f9bb70 00000000`774a213a : 000007fe`fba8630c 00000000`00000000 000007fe`fbab02b0 00000000`00000000 : nt!KiSystemServiceCopyEnd+0x13 (TrapFrame @ fffff880`06f9bbe0)


0e 00000000`00d6efc8 000007fe`fba8630c : 00000000`00000000 000007fe`fbab02b0 00000000`00000000 00000000`005c005c : ntdll!NtLoadKeyEx+0xa


0f 00000000`00d6efd0 00000000`00000000 : 000007fe`fbab02b0 00000000`00000000 00000000`005c005c 00000000`00000000 : 0x7fe`fba8630c


 


 


反汇编分析此处代码实现:


 


0: kd> u


partmgr!PmLegacyDeviceControl+0x1c4:


fffff880`011934a4 fe4743          inc     byte ptr [rdi+43h]  –>将IRP-> CurrentLocation 加一,即当前IRP位置上移(在IofCallDriver中会将此值减一,回到当位置)


fffff880`011934a7 488387b800000048 add     qword ptr [rdi+0B8h],48h –>将当前IRP堆栈(IRP->Tail.Overlay.CurrentStackLocation)上移,在IofCallDriver中会下移回来,这样“低层设备”使用的就是当前设备的IRP堆栈


 


以上两步目的是将FILTER DEVICE隐与无形,不让上下层感知到它的存在,在不需要处理“返回数据”时,是一种常用手法。WDM为此提供了一个宏叫:IoSkipCurrentStackLocation


 


fffff880`011934af 488b4b18        mov     rcx,qword ptr [rbx+18h]  –>从“设备扩展”DEVICE_EXTENSION偏移18h处得到“低层设备”


fffff880`011934b3 488bd7          mov     rdx,rdi  –>rdi为当前IRP


fffff880`011934b6 ff15844b0000    call    qword ptr [partmgr!_imp_IofCallDriver (fffff880`01198040)]


fffff880`011934bc 8bf0            mov     esi,eax


fffff880`011934be eb9d            jmp     partmgr!PmLegacyDeviceControl+0x17d (fffff880`0119345d)


fffff880`011934c0 4181f918cc2d00  cmp     r9d,2DCC18h


 


现在得到了“低层设备”,但要验证假设,还需要得到发送IRP的“当前设备”。因为没有直观信息,我想到的方法是从IRP->Tail.Overlay.CurrentStackLocation->DeviceOjbect获取“当前设备”,也就是发送IRP的设备。


 


0: kd> dt _IRP @rdi /r


ntdll!_IRP


+0x000 Type             : 0n6


+0x002 Size             : 0x3a0


+0x008 MdlAddress       : (null)


+0x010 Flags            : 0x60070


+0x018 AssociatedIrp    :


+0x000 MasterIrp        : 0xfffffa80`0da272c0 _IRP


+0x000 Type             : 0n0


+0x002 Size             : 0


+0x008 MdlAddress       : 0x00320033`006d0065 _MDL


+0x010 Flags            : 0x77005c


+0x018 AssociatedIrp    :


+0x020 ThreadListEntry  : _LIST_ENTRY [ 0x00760072`00500069 – 0x65002e`00450053 ]


+0x030 IoStatus         : _IO_STATUS_BLOCK


+0x040 RequestorMode    : 8 ”


+0x041 PendingReturned  : 0 ”


+0x042 StackCount       : 21 ”


+0x043 CurrentLocation  : 2 ”


+0x044 Cancel           : 0x46 ‘F’


+0x045 CancelIrql       : 0x69 ‘i’


+0x046 ApcEnvironment   : 108 ‘l’


+0x047 AllocationFlags  : 0xe5 ”


+0x048 UserIosb         : (null)


+0x050 UserEvent        : 0x00000180`00000400 _KEVENT


+0x058 Overlay          :


+0x068 CancelRoutine    : 0x00000000`0078004a           void  +78004a


+0x070 UserBuffer       : 0xfffffa80`0cd3d0d0 Void


+0x078 Tail             :


+0x000 IrpCount         : 0n228750016


+0x000 SystemBuffer     : 0xfffffa80`0da272c0 Void


+0x020 ThreadListEntry  : _LIST_ENTRY [ 0xfffffa80`0e5cc448 – 0xfffffa80`0e5cc448 ]


+0x000 Flink            : 0xfffffa80`0e5cc448 _LIST_ENTRY [ 0xfffffa80`0e860c80 – 0xfffffa80`0e860c80 ]


+0x000 Flink            : 0xfffffa80`0e860c80 _LIST_ENTRY [ 0xfffffa80`0e5cc448 – 0xfffffa80`0e5cc448 ]


+0x008 Blink            : 0xfffffa80`0e860c80 _LIST_ENTRY [ 0xfffffa80`0e5cc448 – 0xfffffa80`0e5cc448 ]


+0x008 Blink            : 0xfffffa80`0e5cc448 _LIST_ENTRY [ 0xfffffa80`0e860c80 – 0xfffffa80`0e860c80 ]


+0x000 Flink            : 0xfffffa80`0e860c80 _LIST_ENTRY [ 0xfffffa80`0e5cc448 – 0xfffffa80`0e5cc448 ]


+0x008 Blink            : 0xfffffa80`0e860c80 _LIST_ENTRY [ 0xfffffa80`0e5cc448 – 0xfffffa80`0e5cc448 ]


+0x030 IoStatus         : _IO_STATUS_BLOCK


+0x000 Status           : 0n0


+0x000 Pointer          : (null)


+0x008 Information      : 0


+0x040 RequestorMode    : 0 ”


+0x041 PendingReturned  : 0 ”


+0x042 StackCount       : 6 ”


+0x043 CurrentLocation  : 5 ”


+0x044 Cancel           : 0 ”


+0x045 CancelIrql       : 0 ”


+0x046 ApcEnvironment   : 0 ”


+0x047 AllocationFlags  : 0x4 ”


+0x048 UserIosb         : 0xfffff880`06f9afd0 _IO_STATUS_BLOCK


+0x000 Status           : 0n0


+0x000 Pointer          : (null)


+0x008 Information      : 0xfffffa80`0da3e060


+0x050 UserEvent        : 0xfffff880`06f9afe0 _KEVENT


+0x000 Header           : _DISPATCHER_HEADER


+0x000 Type             : 0 ”


+0x001 TimerControlFlags : 0 ”


+0x001 Absolute         : 0y0


+0x001 Coalescable      : 0y0


+0x001 KeepShifting     : 0y0


+0x001 EncodedTolerableDelay : 0y00000 (0)


+0x001 Abandoned        : 0 ”


+0x001 Signalling       : 0 ”


+0x002 ThreadControlFlags : 0x6 ”


+0x002 CpuThrottled     : 0y0


+0x002 CycleProfiling   : 0y1


+0x002 CounterProfiling : 0y1


+0x002 Reserved         : 0y00000 (0)


+0x002 Hand             : 0x6 ”


+0x002 Size             : 0x6 ”


+0x003 TimerMiscFlags   : 0 ”


+0x003 Index            : 0y000000 (0)


+0x003 Inserted         : 0y0


+0x003 Expired          : 0y0


+0x003 DebugActive      : 0 ”


+0x003 ActiveDR7        : 0y0


+0x003 Instrumented     : 0y0


+0x003 Reserved2        : 0y0000


+0x003 UmsScheduled     : 0y0


+0x003 UmsPrimary       : 0y0


+0x003 DpcActive        : 0 ”


+0x000 Lock             : 0n393216


+0x004 SignalState      : 0n0


+0x008 WaitListHead     : _LIST_ENTRY [ 0xfffff880`06f9afe8 – 0xfffff880`06f9afe8 ]


+0x058 Overlay          :


+0x000 AsynchronousParameters :


+0x000 UserApcRoutine   : (null)


+0x000 IssuingProcess   : (null)


+0x008 UserApcContext   : (null)


+0x000 AllocationSize   : _LARGE_INTEGER 0x0


+0x000 LowPart          : 0


+0x004 HighPart         : 0n0


+0x000 u                :


+0x000 QuadPart         : 0n0


+0x068 CancelRoutine    : (null)


+0x070 UserBuffer       : 0xfffff880`06f9b010 Void


+0x078 Tail             :


+0x000 Overlay          :


+0x000 DeviceQueueEntry : _KDEVICE_QUEUE_ENTRY


+0x000 DriverContext    : [4] (null)


+0x020 Thread           : 0xfffffa80`0e5cc060 _ETHREAD


+0x028 AuxiliaryBuffer  : (null)


+0x030 ListEntry        : _LIST_ENTRY [ 0x00000000`00000000 – 0x0 ]


+0x040 CurrentStackLocation : 0xfffffa80`0e860e50 _IO_STACK_LOCATION  –>定位到这个地址


+0x040 PacketType       : 0xe860e50


+0x048 OriginalFileObject : (null)


+0x000 Apc              : _KAPC


+0x000 Type             : 0 ”


+0x001 SpareByte0       : 0 ”


+0x002 Size             : 0 ”


+0x003 SpareByte1       : 0 ”


+0x004 SpareLong0       : 0


+0x008 Thread           : (null)


+0x010 ApcListEntry     : _LIST_ENTRY [ 0x00000000`00000000 – 0x0 ]


+0x020 KernelRoutine    : 0xfffffa80`0e5cc060           void  +fffffa800e5cc060


+0x028 RundownRoutine   : (null)


+0x030 NormalRoutine    : (null)


+0x038 NormalContext    : (null)


+0x040 SystemArgument1  : 0xfffffa80`0e860e50 Void


+0x048 SystemArgument2  : (null)


+0x050 ApcStateIndex    : 0 ”


+0x051 ApcMode          : 0 ”


+0x052 Inserted         : 0 ”


+0x000 CompletionKey    : (null)


 


将定位到的地址以IO_STACK_LOCATION结构展开


 


0: kd> dt _IO_STACK_LOCATION 0xfffffa80`0e860e50


ntdll!_IO_STACK_LOCATION


+0x000 MajorFunction    : 0xe ”


+0x001 MinorFunction    : 0 ”


+0x002 Flags            : 0 ”


+0x003 Control          : 0xe0 ”


+0x008 Parameters       :


+0x028 DeviceObject     : 0xfffffa80`0d2e6530 _DEVICE_OBJECT  –>此地址是“当前设备”


+0x030 FileObject       : (null)


+0x038 CompletionRoutine : 0xfffff880`011bc200     long  volmgr!VmpRefCountCompletionRoutine+0


+0x040 Context          : 0x00000000`000700f8 Void


 


 


 


查看发起IRP的“当前设备”信息


 


0: kd> !devobj 0xfffffa80`0d2e6530


Device object (fffffa800d2e6530) is for:


\Driver\partmgr DriverObject fffffa800d1707c0 –>注意这里


Current Irp 00000000 RefCount 0 Type 00000007 Flags 00200180


DevExt fffffa800d2e6680 DevObjExt fffffa800d2e6778


ExtensionFlags (0x00000800)


Unknown flags 0x00000800


Device queue is not busy. –>没有发现“低层设备”,符合上面的分析


 


查看“低层设备”信息,即IRP所发往的设备


 


0: kd> !devobj poi(@rbx+0x18)


Device object (fffffa800d2e2380) is for:


\Driver\partmgr DriverObject fffffa800d1707c0  –>注意这里,与上面为同一个“驱动对象”


Current Irp 00000000 RefCount 0 Type 00000007 Flags 00000010


DevExt fffffa800d2e24d0 DevObjExt fffffa800d2e2778


ExtensionFlags (0x00000800)


Unknown flags 0x00000800


AttachedTo (Lower) fffffa800d7ff790 \Driver\Disk  –>发现partmgr的“低层设备”


Device queue is not busy.


 


至此分区设备partmgr的流程算是梳理完了,也验证了我们的假设。现在得到的StackSize是3,同时发现其“设备对象扩展”DEVOBJ_EXTENSION->AttachedTo指向了Disk“类驱动”(Class)的设备对象,接下来顺藤摸瓜,看能否定位到最终的“小端口驱动”(miniport)的设备对象。


(中间重启了几次,所以设备对象地址有变化)


 


0: kd> !devstack poi(@rbx+0x18)


!DevObj   !DrvObj            !DevExt   ObjectName


> fffffa800d2b3380  \Driver\partmgr    fffffa800d2b34d0


fffffa800d7bf790  \Driver\Disk       fffffa800d7bf8e0  DR0


fffffa800d1c1060  \Driver\LSI_SAS    fffffa800d1c11b0  000000a0


!DevNode fffffa800d15d6e0 :


DeviceInst is “SCSI\Disk&Ven_VMware_&Prod_VMware_Virtual_S\5&22be343f&0&000000″


ServiceName is “disk”


 


很明显“类驱动”->“小端口”可以通过“设备对象扩展”DEVOBJ_EXTENSION的AttachedTo域进行定位的,由此可以得出Disk的DR0设备的StackSize为2,LSI_SAS的000000a0设备的StackSize为1,要验证的话可以通过dt _DEVICE_OBJECT命令查看,前面已经给出过例子了,这里不再重复。


 


现在已将一个IRP从产生(自上而下)到消亡所历经的设备栈全部理清,需要说明的是系统发送IRP的流程是“自上而下”的,设备之间多以“设备扩展”DEVICE_EXTENSION的形式找到“低层设备”,而“设备栈”则是在引导过程中“自下而上”搭建起来的。也就是说,我们的跟踪流程并不代表是系统处理IRP的真实路径,这里主要是通用性与兼容性的需要,所以尽量不去使用自定义的“私有”结构,而是通过相对固定的域与结构来定位。下面是Windows 2003 Server (32bit) 在一台虚拟机和一台物理机上的分析对比:


 


在虚拟机服务器上,安装了第三方的磁盘类安全工具,我们来看一下具体情况


 


kd> !drvobj disk


Driver object (81bc4de8) is for:


\Driver\Disk


Driver Extension List: (id , addr)


(f99e33be 81bc4be8)


Device Object list:


81b75030 81bc8030


 


选择一个设备,查看其所在的“设备栈”


 


kd> !devstack 81bc8030


!DevObj !DrvObj !DevExt ObjectName


81bc87e0 \Driver\PartMgr 81bc8898


81bc89f8 \Driver\DeepFrz 81bc8ab0  –>这是第三方产品驱动,可以看到partmgr下面紧挨着的不再是Disk这个“类驱动”的设备


>81bc8030 \Driver\Disk 81bc80e8 DR0 –>当前磁盘设备


81bc08e8 \Driver\atapi 81bc09a0 IdeDeviceP0T0L0-3 –>可以看到下面就是miniport 驱动。


!DevNode 81bc07a0 :


DeviceInst is “IDE\DiskVMware_Virtual_IDE_Hard_Drive___________00000001\3030303030303030303030303030303030303130″


ServiceName is “disk”


 


就以上信息,如果要以“设备扩展”DEVICE_EXTENSION方式来定位“低层设备”,那么就需要分析第三方驱动DeepFrz。


 


 


再看下物理服务器上的情况,此系统是一个没有安装任何第三方驱动的系统:


 


 


lkd> !drvobj disk


Driver object (8a488a08) is for:


\Driver\Disk


Driver Extension List: (id , addr)


(b98ef338 8a4888f0)


Device Object list:


8a4f6c68 8a4a7c68 8a486c68 8a4f1c68


8a487ab8


 


选择一个设备,查看其所在的“设备栈”


 


lkd> !devstack 8a487ab8


!DevObj !DrvObj !DevExt ObjectName


8a4f8930 \Driver\PartMgr 8a4f89e8


>8a487ab8 \Driver\Disk 8a487b70 DR0 –>当前磁盘设备


8a4fb9e8 \Driver\ACPI 8a5037c0 00000070–>可以看到下面不是miniport驱动


8a4ab940 \Driver\atapi 8a4ab9f8 IdeDeviceP2T0L0-6


!DevNode 8a4669b8 :


DeviceInst is “IDE\DiskWDC_WD5000AAKX-001CA0___________________15.01H15\5&2f9ef5d6&0&0.0.0″


ServiceName is “disk”


 


查看此设备的详细信息


 


lkd> dt _DEVICE_OBJECT 8a487ab8


nt!_DEVICE_OBJECT


+0x000 Type : 3


+0x002 Size : 0x518


+0x004 ReferenceCount : 0


+0x008 DriverObject : 0x8a488a08 _DRIVER_OBJECT


+0x00c NextDevice : (null)


+0x010 AttachedDevice : 0x8a4f8930 _DEVICE_OBJECT


+0x014 CurrentIrp : (null)


+0x018 Timer : 0x8a503ca0 _IO_TIMER


+0x01c Flags : 0x50


+0x020 Characteristics : 0x100


+0x024 Vpb : 0x8a487a30 _VPB


+0x028 DeviceExtension : 0x8a487b70 –>设备扩展


+0x02c DeviceType : 7


+0x030 StackSize : 3 ” –>说明在DEVICE STACK中下面还有两个设备,也就是下层设备不见得就是MINIPORT DRIVER。


+0x034 Queue : __unnamed


+0x05c AlignmentRequirement : 1


+0x060 DeviceQueue : _KDEVICE_QUEUE


+0x074 Dpc : _KDPC


+0x094 ActiveThreadCount : 0


+0x098 SecurityDescriptor : 0xe150f500


+0x09c DeviceLock : _KEVENT


+0x0ac SectorSize : 0


+0x0ae Spare1 : 1


+0x0b0 DeviceObjectExtension : 0x8a487fd0 _DEVOBJ_EXTENSION


+0x0b4 Reserved : (null)


 


我们以该设备的“设备扩展”DEVICE_EXTENSION进行定位,这里有个前提,那就是disk.sys所依赖的classpnp.sys驱动是开源的,其“设备扩展”指向了COMMON_DEVICE_EXTENSION结构,其中第三个字段指向“低层设备”。(具体结构见WDK中的代码)


 


lkd> dd 0x8a487b70


8a487b70 00000003 8a487ab8 8a4fb9e8 8a487b70 –>第三个字段是“低层设备”


8a487b80 8a4888f0 00000001 00040001 00000000


8a487b90 8a487b90 8a487b90 ffffffff ffffffff


8a487ba0 00000000 8a487e38 0000ff07 00000000


8a487bb0 002c002a e153f528 8a4f1d20 00000000


8a487bc0 70c06000 00000074 00000000 00000000


8a487bd0 8a4888fc 00000002 00000001 00000000


8a487be0 00040001 00000001 8a487be8 8a487be8


 


查看此设备信息


 


lkd> !devobj 0x8a4fb9e8


Device object (8a4fb9e8) is for:


00000070 \Driver\ACPI DriverObject 8a4c9800 –>可以正确定位到disk的“低层设备”,即ACPI所创建的设备


Current Irp 00000000 RefCount 0 Type 00000032 Flags 00000050


Dacl e150f514 DevExt 8a5037c0 DevObjExt 8a4fbaa0


ExtensionFlags (0000000000)


AttachedDevice (Upper) 8a487ab8 \Driver\Disk


AttachedTo (Lower) 8a4ab940 \Driver\atapi


Device queue is not busy.


 


查看ACPI设备的详细信息


 


lkd> dt _DEVICE_OBJECT 0x8a4fb9e8


nt!_DEVICE_OBJECT


+0x000 Type : 3


+0x002 Size : 0xb8


+0x004 ReferenceCount : 0


+0x008 DriverObject : 0x8a4c9800 _DRIVER_OBJECT


+0x00c NextDevice : 0x8a518030 _DEVICE_OBJECT


+0x010 AttachedDevice : 0x8a487ab8 _DEVICE_OBJECT


+0x014 CurrentIrp : (null)


+0x018 Timer : (null)


+0x01c Flags : 0x50


+0x020 Characteristics : 0x180


+0x024 Vpb : (null)


+0x028 DeviceExtension : 0x8a5037c0 –>ACPI“设备扩展”地址


+0x02c DeviceType : 0x32


+0x030 StackSize : 2 ”


+0x034 Queue : __unnamed


+0x05c AlignmentRequirement : 1


+0x060 DeviceQueue : _KDEVICE_QUEUE


+0x074 Dpc : _KDPC


+0x094 ActiveThreadCount : 0


+0x098 SecurityDescriptor : 0xe150f500


+0x09c DeviceLock : _KEVENT


+0x0ac SectorSize : 0


+0x0ae Spare1 : 1


+0x0b0 DeviceObjectExtension : 0x8a4fbaa0 _DEVOBJ_EXTENSION


+0x0b4 Reserved : (null)


 


lkd> dd 0x8a5037c0


8a5037c0 00000040 05401001 5f534750 00000000


8a5037d0 b9794f8c 00000000 00000000 b979916a


8a5037e0 8a5037d4 8a4fb9e8 8a4ccaa0 00000000


8a5037f0 00000000 00000000 00000000 00000000


8a503800 00000000 00000000 00000000 00000000


8a503810 00000000 00000000 00000000 00000000


8a503820 00000000 00000000 00000000 00000000


8a503830 00000000 00000000 00000000 00000000


 


可以看到ACPI 的“设备扩展”DEVICE_EXTENSION以面目全非。如果要正确找到最终miniport还需要针对ACPI进行分析,而且也不保证其“低层设备”一定保存在这里。


 


通过对Windows 2008 R2 64BIT以及Windows 2003 32BIT两台服务器的分析,可以看出使用自定义结构和假定“驱动名称”或“设备名称”都存在着很大的不确定性,这也是在文章开始和分析partmgr时所提到的。但不得不说在定位volmgr-与partmgr的关系时我没有找到通用的方法,只有用特定偏移进行定位。


 


本文的分析是非常功利性的,手法也很粗暴,目的就是要实现一套通用的定位存储设备的代码,所以内容不涉及Windows驱动框架概念,如:LEGACY Driver、Pnp Manager、Bus Relations、PDO/FDO等。如果要想将内容与之对应,还需参考其他资料。


 


下面就是PoC代码:


 


 


#include “ntddk.h”


#include “string.h”


#include “stdio.h”


 


extern POBJECT_TYPE* IoDriverObjectType;


 


NTSYSAPI


NTSTATUS


NTAPI ObReferenceObjectByName( IN PUNICODE_STRING ObjectName,


IN ULONG Attributes,


IN PACCESS_STATE AccessState OPTIONAL,


IN ACCESS_MASK DesiredAccess OPTIONAL,


IN POBJECT_TYPE ObjectType,


IN KPROCESSOR_MODE AccessMode,


IN OUT PVOID ParseContext OPTIONAL,


OUT PVOID* Object );


 


VOID DriverUnload( IN PDRIVER_OBJECT DriverObject );


 


/////////////////////////////////////////////////////////////////


// 结构作用 : 系统对象操作使用


//


// 注意 : 以下结构是系统结构,来自 2000 源代码,2000-2008适用


//        2008 R2与WIN7此结构有变化


/////////////////////////////////////////////////////////////////


 


typedef struct _OBJECT_CREATE_INFORMATION


{


ULONG Attributes;


HANDLE RootDirectory;


PVOID ParseContext;


KPROCESSOR_MODE ProbeMode;


ULONG PagedPoolCharge;


ULONG NonPagedPoolCharge;


ULONG SecurityDescriptorCharge;


PSECURITY_DESCRIPTOR SecurityDescriptor;


PSECURITY_QUALITY_OF_SERVICE SecurityQos;


SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;


} OBJECT_CREATE_INFORMATION, * POBJECT_CREATE_INFORMATION;


 


typedef struct _OBJECT_HEADER


{


LONG PointerCount;


union


{


LONG HandleCount;


PSINGLE_LIST_ENTRY SEntry;


};


POBJECT_TYPE Type;


UCHAR NameInfoOffset;


UCHAR HandleInfoOffset;


UCHAR QuotaInfoOffset;


UCHAR Flags;


union


{


POBJECT_CREATE_INFORMATION ObjectCreateInfo;


PVOID QuotaBlockCharged;


};


 


PSECURITY_DESCRIPTOR SecurityDescriptor;


QUAD Body;


} OBJECT_HEADER, * POBJECT_HEADER;


 


#define NUMBER_HASH_BUCKETS 37


 


typedef struct _OBJECT_DIRECTORY


{


struct _OBJECT_DIRECTORY_ENTRY* HashBuckets[NUMBER_HASH_BUCKETS];


struct _OBJECT_DIRECTORY_ENTRY** LookupBucket;


BOOLEAN LookupFound;


USHORT SymbolicLinkUsageCount;


struct _DEVICE_MAP* DeviceMap;


} OBJECT_DIRECTORY, * POBJECT_DIRECTORY;


 


typedef struct _OBJECT_HEADER_NAME_INFO


{


POBJECT_DIRECTORY Directory;


UNICODE_STRING Name;


ULONG Reserved;


#if DBG


ULONG Reserved2 ;


LONG DbgDereferenceCount ;


#endif


} OBJECT_HEADER_NAME_INFO, * POBJECT_HEADER_NAME_INFO;


 


#define OBJECT_TO_OBJECT_HEADER( o ) \


CONTAINING_RECORD( (o), OBJECT_HEADER, Body )


 


#define OBJECT_HEADER_TO_NAME_INFO( oh ) ((POBJECT_HEADER_NAME_INFO) \


((oh)->NameInfoOffset == 0 ? NULL : ((PCHAR)(oh) – (oh)->NameInfoOffset)))


 


//


// WINDBG 中获得的结构,因为这里用不到Dope所以将其定义为PVOID


// ntddk.h 中已定义了DEVOBJ_EXTENSION结构,但是不完整,为了避免冲突


// 将名称加上 “REAL” 前缀,在2008,WIN7后在VPB域后扩充了两个字段,


// 但在这里我们用不上,所以此结构通用于所有系统


// added 2012.10.17


//


typedef struct _REAL_DEVOBJ_EXTENSION


{


SHORT Type;


USHORT Size;


PDEVICE_OBJECT DeviceObject;


ULONG PowerFlags;


PVOID Dope;


ULONG ExtensionFlags;


PVOID DeviceNode;


PDEVICE_OBJECT AttachedTo;


LONG StartIoCount;


LONG StartIoKey;


ULONG StartIoFlags;


PVPB Vpb;


} REAL_DEVOBJ_EXTENSION, *PREAL_DEVOBJ_EXTENSION;


 


//


// 逆向xp->2003的ftdisk.sys 以及2008与WIN7的volmgr.sys


// 所获得的结构,这里2008与WIN7的volmgr.sys偏移不同,为了更有益于


// 理解,定义了此通用结构,支持xp,2003,2008,WIN7


// added 2012.10.22


//


 


#ifdef _WIN64


typedef struct _VOLUME_DEVICE_EXTENSION


{


PDEVICE_OBJECT DeviceObject;


ULONG64 Unknown1;


ULONG64 Flags;


ULONG64 Unknown2;


PDEVICE_OBJECT DiskDeviceObject;            //在XP-2003中,此设备是disk.sys创建的


UCHAR UnBlock1[0xE8];


PDEVICE_OBJECT PartmgrDeviceObject_7;           //在WIN7中,此设备是partmgr创建,偏移0x110


UCHAR UnBlock2[0x10];


PDEVICE_OBJECT PartmgrDeviceObject_2008;//在2008中,此设备是partmgr创建,偏移0x120


}VOLUME_DEVICE_EXTENSION, *PVOLUME_DEVICE_EXTENSION;


#else


typedef struct _VOLUME_DEVICE_EXTENSION


{


PDEVICE_OBJECT DeviceObject;


ULONG Unknown1;


ULONG Flags;


ULONG Unknown2;


PDEVICE_OBJECT DiskDeviceObject;             //在XP-2003中,此设备是disk.sys创建的


UCHAR UnBlock1[0x95];


PDEVICE_OBJECT PartmgrDeviceObject_7;            //在WIN7中,此设备是partmgr创建,偏移0xa4


UCHAR UnBlock2[0x0C];


PDEVICE_OBJECT PartmgrDeviceObject_2008; //在2008中,此设备是partmgr创建,偏移0xb4


UCHAR UnBlock3[0x58];


}VOLUME_DEVICE_EXTENSION, *PVOLUME_DEVICE_EXTENSION;


#endif


 


 


RTL_OSVERSIONINFOW OsVerinfo;


 


typedef PVOID(NTAPI *pObQueryNameInfo)(IN PVOID Object);


pObQueryNameInfo ObQueryNameInfo;


 


/////////////////////////////////////////////////////////////////


// 函数类型 : 自定义工具函数


// 函数模块 : 设备栈信息模块


/////////////////////////////////////////////////////////////////


// 功能 : 从 DEVICE_OBJECT 中得到设备与驱动名称并打印地址


// 注意 : 函数功能只是打印信息,不同环境使用中应该会做修改


/////////////////////////////////////////////////////////////////


 


VOID


GetDeviceObjectInfo( PDEVICE_OBJECT DevObj )


{


POBJECT_HEADER ObjectHeader;


POBJECT_HEADER_NAME_INFO ObjectNameInfo;


UNICODE_STRING UnicodeName;


 


if ( DevObj == NULL )


{


DbgPrint( “DevObj is NULL!\n” );


return;


}


 


if (OsVerinfo.dwMajorVersion == 6 && OsVerinfo.dwMinorVersion == 1)


{


//


// 判断此 DEVICE_OBJECT 是否有名字


//


if (DevObj->Flags & DO_DEVICE_HAS_NAME)


{


RtlInitUnicodeString(&UnicodeName, L”ObQueryNameInfo”);


 


ObQueryNameInfo = (pObQueryNameInfo)MmGetSystemRoutineAddress(&UnicodeName);


if (ObQueryNameInfo == NULL)


{


DbgPrint( “pObQueryNameInfo is NULL!\n” );


return;


}


 


//


// WIN7 下此函数可直接获得设备名称


//


ObjectNameInfo = (POBJECT_HEADER_NAME_INFO)ObQueryNameInfo((PVOID)DevObj);


 


if ( ObjectNameInfo && ObjectNameInfo->Name.Buffer)


{


DbgPrint( “Driver Name:%wZ – Device Name:%wZ – Driver Address:0x%x – Device Address:0x%x\n”,


&DevObj->DriverObject->DriverName,


&ObjectNameInfo->Name,


DevObj->DriverObject,


DevObj );


}


}


else


{


DbgPrint( “Driver Name:%wZ – Device Name:%S – Driver Address:0x%x – Device Address:0x%x\n”,


&DevObj->DriverObject->DriverName,


L”NULL”,


DevObj->DriverObject,


DevObj );


}


 


return ;


}


 


//


// 非WIN7系统,利用相关结构得到对象头


//


ObjectHeader = OBJECT_TO_OBJECT_HEADER( DevObj );


 


if ( ObjectHeader )


{


//


// 查询设备名称并打印


//


ObjectNameInfo = OBJECT_HEADER_TO_NAME_INFO( ObjectHeader );


 


if ( ObjectNameInfo && ObjectNameInfo->Name.Buffer)


{


DbgPrint( “Driver Name:%wZ – Device Name:%wZ – Driver Address:0x%x – Device Address:0x%x\n”,


&DevObj->DriverObject->DriverName,


&ObjectNameInfo->Name,


DevObj->DriverObject,


DevObj );


}


 


//


// 对于没有名称的设备,则打印 NULL


//


else if ( DevObj->DriverObject )


{


DbgPrint( “Driver Name:%wZ – Device Name:%S – Driver Address:0x%x – Device Address:0x%x\n”,


&DevObj->DriverObject->DriverName,


L”NULL”,


DevObj->DriverObject,


DevObj );


}


}


}


 


/////////////////////////////////////////////////////////////////


// 函数类型 : 自定义工具函数


// 函数模块 : 设备栈信息模块


/////////////////////////////////////////////////////////////////


// 功能 : 遍历 DEVICE_OBJECT 中 AttachedDevice 域,得到所有附加


//        在此设备上的过滤驱动(Filter Driver)


// 注意 : 函数功能只是打印信息,不同环境使用中应该会做修改


/////////////////////////////////////////////////////////////////


 


VOID


GetUpperFilterDeviceInfo(PDEVICE_OBJECT DevObj)


{


PDEVICE_OBJECT DeviceObject = DevObj;


 


if (DeviceObject == NULL)


{


DbgPrint( “GetUpperFilterDeviceInfo(): — DevObj is NULL!\n” );


return;


}


 


DeviceObject = DeviceObject->AttachedDevice;


 


while (DeviceObject)


{


DbgPrint( “Upper Attached Driver Name:%wZ,Driver Address:0x%x,DeviceAddress:0x%x\n”,


&DeviceObject->DriverObject->DriverName,


DeviceObject->DriverObject,


DeviceObject );


 


DeviceObject = DeviceObject->AttachedDevice;


}


}


 


/////////////////////////////////////////////////////////////////


// 函数类型 : 自定义工具函数


// 函数模块 : 设备栈信息模块


/////////////////////////////////////////////////////////////////


// 功能 : 从 DEVICE_OBJECT 中 DEVOBJ_EXTENSION 域所指向的 AttachedTo


//        获得低层设备即 LowerFilter


//


// 注意 : 函数返回下层设备对象


/////////////////////////////////////////////////////////////////


 


VOID


GetLowerFilterDevice(PDEVICE_OBJECT DevObj, PDEVICE_OBJECT* LowerDeviceObject)


{


PDEVICE_OBJECT DeviceObject = DevObj;


PREAL_DEVOBJ_EXTENSION DevObjExt = NULL;


PDEVICE_OBJECT LowerDevObj  = NULL;


 


if (DeviceObject == NULL)


{


DbgPrint(“GetLowerFilterDeviceInfo(): — DevObj is NULL!\n”);


return ;


}


 


if (DeviceObject->DeviceObjectExtension == NULL)


{


DbgPrint(“GetLowerFilterDeviceInfo(): — DeviceObjectExtension is NULL!\n”);


return ;


}


 


DevObjExt   = (PREAL_DEVOBJ_EXTENSION)DeviceObject->DeviceObjectExtension;


LowerDevObj = ((PREAL_DEVOBJ_EXTENSION)DevObjExt)->AttachedTo;


 


if (LowerDevObj == NULL ||


LowerDevObj->Type != IO_TYPE_DEVICE ||


LowerDevObj->Size <=0)


{


DbgPrint(“GetLowerFilterDeviceInfo(): — AttachedTo is NULL!\n”);


return ;


}


 


DbgPrint( “Lower Attached Driver Name:%S,Driver Address:0x%x,DeviceAddress:0x%x\n”,


LowerDevObj->DriverObject->DriverName.Buffer,


LowerDevObj->DriverObject,


LowerDevObj );


 


*LowerDeviceObject = LowerDevObj;


}


 


/////////////////////////////////////////////////////////////////


// 函数类型 : 自定义工具函数


// 函数模块 : 设备栈信息模块


/////////////////////////////////////////////////////////////////


// 功能 : 从文件系统的 DEVICE_OBJECT 中 DEVOBJ_EXTENSION 域所指向的


//        VPB域得到卷 DEVICE_OBJECT


//


// 注意 : 返回卷设备对象,这里的设备栈已经不同


/////////////////////////////////////////////////////////////////


 


BOOLEAN


GetVolumeDevFromFs(PDEVICE_OBJECT DevObj, PDEVICE_OBJECT* VolumeDeviceObject)


{


PDEVICE_OBJECT DeviceObject = DevObj;


PREAL_DEVOBJ_EXTENSION DevObjExt = NULL;


PDEVICE_OBJECT VolumeDevObj  = NULL;


PVPB Vpb = NULL;


 


if ( DeviceObject == NULL )


{


DbgPrint(“GetVolumeDevFromFs(): — DevObj is NULL!\n”);


return FALSE;


}


 


if (DeviceObject->DeviceObjectExtension == NULL)


{


DbgPrint(“GetVolumeDevFromFs(): — DeviceObjectExtension is NULL!\n”);


return FALSE;


}


 


DevObjExt = (PREAL_DEVOBJ_EXTENSION)DeviceObject->DeviceObjectExtension;


Vpb = DevObjExt->Vpb;


 


if (Vpb == NULL ||


Vpb->Type != IO_TYPE_VPB ||


Vpb->Size <= 0 ||


Vpb->RealDevice == NULL)


{


DbgPrint(“GetVolumeDevFromFs(): — VPB ERROR!\n”);


return FALSE;


}


 


*VolumeDeviceObject = Vpb->RealDevice;


 


return TRUE;


}


 


/////////////////////////////////////////////////////////////////


// 函数类型 : 自定义工具函数


// 函数模块 : 设备栈信息模块


/////////////////////////////////////////////////////////////////


// 功能 : 从卷 DEVICE_OBJECT 中的 DEVICE_EXTENSION 中根据不同系统


//        利用自hacking结构获得下层存储类 DEVICE_OBJECT


//


// 注意 : 返回存储类设备对象,不同系统所指定的驱动不一致


/////////////////////////////////////////////////////////////////


 


BOOLEAN


GetDiskDevFromVolumeDevExt(PDEVICE_OBJECT DevObj, PDEVICE_OBJECT* StorageDeviceObject)


{


PDEVICE_OBJECT DeviceObject = DevObj;


 


PVOLUME_DEVICE_EXTENSION VolumeDevExt = NULL;


 


#ifdef DBG


KdBreakPoint();


#endif


 


if (DeviceObject == NULL)


{


DbgPrint(“DevObj is NULL!\n”);


return FALSE;


}


 


if (DevObj->DeviceExtension == NULL)


{


DbgPrint(“DeviceExtension is NULL!\n”);


return FALSE;


}


 


VolumeDevExt = (PVOLUME_DEVICE_EXTENSION)DevObj->DeviceExtension;


if (VolumeDevExt == NULL)


{


DbgPrint(“VolumeDevExt is NULL!\n”);


return FALSE;


}


 


if (VolumeDevExt->DeviceObject != DevObj || VolumeDevExt->Flags != 1)


{


DbgPrint(“No Volume Device!\n” );


return FALSE;


}


 


if (OsVerinfo.dwMajorVersion == 5)


{


if (VolumeDevExt->DiskDeviceObject == NULL)


{


DbgPrint(“DiskDeviceObject is NULL!\n”);


return FALSE;


}


 


if (VolumeDevExt->DiskDeviceObject->Type != IO_TYPE_DEVICE ||


VolumeDevExt->DiskDeviceObject->Size <=0)


{


DbgPrint(“DiskDeviceObject Type is NULL!\n”);


return FALSE;


}


 


*StorageDeviceObject = VolumeDevExt->DiskDeviceObject;


}


else if (OsVerinfo.dwMajorVersion == 6 && OsVerinfo.dwMinorVersion == 0)


{


if (VolumeDevExt->PartmgrDeviceObject_2008 == NULL)


{


DbgPrint(“DiskDeviceObject is NULL!\n”);


return FALSE;


}


 


if (VolumeDevExt->PartmgrDeviceObject_2008->Type != IO_TYPE_DEVICE ||


VolumeDevExt->PartmgrDeviceObject_2008->Size <=0)


{


DbgPrint(“PartmgrDeviceObject is ERROR!\n”);


return FALSE;


}


 


*StorageDeviceObject = VolumeDevExt->PartmgrDeviceObject_2008;


}


 


else if (OsVerinfo.dwMajorVersion == 6 && OsVerinfo.dwMinorVersion == 1)


{


 


if (VolumeDevExt->PartmgrDeviceObject_7 == NULL)


{


DbgPrint(“DiskDeviceObject is NULL!\n”);


return FALSE;


}


 


if (VolumeDevExt->PartmgrDeviceObject_7->Type != IO_TYPE_DEVICE ||


VolumeDevExt->PartmgrDeviceObject_7->Size <=0)


{


DbgPrint(“PartmgrDeviceObject is ERROR!\n”);


return FALSE;


}


 


*StorageDeviceObject = VolumeDevExt->PartmgrDeviceObject_7;


}


 


return TRUE;


}


 


/////////////////////////////////////////////////////////////////


// 函数类型 : 自定义工具函数


// 函数模块 : 设备栈信息模块


/////////////////////////////////////////////////////////////////


// 功能 : 通过驱动名称得到 DRIVER_OBJECT 驱动对象,并找出其上


//        建立的所有设备


// 注意 :


/////////////////////////////////////////////////////////////////


 


BOOLEAN


EnumDeviceStack( PWSTR pwszDeviceName )


{


UNICODE_STRING DriverName;


PDRIVER_OBJECT DriverObject = NULL;


PDEVICE_OBJECT DeviceObject = NULL;


 


PDEVICE_OBJECT VolumeDeviceObject = NULL;


PDEVICE_OBJECT DiskDeviceObject   = NULL;


PDEVICE_OBJECT LowerDeviceObject  = NULL;


 


RtlInitUnicodeString(&DriverName, pwszDeviceName);


 


ObReferenceObjectByName(&DriverName,


OBJ_CASE_INSENSITIVE,


NULL,


0,


*IoDriverObjectType,


KernelMode,


NULL,


&DriverObject


);


 


if (DriverObject == NULL)


{


return FALSE;


}


 


DeviceObject = DriverObject->DeviceObject;


 


//


// 这里使用 StackSize 判断是否到了最下层的 MINIPORT 驱动


// 循环中的顺序经过安排,不可随意改变


//


while (DeviceObject && DeviceObject->StackSize > 1)


{


GetDeviceObjectInfo( DeviceObject );


 


//


// 判断当前设备上是否有上层过滤驱动(Upper Filter Driver)


//


if ( DeviceObject->AttachedDevice )


{


GetUpperFilterDeviceInfo( DeviceObject );


}


 


//


// 判断当前设备上是否有下层过滤驱动(Lower Filter Driver)


// added 2012.10.17


//


GetLowerFilterDevice(DeviceObject, &LowerDeviceObject);


if ( LowerDeviceObject != NULL )


{


GetDeviceObjectInfo( LowerDeviceObject );


DeviceObject = LowerDeviceObject;


continue;


}


 


//


// 如果是文件系统则获得其卷设备


// added by 2012.10.22


//


if (GetVolumeDevFromFs(DeviceObject, &VolumeDeviceObject))


{


DeviceObject = VolumeDeviceObject;


continue;


}


 


//


// 如果是卷设备则获得存储类设备


// added by 2012.10.25


//


if (GetDiskDevFromVolumeDevExt(DeviceObject, &DiskDeviceObject))


{


DeviceObject = DiskDeviceObject;


continue;


}


 


//


// 得到建立在此驱动上的下一个设备 DEVICE_OBJECT


// 利用这个赋值,避免了不同系统中存储类驱动 DEVICE_EXTENSION 自定义


// 的差异,如partmgr的设备就是通过这里进行定位的


//


DeviceObject = DeviceObject->NextDevice;


}


 


ObDereferenceObject(DriverObject);


 


return TRUE;


}


 


VOID


DriverUnload( IN PDRIVER_OBJECT DriverObject )


{


 


DbgPrint(“Unload\n”);


return;


}


 


NTSTATUS


DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath )


{


 


BOOLEAN bReturn = FALSE;


 


DriverObject->DriverUnload = DriverUnload;


 


DbgPrint(“Entry\n”);


 


//__asm{int 3};


 


OsVerinfo.dwOSVersionInfoSize = sizeof(OsVerinfo);


 


PsGetVersion(&OsVerinfo.dwMajorVersion,


&OsVerinfo.dwMinorVersion,


NULL,


NULL


);


 


//


// 这里使用字符串仅是为了测试,实际应用时将代码放到minifilter中,连NTFS名字特征


// 都可以省略


//


bReturn = EnumDeviceStack( L”\\FileSystem\\Ntfs” );


 


return STATUS_SUCCESS;


}


作者:椒图实验室


转载请注明出处:http://blog.jowto.com