_ _ (_) | | __ ____ __ _ _ _ _ __ ___ _ __ _ __ ___ | |_ \ \ / /\ \/ /| || | | || '_ ` _ \ | '_ \ | '_ \ / _ \| __| \ V / > < | || |_| || | | | | || |_) |_ | | | || __/| |_ \_/ /_/\_\| | \__,_||_| |_| |_|| .__/(_)|_| |_| \___| \__| _/ | | | |__/ |_| /---------------------------------------------------------------------------------------\ |>...................[ 文件系统识别器的初步研究(一) ]...............<| |>......................[ by 十二羽翼/vxjump.net ]..................<| |>......................[ 2010-09-25 ]..................<| \>...................... [ QQ:764439262 ] ..................MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = FsRecFsControl; DriverObject->MajorFunction[IRP_MJ_CREATE] = FsRecCreate; DriverObject->MajorFunction[IRP_MJ_CLEANUP] = FsRecCleanupClose; DriverObject->MajorFunction[IRP_MJ_CLOSE] = FsRecCleanupClose; DriverObject->DriverUnload = FsRecUnload; FsRecLoadSync = ExAllocatePoolWithTag( NonPagedPool, sizeof(KEVENT), FSREC_POOL_TAG ); if (FsRecLoadSync == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } KeInitializeEvent( FsRecLoadSync, SynchronizationEvent, TRUE ); // // Create and initialize each of the file system driver type device // objects. // status = FsRecCreateAndRegisterDO( DriverObject, NULL, NULL, L"\\Cdfs", L"\\FileSystem\\CdfsRecognizer", CdfsFileSystem, FILE_DEVICE_CD_ROM_FILE_SYSTEM ); if (NT_SUCCESS( status )) { count++; } status = FsRecCreateAndRegisterDO( DriverObject, NULL, &UdfsMainRecognizerDeviceObject, L"\\UdfsCdRom", L"\\FileSystem\\UdfsCdRomRecognizer", UdfsFileSystem, FILE_DEVICE_CD_ROM_FILE_SYSTEM ); if (NT_SUCCESS( status )) { count++; } status = FsRecCreateAndRegisterDO( DriverObject, UdfsMainRecognizerDeviceObject, NULL, L"\\UdfsDisk", L"\\FileSystem\\UdfsDiskRecognizer", UdfsFileSystem, FILE_DEVICE_DISK_FILE_SYSTEM ); if (NT_SUCCESS( status )) { count++; } status = FsRecCreateAndRegisterDO( DriverObject, NULL, NULL, L"\\Fat", L"\\FileSystem\\FatRecognizer", FatFileSystem, FILE_DEVICE_DISK_FILE_SYSTEM ); if (NT_SUCCESS( status )) { count++; } status = FsRecCreateAndRegisterDO( DriverObject, NULL, NULL, L"\\Ntfs", L"\\FileSystem\\NtfsRecognizer", NtfsFileSystem, FILE_DEVICE_DISK_FILE_SYSTEM ); if (NT_SUCCESS( status )) { count++; } if (count) { return STATUS_SUCCESS; } else { return STATUS_IMAGE_ALREADY_LOADED; } } 因为我用于实验的文件系统识别器代码是在WIN2K的泄露代码中扒拉出来的。在WIN2K时,文件系统识别器只支持NTFS、FAT、CDFS、UDFS这四种文件系统类型。 在DriverEntry中,我们看到四次调用FsRecCreateAndRegisterDO这个例程。这个例程的作用就是创建对应的文件系统设备,以便于在I/O管理器执行IRP_MN_MOUNT_VOLUME操作时, 提前识别卷上的文件系统类型,然后加载对应的文件系统驱动。 我们看下FsRecCreateAndRegisterDO的实现: NTSTATUS FsRecCreateAndRegisterDO ( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT HeadRecognizer OPTIONAL, OUT PDEVICE_OBJECT *NewRecognizer OPTIONAL, IN PWCHAR RecFileSystem, IN PWCHAR FileSystemName, IN FILE_SYSTEM_TYPE FileSystemType, IN DEVICE_TYPE DeviceType ) { PDEVICE_OBJECT deviceObject = NULL; NTSTATUS status = STATUS_SUCCESS; UNICODE_STRING nameString; OBJECT_ATTRIBUTES objectAttributes; HANDLE fsHandle = NULL; IO_STATUS_BLOCK ioStatus; PDEVICE_EXTENSION deviceExtension = NULL; PAGED_CODE(); if (NewRecognizer) { *NewRecognizer = NULL; } //根据给定的文件系统设备名,尝试去打开,如果发现该设备已经存在了,则就没必要创建对应的文件//系统识别器设备了 RtlInitUnicodeString( &nameString, RecFileSystem ); InitializeObjectAttributes( &objectAttributes, &nameString, OBJ_CASE_INSENSITIVE, (HANDLE) NULL, (PSECURITY_DESCRIPTOR) NULL ); status = ZwCreateFile( &fsHandle, SYNCHRONIZE, &objectAttributes, &ioStatus, (PLARGE_INTEGER) NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, 0, (PVOID) NULL, 0 ); if (NT_SUCCESS( status )) { ZwClose( fsHandle ); } else if (status != STATUS_OBJECT_NAME_NOT_FOUND) { status = STATUS_SUCCESS; } if (NT_SUCCESS( status )) { //如果发现已经存在,则返回STATUS_IMAGE_ALREADY_LOADED,表示该文件系统已经载入了 return STATUS_IMAGE_ALREADY_LOADED; } //以下是创建文件系统识别器设备,并且注册该设备为文件系统设备. RtlInitUnicodeString( &nameString, FileSystemName ); status = IoCreateDevice( DriverObject, sizeof( DEVICE_EXTENSION ), &nameString, DeviceType, 0, FALSE, &deviceObject ); if (!NT_SUCCESS( status )) { return status; } // // Initialize the device extension for this device object. // deviceExtension = (PDEVICE_EXTENSION) deviceObject->DeviceExtension; deviceExtension->FileSystemType = FileSystemType; deviceExtension->State = Active; // // Is this a filesystem being jointly recognized by recognizers for // different device types? // if (HeadRecognizer) { // // Link into the list. // deviceExtension->CoRecognizer = ((PDEVICE_EXTENSION)HeadRecognizer->DeviceExtension)->CoRecognizer; ((PDEVICE_EXTENSION)HeadRecognizer->DeviceExtension)->CoRecognizer = deviceObject; } else { // // Initialize the list of codependant recognizer objects. // deviceExtension->CoRecognizer = deviceObject; } #if _PNP_POWER_ deviceObject->DeviceObjectExtension->PowerControlNeeded = FALSE; #endif // // Finally, register this driver as an active, loaded file system and // return to the caller. // if (NewRecognizer) { *NewRecognizer = deviceObject; } IoRegisterFileSystem( deviceObject ); return STATUS_SUCCESS; } 从以上的代码中可以看到,该例程首先尝试去打开要识别的文件系统设备,如果该文件系统的设备已经存在了,则表明该文件系统已经在内核中了,就没必要再去创建对应的文件系统 识别器设备了.如果发现要识别的文件系统设备不存在,则创建一个文件系统识别器设备,然后用IoRegisterFileSystem把这个设备注册成文件系统设备。 在使用IoRegisterFileSystem注册文件系统设备后,I/O管理器就知道系统中存在的文件系统了,在加载卷的过程中,I/O管理器会向这些文件系统设备 发送IRP_MN_MOUNT_VOLUME和IRP_MN_LOAD_FILE_SYSTEM请求。 下面我们来查看下I/O管理器在发送IRP_MN_MOUNT_VOLUME和IRP_MN_LOAD_FILE_SYSTEM请求给文件系统识别器时,文件系统识别器是怎么对卷上的文件系统设备类型做出判断, 并且加载对应的文件系统驱动的。 NTSTATUS FsRecFsControl ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PDEVICE_EXTENSION deviceExtension = NULL; PIO_STACK_LOCATION irpSp = NULL; NTSTATUS status = STATUS_SUCCESS; PAGED_CODE(); deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; irpSp = IoGetCurrentIrpStackLocation( Irp ); if (deviceExtension->State != Active && irpSp->MinorFunction == IRP_MN_MOUNT_VOLUME) { if (deviceExtension->State == Transparent) { status = STATUS_UNRECOGNIZED_VOLUME; } else { status = STATUS_FS_DRIVER_REQUIRED; } Irp->IoStatus.Status = status; IoCompleteRequest( Irp, IO_NO_INCREMENT ); return status; } //在此判断是哪个识别器获取的消息 switch ( deviceExtension->FileSystemType ) { case FatFileSystem: status = FatRecFsControl( DeviceObject, Irp ); break; case NtfsFileSystem: status = NtfsRecFsControl( DeviceObject, Irp ); break; case CdfsFileSystem: status = CdfsRecFsControl( DeviceObject, Irp ); break; case UdfsFileSystem: status = UdfsRecFsControl( DeviceObject, Irp ); break; default: status = STATUS_INVALID_DEVICE_REQUEST; } return status; } 在这个文件系统识别器驱动中,创建了四个文件系统识别器设备,这四个设备,每个识别一种文件系统类型。 在FsRecFsControl例程中,我们看到,会根据文件系统识别器设备的不同,调用不同的处理例程。我们查看下FatFileSystem类型的识别过程。 NTSTATUS FatRecFsControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { NTSTATUS status = STATUS_SUCCESS; PIO_STACK_LOCATION irpSp = NULL; PDEVICE_EXTENSION deviceExtension = NULL; PDEVICE_OBJECT targetDevice = NULL; PPACKED_BOOT_SECTOR buffer = NULL; LARGE_INTEGER byteOffset; UNICODE_STRING driverName; ULONG bytesPerSector = 0; BOOLEAN isDeviceFailure = FALSE; PAGED_CODE(); // // Begin by determining what function that is to be performed. // deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; irpSp = IoGetCurrentIrpStackLocation( Irp ); switch ( irpSp->MinorFunction ) { case IRP_MN_MOUNT_VOLUME: status = STATUS_UNRECOGNIZED_VOLUME; targetDevice = irpSp->Parameters.MountVolume.DeviceObject; // // 首先获取每个扇区的字节长度 // if (FsRecGetDeviceSectorSize( targetDevice, &bytesPerSector )) { byteOffset.QuadPart = 0; buffer = NULL; // if (FsRecReadBlock( targetDevice, &byteOffset, 512, bytesPerSector, &buffer, &isDeviceFailure ) && IsFatVolume( buffer )) { status = STATUS_FS_DRIVER_REQUIRED; } if (buffer != NULL) { ExFreePool( buffer ); } } else { // // 获取磁盘扇区大小失败 // isDeviceFailure = TRUE; } if (isDeviceFailure) { if (targetDevice->Characteristics & FILE_FLOPPY_DISKETTE) { //如果获取扇区尺寸失败,但是设备属性是软盘,则表示识别成功 status = STATUS_FS_DRIVER_REQUIRED; } } break; case IRP_MN_LOAD_FILE_SYSTEM: status = FsRecLoadFileSystem( DeviceObject, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Fastfat" ); break; default: status = STATUS_INVALID_DEVICE_REQUEST; } // // Finally, complete the request and return the same status code to the // caller. // Irp->IoStatus.Status = status; IoCompleteRequest( Irp, IO_NO_INCREMENT ); return status; } 我们看到在FatRecFsControl中,会处理IRP_MN_MOUNT_VOLUME和IRP_MN_LOAD_FILE_SYSTEM两种请求。 首先看IRP_MN_MOUNT_VOLUME请求的处理过程。 case IRP_MN_MOUNT_VOLUME: status = STATUS_UNRECOGNIZED_VOLUME; targetDevice = irpSp->Parameters.MountVolume.DeviceObject; // // 首先获取每个扇区的字节长度 // if (FsRecGetDeviceSectorSize( targetDevice, &bytesPerSector )) { byteOffset.QuadPart = 0; buffer = NULL; //读取卷设备上首个扇区。 if (FsRecReadBlock( targetDevice, &byteOffset, 512, bytesPerSector, &buffer, &isDeviceFailure ) && IsFatVolume( buffer )) { status = STATUS_FS_DRIVER_REQUIRED; } if (buffer != NULL) { ExFreePool( buffer ); } } else { // // 获取磁盘扇区大小失败 // isDeviceFailure = TRUE; } if (isDeviceFailure) { if (targetDevice->Characteristics & FILE_FLOPPY_DISKETTE) { //如果获取扇区尺寸失败,但是设备属性是软盘,则表示识别成功 status = STATUS_FS_DRIVER_REQUIRED; } } 从上面的代码中看到,文件系统识别器识别文件系统类型的过程,以上代码是识别FAT类型文件系统的过程。 首先通过targetDevice = irpSp->Parameters.MountVolume.DeviceObject获取卷设备对象指针。 然后调用FsRecGetDeviceSectorSize函数(该函数的实现,可以自己看代码,是自己构建IRP请求发给卷设备),获取该卷上每个扇区的字节长度。 之后,获取到该卷上每个扇区的字节长度之后,就调用FsRecReadBlock函数(该函数是通过构建IRP去读取卷的首个扇区的数据)读取卷上首个扇区的数据。 然后调用IsFatVolume函数(该函数是通过特征码匹配等,去尝试匹配读取的首个扇区是否符合FAT文件系统的特征)去识别是否是FAT文件系统。 如果识别出这个卷上的文件系统类型是FAT文件系统类型,则会返回STATUS_FS_DRIVER_REQUIRED的NTSTATUS类型的返回值,表示已经识别出这个卷上的文件系统类型。 如果不能识别出是FAT文件系统类型,则返回STATUS_UNRECOGNIZED_VOLUME。 然后我们看下当文件系统识别器返回这两种类型的返回值时,I/O管理器的处理过程。 文件系统识别的过程调用堆栈如下: <图片链接:> http://www.vxjump.net/files/security_research/filesystem_1.jpg 通过调用堆栈,我们看到,卷上文件系统的识别,是在第一次打开该卷上的文件时,发生的,而卷的识别过程是在I/O管理器的例程IopMountVolume中进行的。我们查看下,在这个例程中,根据返回的两个值,看看有些什么操作。 查看从WRK中找到的如下代码: IopMountVolume代码: irp = IoAllocateIrp ((CCHAR) (attachedDevice->StackSize + extraStack), FALSE); if ( !irp ) { status = STATUS_INSUFFICIENT_RESOURCES; break; } irp->Flags = IRP_MOUNT_COMPLETION | IRP_SYNCHRONOUS_PAGING_IO; irp->RequestorMode = KernelMode; irp->UserEvent = &event; irp->UserIosb = &ioStatus; irp->Tail.Overlay.Thread = CurrentThread; irpSp = IoGetNextIrpStackLocation( irp ); irpSp->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL; irpSp->MinorFunction = IRP_MN_MOUNT_VOLUME; irpSp->Flags = AllowRawMount; irpSp->Parameters.MountVolume.Vpb = DeviceObject->Vpb; irpSp->Parameters.MountVolume.DeviceObject = attachedDevice; numRegOps = IopFsRegistrationOps; IopInterlockedIncrementUlong( LockQueueIoDatabaseLock, &savedFsDeviceObject->ReferenceCount ); ExReleaseResourceLite( &IopDatabaseResource ); status = IoCallDriver( fsDeviceObject, irp ); if (status == STATUS_PENDING) { (VOID) KeWaitForSingleObject( &event, Executive, KernelMode, FALSE, (PLARGE_INTEGER) NULL ); } else { ioStatus.Status = status; ioStatus.Information = 0; } (VOID) ExAcquireResourceSharedLite( &IopDatabaseResource, TRUE ); IopInterlockedDecrementUlong( LockQueueIoDatabaseLock, &savedFsDeviceObject->ReferenceCount ); if (NT_SUCCESS( ioStatus.Status )) { status = ioStatus.Status; *Vpb = IopMountInitializeVpb(DeviceObject, attachedDevice, rawMountOnly); } else { status = ioStatus.Status; if (IoIsErrorUserInduced(status) && ioStatus.Information == IOP_ABORT) { break; } if (numRegOps != IopFsRegistrationOps) { dummy.Flink = queueHeader->Flink; entry = &dummy; status = STATUS_UNRECOGNIZED_VOLUME; } if (status == STATUS_FS_DRIVER_REQUIRED) { IopInterlockedIncrementUlong( LockQueueIoDatabaseLock, &savedFsDeviceObject->ReferenceCount ); ExReleaseResourceLite( &IopDatabaseResource ); if (!DeviceLockAlreadyHeld) { KeSetEvent( &DeviceObject->DeviceLock, 0, FALSE ); } KeLeaveCriticalRegionThread(&CurrentThread->Tcb); IopLoadFileSystemDriver( savedFsDeviceObject ); if (!DeviceLockAlreadyHeld) { status = KeWaitForSingleObject( &DeviceObject->DeviceLock, Executive, KeGetPreviousModeByThread(&CurrentThread->Tcb), Alertable, (PLARGE_INTEGER) NULL ); if (status == STATUS_ALERTED || status == STATUS_USER_APC) { ObDereferenceObject( attachedDevice ); return status; } } KeEnterCriticalRegionThread(&CurrentThread->Tcb); (VOID) ExAcquireResourceSharedLite( &IopDatabaseResource, TRUE ); if (DeviceObject->Vpb->Flags & VPB_MOUNTED) { ObDereferenceObject( attachedDevice ); status = STATUS_SUCCESS; break; } dummy.Flink = queueHeader->Flink; entry = &dummy; status = STATUS_UNRECOGNIZED_VOLUME; } } 从FatRecFsControl例程的代码中,我们看到如果是文件系统识别器做的识别,只会返回STATUS_UNRECOGNIZED_VOLUM和STATUS_FS_DRIVER_REQUIRED两个NTSTATUS类型的值。 在上面的代码中看到; 构建IRP,下发给文件系统设备,这里的文件系统设备是我们的文件系统识别器设备。 根据返回值做不同的处理。 if (NT_SUCCESS( ioStatus.Status )) { status = ioStatus.Status; *Vpb = IopMountInitializeVpb(DeviceObject, attachedDevice, rawMountOnly); 如果返回的是STATUS_SUCCESS,则会调用IopMountInitializeVpb。这里我们先不看,以后讲解到真正文件系统的加载时在进一步做这个的讲解。 如果返回的是STATUS_FS_DRIVER_REQUIRED,则我们会看到如下代码: IopInterlockedIncrementUlong( LockQueueIoDatabaseLock, &savedFsDeviceObject->ReferenceCount ); ExReleaseResourceLite( &IopDatabaseResource ); if (!DeviceLockAlreadyHeld) { KeSetEvent( &DeviceObject->DeviceLock, 0, FALSE ); } KeLeaveCriticalRegionThread(&CurrentThread->Tcb); IopLoadFileSystemDriver( savedFsDeviceObject ); if (!DeviceLockAlreadyHeld) { status = KeWaitForSingleObject( &DeviceObject->DeviceLock, Executive, KeGetPreviousModeByThread(&CurrentThread->Tcb) } 我们看到会调用IopLoadFileSystemDriver( savedFsDeviceObject );函数,而savedFsDeviceObject正是我们的文件系统设备器驱动,因为在这里是识别FAT文件系统,所以, savedFsDeviceObject在这里就是文件系统识别器创建的用于识别FAT文件系统类型的文件系统识别器设备。 IopLoadFileSystemDriver的功能就是自己构建IRP,向savedFsDeviceObject发送 irpSp->MajorFunction = IRP_MJ_FILE_SYSTEM_CONTROL; irpSp->MinorFunction = IRP_MN_LOAD_FILE_SYSTEM; 如上的请求。 然后请求又回到了FatRecFsControl中,这次我们要看文件系统识别器对IRP_MN_LOAD_FILE_SYSTEM请求的处理了,这个处理很简单,就一个函数调用: 如下: status = FsRecLoadFileSystem( DeviceObject, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Fastfat" ); FsRecLoadFileSystem调用ZwLoadDriver将FAT的文件系统驱动加载到内核中。 至此,文件系统识别器要做的工作就到此为止了。 做个总结,卷上的文件系统识别工作,并不是枚举到卷设备后,马上就做的 它会延迟到针对卷的第一次访问才进行。从上面的调用堆栈中可以看到。 致谢 感觉潘爱民老师的《Windows内核原理与实现》以及提供WIN2K泄露源代码的朋友们。更要感谢MARK的《深入理解WINDOWS操作系统》 本文相关实例代码下载见 - 代码&&工具中 http://www.vxjump.net/files/a_page/4/code1.htm