读源码笔记--文件过滤驱动FileSpy第3篇 -- 绑定VDO

在第2篇已经看到,SpyFsNotification中成功绑定了文件系统的控制设备对象CDO,然后判断编译版本时,如果是XP及以后的OS版本,就直接枚举文件系统下的所有的已经挂载了的卷设备,并绑定他们。

 

在看函数SpyEnumerateFileSystemVolumes之前,复习前面2篇的过程。

DriverEntry里面主要做4件事:

1:创建设备,该设备用来与应用层程序通信和修改驱动内部配置所用。

2:初始化DriverObject的派遣例程和FastIO,判断编译版本,决定是否注册FS_FILTER_CALLBACKS的过滤回调。

3:注册一个文件系统变动回调函数。IoRegisterFsRegistrationChange实现。

4:初始化一些全局变量,内存,锁等。

IoRegisterFsRegistrationChange变动回调里的函数主要做下面的事:

1:根据设备对象DeviceObject获取设备对象名。

2:调用绑定文件系统控制设备函数SpyAttachToFileSystemDevice或解除绑定文件系统控制设备函数SpyDetachFromFileSystemDevice

SpyAttachToFileSystemDevice主要做如下事:

1:根据DeviceObject的成员DriverObject获取驱动名称,判断是否是文件系统的识别器。

2:创建设备,并绑定文件系统控制设备对象CDO。

3:如果是XP以后的OS,就调用SpyEnumerateFileSystemVolumes 枚举所有的卷设备,并绑定他们。

 

总结下思路为:

先绑定文件系统控制设备对象CDO,然后绑定卷设备。什么时候绑定文件系统控制设备对象呢?就是在文件系统变动回调函数里面,当注册了文件系统变动回调函数后,文件系统将被重新激活,这时变动回调将被调用,而变动回调函数的DeviceObject参数,就是指的文件系统控制设备对象,也就是我们要绑定的设备对象。

步骤:

1:设置变动回调函数。在变动回调里面绑定文件系统控制设备对象CDO。

2:绑定卷设备。

 

现在来看看这个枚举卷设备,并绑定卷设备的函数SpyAttachDeviceToDeviceStack(fspyLib.c中)是怎么实现的。

NTSTATUS
SpyEnumerateFileSystemVolumes (
    __in PDEVICE_OBJECT FSDeviceObject
    )

{

PDEVICE_OBJECT newDeviceObject;
PFILESPY_DEVICE_EXTENSION newDevExt;
PDEVICE_OBJECT *devList;
PDEVICE_OBJECT storageStackDeviceObject;
NTSTATUS status;
PNAME_CONTROL devName;
ULONG numDevices;
ULONG i;
BOOLEAN hasLock = FALSE;

PAGED_CODE();

 

//

//  调用IoEnumerateDeviceObjectList获取DriverObject下有多少个DeviceObject,便于后面开辟内存空间。返回状态为

//  STATUS_BUFFER_TOO_SMALL,如果没有得到错误的返回码,说明这里没有设备需要返回。

//  gSpyDynamicFunctions是存放动态函数地址的全家变量。

//

    ASSERT( NULL != gSpyDynamicFunctions.EnumerateDeviceObjectList );
    status = (gSpyDynamicFunctions.EnumerateDeviceObjectList)(
                                    FSDeviceObject->DriverObject,
                                    NULL,
                                    0,
                                    &numDevices );

    if (NT_SUCCESS( status )) {

        return status;
    }

    ASSERT(STATUS_BUFFER_TOO_SMALL == status);

//

//  为已知的设备开辟内存空间进行存储。额外增加8字节。

// 

    numDevices += 8;        //grab a few extra slots

    devList = ExAllocatePoolWithTag( NonPagedPool,
                                     (numDevices * sizeof(PDEVICE_OBJECT)),
                                     FILESPY_POOL_TAG );
    if (NULL == devList) {

        return STATUS_INSUFFICIENT_RESOURCES;
    }

//

//  调用IoEnumerateDeviceObjectList获取DriverObject下的设备链中的详细信息。返回PDEVICE_OBJECT的数组。

//

    status = (gSpyDynamicFunctions.EnumerateDeviceObjectList)(
                    FSDeviceObject->DriverObject,
                    devList,
                    (numDevices * sizeof(PDEVICE_OBJECT)),
                    &numDevices );

    if (!NT_SUCCESS( status ))  {

        ExFreePoolWithTag( devList, FILESPY_POOL_TAG );
        return status;
    }

//

//  开辟空间存放设备名称

//

    status = NLAllocateNameControl( &devName, &gFileSpyNameBufferLookasideList );

    if (!NT_SUCCESS( status )) {

//

//  减少设备对象的计数,设备对象计数的增加,是由于调用了IoEnumerateDeviceObjectList。该函数会增加设备对象的计数。

//

        for (i=0; i

            ObDereferenceObject( devList[i] );
        }

        ExFreePoolWithTag( devList, FILESPY_POOL_TAG );
        goto SkipAttach;
    }

 

//

//  遍历由IoEnumerateDeviceObjectList返回的数组,如果需要,则绑定他们。

//

    for (i=0; i < numDevices; i++) {

        devName->Name.Length = 0;

        storageStackDeviceObject = NULL;
        newDeviceObject = NULL;

        try {

//

//  下面3种情况不绑定:

//  1:是本设备,因为本函数是在文件系统变动回调里面调用。本函数的参数FSDeviceObject就是变动回调函数里面的DeviceObject,

//        该参数是一个文件系统控制设备对象CDO。

//  2:与本设备类型不符合。

//  3:已经被绑定过的设备。

//   函数SpyIsAttachedToDevice就是判断是否被绑定过,返回值:TRUE,表示绑定过;FASLE,表示未绑定。

//  SpyIsAttachedToDevice思路比较简单,进入该函数后,判断操作系统版本,XP以后调用:SpyIsAttachedToDeviceWXPAndLater

//  2K以前调用:SpyIsAttachedToDeviceW2K

//

//  函数:SpyIsAttachedToDeviceWXPAndLater

//            1:调用IoGetAttachedDeviceReference获取设备栈顶的设备对象。

//            2:利用宏IS_FILESPY_DEVICE_OBJECT来判断这个设备对象DeviceObject是否是FileSpy的设备。是返回TRUE。

//            3:不是,则调用IoGetLowerDeviceObject,获取1中设备的下一层设备(同一个设备堆栈)。

//            4:跳至2步。

//  函数:SpyIsAttachedToDeviceW2K

//            1:利用宏IS_FILESPY_DEVICE_OBJECT来判断这个设备对象DeviceObject是否是FileSpy的设备。是返回TRUE。

//            2:不是,则利用DEVICE_OBJECT的成员:AttachedDevice来获取绑定到当前这个设备对象的过滤驱动的设备对象。

//            3:跳至1步。

//

            if ((devList[i] == FSDeviceObject) ||
                (devList[i]->DeviceType != FSDeviceObject->DeviceType) ||
                SpyIsAttachedToDevice( devList[i], NULL )) {

                leave;
            }

// 

//  驱动这个设备是否有名称,如果有名称,或者名称获取失败,则不绑定。

//  有名称则说明是文件系统控制设备对象CDO。

//  函数:SpyGetBaseDeviceObjectName

//             调用IoGetDeviceAttachmentBaseRef来获取设备栈底的设备对象,然后调用ObQueryNameString来获取设备的名称。

//     注意:函数IoGetDeviceAttachmentBaseRef是动态的,存于全局变量:gSpyDynamicFunctions中的。

//

            status = SpyGetBaseDeviceObjectName( devList[i], devName );

            if (!NT_SUCCESS(status) || (devName->Name.Length > 0)) {

                leave;
            }

// 

//  获取一个与当前这个文件系统设备对象有关的磁盘设备对象,只绑定已经拥有一个磁盘设备对象的文件系统设备对象。

//  这里调用IoGetDiskDeviceObject来获取一个与文件系统设备对象有关的磁盘设备对象。

//     注意:函数IoGetDiskDeviceObject是动态的,存于全局变量:gSpyDynamicFunctions中的。

//

            ASSERT( NULL != gSpyDynamicFunctions.GetStorageStackDeviceObject );
            status = (gSpyDynamicFunctions.GetStorageStackDeviceObject)( devList[i],
                                                                         &storageStackDeviceObject );

            if (!NT_SUCCESS( status )) {

                storageStackDeviceObject = NULL;
                leave;
            }

// 

// 是一个磁盘设备对象,创建新的设备对象,准备绑定

//

            status = IoCreateDevice( gFileSpyDriverObject,
                                 sizeof( FILESPY_DEVICE_EXTENSION ),
                                 (PUNICODE_STRING) NULL,
                                 devList[i]->DeviceType,
                                 0,
                                 FALSE,
                                 &newDeviceObject );

            if (!NT_SUCCESS( status )) {

                SPY_LOG_PRINT( SPYDEBUG_ERROR,
                               ("FileSpy!SpyEnumberateFileSystemVolumes: Error creating volume device object, status=%08x\n",
                                status) );
                newDeviceObject = NULL;
                leave;
            }

// 

//  初始化设备拓展的头结构体,主要完成如下功能:设置AttachedToDeviceObject成员为NULL,

//  保存当前新创建的设备对象,上面获取到的设备堆栈栈底的设备对象。函数:NLInitDeviceExtensionHeader中完成

//

            newDevExt = newDeviceObject->DeviceExtension;

            NLInitDeviceExtensionHeader( &newDevExt->NLExtHeader,
                                         newDeviceObject,
                                         storageStackDeviceObject );

            newDevExt->Flags = 0;

// 

//  初始化用户名称缓冲区

//

            RtlInitEmptyUnicodeString( &newDevExt->UserNames, NULL, 0 );

#if WINVER >= 0x0600
            InitializeListHead( &newDevExt->TxListHead );
            ExInitializeFastMutex( &newDevExt->TxListLock );
#endif

// 

//  获取设备名称,重复利用NAME_CONTROL

//

            devName->Name.Length = 0;

            status = NLGetObjectName( storageStackDeviceObject,
                                      devName );

            if (!NT_SUCCESS( status )) {

                leave;
            }

// 

//  将名称复制到设备拓展中,因为这些内存从非分页内存中开辟的,所以他们一直都是可用的。

//  函数:NLAllocateAndCopyUnicodeString

//            1:从非分页内存中开辟空间。

//            2:将设备名称拷入这段空间。

//

            status = NLAllocateAndCopyUnicodeString( &newDevExt->NLExtHeader.DeviceName,
                                                     &devName->Name,
                                                     FILESPY_DEVNAME_TAG );

            if (!NT_SUCCESS(status)) {

                leave;
            }

// 

//  在绑定最后的时候,再测试下,该设备是否被绑定过。

//  这次测试加了一个锁。如果没被绑定,则执行下面的绑定过程,否则,直接返回。

//

            ExAcquireFastMutex( &gSpyAttachLock );
            hasLock = TRUE;

            if (SpyIsAttachedToDevice( devList[i], NULL )) {

                leave;
            }

//

//  绑定这个设备。

//  函数:SpyAttachToMountedDevice将在下面将。这个函数的功能就是完成绑定一个文件系统卷设备的操作。

//

            status = SpyAttachToMountedDevice( devList[i],
                                               newDeviceObject );

//

//  处理错误,继续循环处理。

//

            if (!NT_SUCCESS( status )) {

                leave;
            }

//

//  成功绑定之后,清楚初始化的flag,DO_DEVICE_INITIALIZING。

//  允许其他驱动绑定我们的设备对象。

//

            ClearFlag( newDeviceObject->Flags, DO_DEVICE_INITIALIZING );

//

//  释放锁。

//

            ExReleaseFastMutex( &gSpyAttachLock );
            hasLock = FALSE;

//

//  获取设备的DOS名称,这个操作不在上面完成,是因为加了锁。

//

            NLGetDosDeviceName( newDeviceObject,
                                &newDevExt->NLExtHeader );

//

//  设置新生成的设备对象为NULL,后面就不会释放这个指针,这个新设备对象已经保存到了设备拓展里面。

//  见函数:NLInitDeviceExtensionHeader

//

            newDeviceObject = NULL;

        } finally {

            if (hasLock) {

                ExReleaseFastMutex( &gSpyAttachLock );
            }

//

//  减少设备对象的计数,这个计数有函数IoGetDiskDeviceObject增加的。

//  成功绑定后,就减少该设备对象的计数。一旦成功绑定到devList[i],I/O管理器会确定设备栈的下层设备,

//  会一直存在,一直到这个文件系统栈被卸掉。

//

            if (storageStackDeviceObject != NULL) {

                ObDereferenceObject( storageStackDeviceObject );
            }

//

//  新创建的设备对象,如果还存在,说明绑定失败,直接清除其初始化内容,删除该新建的设备。

//

            if (newDeviceObject != NULL) {

                SpyCleanupMountedDevice( newDeviceObject );
                IoDeleteDevice( newDeviceObject );
            }

//

//  减少设备对象的计数,这个计数有函数IoEnumerateDeviceObjectList增加的。

//

            ObDereferenceObject( devList[i] );
        }
    }

    NLFreeNameControl( devName, &gFileSpyNameBufferLookasideList );

SkipAttach:

//

//  忽略所有接受到的错误,如果得到一个错误,就只是简单的放弃绑定卷设备。

//

    status = STATUS_SUCCESS;

//

//  释放空间,返回。

//

    ExFreePoolWithTag( devList, FILESPY_POOL_TAG );

    return status;

}

你可能感兴趣的:(c++)