上面给出了RecognizerFSControl的全部代码,这个函数用于处理 IRP_MJ_FILE_SYSTEM_CONTROL 请求。这个函数总是在 PASSIVE_LEVEL 上被调用。这个函数处理两个次功能码,分别是 IRP_MN_MOUNT_VOLUME 和IRP_MN_LOAD_FILE_SYSTEM。当有一个卷被装配时,IRP_MN_MOUNT_VOLUME请求被发送。IRP_MN_LOAD_FILE_SYSTEM 在文件系统驱动程序必须被加载时调用。
在本文的前面部分,我们已经描述了在一个没有被装配的物理磁盘卷被访问时IO管理器如何调用所有注册了的文件系统。每一个文件系统驱动程序都有机会去分析卷直到一个驱动程序声明可以识别此卷。所以文件系统识别器在这里收到一个装配请求,文件系统识别器调用Recognizer _DetectFileSystem函数,这个函数将在稍候被描述。如果该函数返回STATUS_SUCCESS,说明该卷该文件系统识别器的文件系统驱动识别。接着返回STATUS_FS_DRIVER_REQUIRED给IO管理器。这个错误码被IO管理器特殊对待,因为对文件系统识别器的支持是建立在IO管理器的装配过程。一旦收到此IRP,IO管理器便发送一个新的IO请求给文件系统识别器。这一次发送的次功能码IRP_MN_LOAD_FILE_SYSTEM。识别器用ZwLoadDriver函数处理这个请求。该函数唯一的参数是需要加载的驱动的注册表项。如果加载驱动成功,ZwLoadDriver返回STATUS_SUCCESS,识别器可以反注册和删除识别器设备对象,这将导致识别器驱动程序被卸载。从该文件系统被注册开始,这是它第一次被调用。
当整个文件系统被加载后,IO管理器将和文件系统程序再进行一次装配请求。因为整个文件系统被加载了,识别器已经不再需要了。随后的请求将被文件系统驱动程序处理。 //
// RecognizerDetectFileSystem
//
// 用户需要根据自己文件系统的不同改变这些代码
// 输入参数:
// Irp – 请求装配卷的IRP.
//
// 输出参数:
// None.
//
// Returns:
// STATUS_SUCCESS – 这是我们的驱动可以识别的文件系统
// Other - I/O error
//
// Notes:
// None.
//
static NTSTATUS RecognizerDetectFileSystem(PIRP Irp)
{
NTSTATUS code =STATUS_SUCCESS;
DISK_GEOMETRY diskGeometry;
PARTITION_INFORMATION partitionInfo;
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
PVPB vpb = irpSp->Parameters.MountVolume.Vpb;
PDEVICE_OBJECT mediaDeviceObject = irpSp->Parameters.MountVolume.DeviceObject;
unsigned char* pBuffer = NULL;
//首先构造一个可以获得卷的分区表等信息的IRP,用RecognizerIoControl传递到下层驱动。
// IF error{
// return error
//}
// else
// {
//用RecognizerReadDiskSector 读卷参数块(VPB)}
// If read error{
// return error
//}
// else
// {检查文件系统的标识符,如果不匹配
// return error
//}
// else
// return code STATUS_SUCCESS;
// endif
// endif
// endif
//
// 现在返回错误码
code = STATUS_UNRECOGNIZED_VOLUME;
return code;
}
上面的代码显示RecognizerDetectFileSystem的全部内容。它负责实际的的卷的类型的检测。因为它的实现依赖于实际的物理卷。所以我们只提供一个通用的方法。你需要修改使得它适用于实际的卷。
这个例程需要实现两个关键的功能,一个是从存储介质读取数据,另外一个是从存储介质中获得关于介质的关键信息。这些功能被两个函数封装了,第一个是RecogizerReadDiskSector()从物理卷中读取用于分析的数据。第二个函数RecognizerIoControl(…)用于向下层设备发送请求,这些请求返回关于物理介质的信息,例如每一个扇区多少个字节。IOCTL_DISK_GET_PARTITION_ INFORMATION用于返回已经被装配了的卷的信息。NT文件系统用分区信息用作匹配签名算法的一部分。注意RecognizerReadDiskSector()函数用的是相对于分区的扇区数而不是相对于磁盘的扇区数。所以如果你传入参数0,你将得到的是分区的第一个扇区,而不是磁盘的第一个分区。换句话说,如果分区从第540扇区开始,用0做参数调用函数,你得到的是相对于分区为0,而相对于磁盘为549的扇区数据。. //
// RecognizerIoControl
//
// 这个函数用于发送特定的IRP个下层驱动
//
// 输出参数:
// MediaHandle – 特定设备的卷句柄
// Offset – 读请求的逻辑偏移量
// Length – 读的长度
// MDL – 数据将要被拷贝的MDL链t
//
// 输出参数:
// None.
//
// Returns:
// STATUS_SUCCESS - I/O completed successfully
// Other - I/O error
//
// Notes:
// None.
//
static NTSTATUS RecognizerIoControl( IN PDEVICE_OBJECT deviceObject,
IN ULONG IoctlCode,
IN PVOID InputBuffer,
IN ULONG InputBufferSize,
OUT PVOID OutputBuffer,
OUT ULONG OutputBufferSize)
{
PIRP irp;
NTSTATUS code;
KEVENT event;
IO_STATUS_BLOCK iosb;
// 初始化用于等待操作成功完成的事件对象
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
// 创建请求
//
irp = IoBuildDeviceIoControlRequest(IoctlCode,
deviceObject,
InputBuffer,
InputBufferSize,
OutputBuffer,
OutputBufferSize,
FALSE,
&event,
&iosb);
// 向下层驱动发送IRP
code = IoCallDriver(deviceObject, irp);
//如果可能的话,我们必须等待。注意,我们不接受异步过程调用。在IO操作完成之前,我们不能返回。
if (code == STATUS_PENDING) {
(void) KeWaitForSingleObject(&event, Executive, KernelMode,
TRUE, 0);
code = iosb.Status;
}
//
// 设置最终的输出缓冲区大小.
//
OutputBufferSize = iosb.Information;
//
// 完成
//
return(code);
}
//
//
// RecognizerReadDiskSector
//
// 从一个特定的卷读取一个扇区
//
// 输入参数:
// pDeviceObject –磁盘设备对象的指针.
// DiskSector – 需要读取的扇区数目.
//
// Outputs:
// Buffer – 用于接收读取内容的缓冲区.
//
// Returns:
// Returns TRUE if the disk sector was read.
//
// Notes:
// None.
//
static BOOLEAN RecognizerReadDiskSector(
IN PDEVICE_OBJECT pDeviceObject,
IN ULONG DiskSector,
IN UCHAR* Buffer // must be DEVICE_LOGICAL_BLOCKSIZE bytes long.)
{
LARGE_INTEGER sectorNumber;
PIRP irp;
IO_STATUS_BLOCK ioStatus;
KEVENT event;
NTSTATUS status;
ULONG sectorSize;
PULONG mbr;
PAGED_CODE();
sectorNumber.QuadPart = (LONGLONG) DiskSector *DEVICE_LOGICAL_BLOCKSIZE;
//创建一个用于检查是否完成通知事件对象
KeInitializeEvent(&event, NotificationEvent, FALSE);
// 获得扇区大小
sectorSize = DEVICE_LOGICAL_BLOCKSIZE;
// 分配内存.
mbr = ExAllocatePool(NonPagedPoolCacheAligned, sectorSize);
if (!mbr) {
return FALSE;
}
// 创建读MBR的IRP
irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
pDeviceObject,
mbr,
sectorSize,
§orNumber,
&event,
&ioStatus );
if (!irp) {
ExFreePool(mbr);
return FALSE;
}
// 把IRP传送个端口驱动程序
status = IoCallDriver(pDeviceObject, irp);
if (status == STATUS_PENDING) {
KeWaitForSingleObject(&event,
Suspended,
KernelMode,
FALSE,
NULL);
status = ioStatus.Status;
}
if (!NT_SUCCESS(status)) {
ExFreePool(mbr);
return FALSE;
}
//
// 返回读取的扇区信息
//
RtlCopyMemory(Buffer,mbr,sectorSize);
ExFreePool(mbr);
return TRUE;
}
上面的代码包含了RecognizerIoControl()和RecognizerReadDiskSector(…)的实现。和所有的代码一样,他们是自解释的。因为该函数运行在PASSIVE_LEVEL 级别的IRQL上,所以可以调用KeWaitForSingleObject()函数去等待相关操作完成。 (编辑:aniston)
|