在第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 ); // // 遍历由IoEnumerateDeviceObjectList返回的数组,如果需要,则绑定他们。 // for (i=0; i < numDevices; i++) { devName->Name.Length = 0; storageStackDeviceObject = 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) || 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 ); if (!NT_SUCCESS( status )) { storageStackDeviceObject = NULL; // // 是一个磁盘设备对象,创建新的设备对象,准备绑定。 // status = IoCreateDevice( gFileSpyDriverObject, if (!NT_SUCCESS( status )) { SPY_LOG_PRINT( SPYDEBUG_ERROR, // // 初始化设备拓展的头结构体,主要完成如下功能:设置AttachedToDeviceObject成员为NULL, // 保存当前新创建的设备对象,上面获取到的设备堆栈栈底的设备对象。函数:NLInitDeviceExtensionHeader中完成 // newDevExt = newDeviceObject->DeviceExtension; NLInitDeviceExtensionHeader( &newDevExt->NLExtHeader, newDevExt->Flags = 0; // // 初始化用户名称缓冲区 // RtlInitEmptyUnicodeString( &newDevExt->UserNames, NULL, 0 ); #if WINVER >= 0x0600 // // 获取设备名称,重复利用NAME_CONTROL // devName->Name.Length = 0; status = NLGetObjectName( storageStackDeviceObject, if (!NT_SUCCESS( status )) { leave; // // 将名称复制到设备拓展中,因为这些内存从非分页内存中开辟的,所以他们一直都是可用的。 // 函数:NLAllocateAndCopyUnicodeString // 1:从非分页内存中开辟空间。 // 2:将设备名称拷入这段空间。 // status = NLAllocateAndCopyUnicodeString( &newDevExt->NLExtHeader.DeviceName, if (!NT_SUCCESS(status)) { leave; // // 在绑定最后的时候,再测试下,该设备是否被绑定过。 // 这次测试加了一个锁。如果没被绑定,则执行下面的绑定过程,否则,直接返回。 // ExAcquireFastMutex( &gSpyAttachLock ); if (SpyIsAttachedToDevice( devList[i], NULL )) { leave; // // 绑定这个设备。 // 函数:SpyAttachToMountedDevice将在下面将。这个函数的功能就是完成绑定一个文件系统卷设备的操作。 // status = SpyAttachToMountedDevice( devList[i], // // 处理错误,继续循环处理。 // if (!NT_SUCCESS( status )) { leave; // // 成功绑定之后,清楚初始化的flag,DO_DEVICE_INITIALIZING。 // 允许其他驱动绑定我们的设备对象。 // ClearFlag( newDeviceObject->Flags, DO_DEVICE_INITIALIZING ); // // 释放锁。 // ExReleaseFastMutex( &gSpyAttachLock ); // // 获取设备的DOS名称,这个操作不在上面完成,是因为加了锁。 // NLGetDosDeviceName( newDeviceObject, // // 设置新生成的设备对象为NULL,后面就不会释放这个指针,这个新设备对象已经保存到了设备拓展里面。 // 见函数:NLInitDeviceExtensionHeader // newDeviceObject = NULL; } finally { if (hasLock) { ExReleaseFastMutex( &gSpyAttachLock ); // // 减少设备对象的计数,这个计数有函数IoGetDiskDeviceObject增加的。 // 成功绑定后,就减少该设备对象的计数。一旦成功绑定到devList[i],I/O管理器会确定设备栈的下层设备, // 会一直存在,一直到这个文件系统栈被卸掉。 // if (storageStackDeviceObject != NULL) { ObDereferenceObject( storageStackDeviceObject ); // // 新创建的设备对象,如果还存在,说明绑定失败,直接清除其初始化内容,删除该新建的设备。 // if (newDeviceObject != NULL) { SpyCleanupMountedDevice( newDeviceObject ); // // 减少设备对象的计数,这个计数有函数IoEnumerateDeviceObjectList增加的。 // ObDereferenceObject( devList[i] ); NLFreeNameControl( devName, &gFileSpyNameBufferLookasideList ); SkipAttach: // // 忽略所有接受到的错误,如果得到一个错误,就只是简单的放弃绑定卷设备。 // status = STATUS_SUCCESS; // // 释放空间,返回。 // ExFreePoolWithTag( devList, FILESPY_POOL_TAG ); return status; }
}
goto SkipAttach;
}
newDeviceObject = NULL;
(devList[i]->DeviceType != FSDeviceObject->DeviceType) ||
SpyIsAttachedToDevice( devList[i], NULL )) {
}
}
status = (gSpyDynamicFunctions.GetStorageStackDeviceObject)( devList[i],
&storageStackDeviceObject );
leave;
}
sizeof( FILESPY_DEVICE_EXTENSION ),
(PUNICODE_STRING) NULL,
devList[i]->DeviceType,
0,
FALSE,
&newDeviceObject );
("FileSpy!SpyEnumberateFileSystemVolumes: Error creating volume device object, status=%08x\n",
status) );
newDeviceObject = NULL;
leave;
}
newDeviceObject,
storageStackDeviceObject );
InitializeListHead( &newDevExt->TxListHead );
ExInitializeFastMutex( &newDevExt->TxListLock );
#endif
devName );
}
&devName->Name,
FILESPY_DEVNAME_TAG );
}
hasLock = TRUE;
}
newDeviceObject );
}
hasLock = FALSE;
&newDevExt->NLExtHeader );
}
}
IoDeleteDevice( newDeviceObject );
}
}
}