阅读:6192回复:17
使用shadow device 解决 ifs filter重入.
如果有人会实现下面文章的思想,请给我一点指导,同时希望能提供一些源码,谢谢!
我再次表示感谢! While processing an IRP_MJ_CREATE a filter may need to open the file with different attributes/rights, etc. This is often done by using a second call to ZwCreatefile. This then will generate a call back into the FSD filter. Thus, a common filter issue is being able to detect this reentrancy. There are several ways of dealing with reentrancy during an IRP_MJ_CREATE operation, and the appropriate solution for your particular driver will depend upon the circumstances. In addition, there are a number of techniques that might work for a single file system filter driver, but that fail when used in a multi-filter environment. For Windows XP and newer versions of Windows, the best mechanism for opening a file within the filter is to use IoCreateFileSpecifyDeviceObjectHint. A filter driver can call this function and specify a given device object. The IRP_MJ_CREATE that is built will be passed to the specified device object. This technique avoids reentrancy issues and is the best mechanism available for a filter to open a file. For versions of Windows prior to Windows XP, this mechanism is not available. The best mechanism in this environment is to implement your own functional equivalent of IoCreateFileSpecifyDeviceObjectHint. This can be done by creating a second device object for each device you are filtering. For example, suppose you decide to filter some given file system device object, FSDVolumeDeviceObject. You then create a device object MyFilterDeviceObject and attach it using IoAttachDeviceToDeviceStack (of course, in Windows XP you would use IoAttachDeviceToDeviceStackSafe instead). In addition, you create a second device object MyFilterShadowDeviceObject. This device object must be assigned a name (\"\\Device\\MyFilterDevice27\", for example). The name can be anything, but it must obviously be unique. In your device extension for your two device objects, you need to track this name, and you need to maintain pointers to the respective device objects (that is, the device extension for MyFilterShadowDeviceObject should point to MyFilterDeviceObject and the device object extension for MyFilterDeviceObject should point to MyFilterShadowDeviceObject). Don\'t forget to set the StackSize field of the device object correctly!) Now, an IRP_MJ_CREATE request arrives in your filter, specifying MyFilterDeviceObject. To open the file without experiencing reentrancy problems, you call IoCreateFile (or ZwCreateFile). Since you must pass the name of the file being opened, you construct that by using both the name you gave MyFilterShadowDeviceObject and the name that is in the FileObject of the I/O stack Location (IoGetCurrentIrpStackLocation(Irp)->FileObject). Since you are passing a name in that points to your second device object, the I/O Manager will build the IRP_MJ_CREATE and pass the resulting I/O request packet to your driver, but specifying MyFilterShadowDeviceObject. In your IRP_MJ_CREATE dispatch handler you must detect that this is a \"shadow\" device object, rather than a typical filter device object. In this case, you should send the IRP_MJ_CREATE operation down to the device being filtered by MyFilterDeviceObject. Indeed, since you should not need to do any further processing, you can use IoSkipCurrentIrpStackLocation (rather than IoCopyCurrentIrpStackLocationToNext). The original filter (where the IoCreateFile call was made) will receive back a file handle that can then be used for subsequent operations (using the Zw API routines). Typically, filter drivers that attempt to use IoCreateFile or ZwCreateFile with the same file/device name as the original request experience reentrancy into their driver. A number of techniques for dealing with this have been tried in the past, but they exhibit various problems when combined with other filters. These include: ? Appending a \"special string\" to the end of the file name. This will not work when two filters using this technique are loaded onto the same system (since each one appends its \"special string\" onto the previous filter\'s \"special string\"). ? Track thread identifiers to detect reentrancy. This technique fails when combined with a filter that utilizes a separate service for opening the file; filters sometimes must switch to a different thread context in order to eliminate stack overflow conditions. ? Building create IRPs within a filter. This technique does work properly, but is completely unsupported and quite difficult to implement correctly. Because of this, it is a fragile solution that should not be used given the availability of alternative mechanisms. ? Re-using the file object from the current IRP_MJ_CREATE. In this sequence, the filter allows the create operation to complete and then uses the file object subsequently. When done, the filter then sends a cleanup and close operation to the underlying file system. It then sends the original IRP_MJ_CREATE operation to the underlying FSD. There are several potential issues with this approach. First, in this technique the filter does not have a file handle for the file object and thus cannot use the Zw API calls. Second, the file object must be restored to its original state - otherwise, there are fields within it that are not set up properly. Third, because the file object has not yet been \"properly referenced\" the filter may find that the OS deletes the object because its reference count drops to zero during its use. Used carefully, this technique has been successful in the past. Regardless of the technique used, the filter driver must be cognizant of the issues involving oplocks (and the FILE_COMPLETE_IF_OPLOCKED option). If an oplock break occurs during an IRP_MJ_CREATE, whether from an original application, or from a filter driver attempting to open the file, the filter must be able to handle it gracefully. Finally, the filter must be aware of the fact that even though it is calling from kernel mode, the underlying FSD will continue to enforce sharing access against the file. [编辑 - 3/11/05 by znsoft] |
|
|
沙发#
发布于:2002-02-01 20:05
根本不需要这么复杂. 用ZwCreateFile会导致PREVIOUS MODE成为KERNEL MODE, 从而破坏NT本身的安全检查机制.
此人无非就是要将IRP发送到特定的FSD栈里的某一层. 自己历遍FSD的DEVICE OBJECT栈就可以完成. 而XP的对应函数其实也是通过历遍FSD CALL STACK完成锁定目标FSD的. 我的提示就到这里. 如果需要进步的讲解就不是免费的了. |
|
|
板凳#
发布于:2002-02-23 02:55
版主,你好像误解了那个人提出的问题.不光是要将IRP发送到特定的FSD栈里的某一层.而是要在我自己的Filter中Create 文件,而又不重入我的Filter,“老外”的做法是起一个“标志性”的名字,然后在Filter中区分那些文件是我的Filiter要打开的,那些是一般程序要打开的,但是么,这实在不明智。有一种好方法,就是自己建立IRP请求,然后下传。其中用到了一些构建自己的IRP,以及FileObject的方法。
下面是创建避免重入的关键代码,用该代码可以直接构建并下传打开文件的IRP。不过,这只是测试版的代码,如果哪位大侠发现这里的Bug,希望与我联系(li-gen@x263.net),谢谢。另外感谢Smith, Joel 的指导。 NTSTATUS FsSimpleOpen(PUNICODE_STRING pstrName, ACCESS_MASK DesiredAccess, PDEVICE_OBJECT pVolume, PDEVICE_OBJECT pRealDevice, PFILE_OBJECT pParent, PFILE_OBJECT *ppFile) { PIRP pIrp = NULL; PIO_STACK_LOCATION pStack; KEVENT event; IO_STATUS_BLOCK stat; NTSTATUS ntRet; PFILE_OBJECT pFile = NULL; OBJECT_ATTRIBUTES oa; ACCESS_STATE AccessState; HANDLE hFile = NULL; IO_SECURITY_CONTEXT ctx; ULONG *pAuxData = ExAllocatePool(NonPagedPool, 1024); if (pAuxData == NULL) { ntRet = STATUS_INSUFFICIENT_RESOURCES; CLEANUP(); } ASSERT(pVolume != NULL); ASSERT(pRealDevice != NULL); ASSERT(ppFile != NULL); ASSERT(pstrName != NULL); memset(pAuxData, 0, 1024); //initialize an event and an IRP to send to the fsd KeInitializeEvent(&event, SynchronizationEvent, FALSE); pIrp = IoAllocateIrp(pVolume->StackSize, FALSE); IF_OUTOFMEMORY_CLEANUP(pIrp, &ntRet); InitializeObjectAttributes(&oa, NULL, OBJ_CASE_INSENSITIVE, NULL, NULL); //create a file object for the irp ntRet = ObCreateObject(KernelMode, *IoFileObjectType, &oa, KernelMode, 0, sizeof(FILE_OBJECT), 0, 0, &pFile); IF_FAIL_CLEANUP(ntRet); //initialize the file object RtlZeroMemory(pFile, sizeof(FILE_OBJECT)); pFile->Type = IO_TYPE_FILE; pFile->Size = sizeof(FILE_OBJECT); pFile->DeviceObject = pRealDevice; pFile->Flags = FO_SYNCHRONOUS_IO; pFile->RelatedFileObject = pParent; //initialize embedded synch objects KeInitializeEvent(&pFile->Lock, SynchronizationEvent, FALSE); KeInitializeEvent(&pFile->Event, NotificationEvent, FALSE); //copy the file name into the buffer pFile->FileName.Buffer = ExAllocatePool(NonPagedPool, pstrName->MaximumLength); if (pFile->FileName.Buffer == NULL) { ntRet = STATUS_INSUFFICIENT_RESOURCES; CLEANUP(); } pFile->FileName.MaximumLength = pstrName->MaximumLength; pFile->FileName.Length = pstrName->Length; RtlCopyMemory(pFile->FileName.Buffer, pstrName->Buffer, pstrName->Length); //setup the irp pIrp->UserEvent = &event; pIrp->UserIosb = &stat; pIrp->Tail.Overlay.Thread = KeGetCurrentThread(); pIrp->Tail.Overlay.OriginalFileObject = pFile; pIrp->RequestorMode = KernelMode; pIrp->Flags |= (IRP_CREATE_OPERATION | IRP_SYNCHRONOUS_API); pIrp->MdlAddress = NULL; pIrp->PendingReturned = FALSE; pIrp->Cancel = FALSE; pIrp->CancelRoutine = NULL; pIrp->Tail.Overlay.AuxiliaryBuffer = NULL; //Set up the I/O stack location. pStack = IoGetNextIrpStackLocation(pIrp); pStack->MajorFunction = IRP_MJ_CREATE; pStack->DeviceObject = pVolume; pStack->FileObject = pFile; //create an access state for the sd ntRet = SeCreateAccessState(&AccessState, pAuxData, DesiredAccess, IoGetFileObjectGenericMapping()); IF_FAIL_CLEANUP(ntRet); //fill out the create\'s security context ctx.AccessState = &AccessState; ctx.DesiredAccess = DesiredAccess; ctx.SecurityQos = NULL; ctx.FullCreateOptions = 0; //fill out the the create parameter pStack->Parameters.Create.SecurityContext = &ctx; pStack->Parameters.Create.Options = FILE_OPEN << 24; pStack->Parameters.Create.FileAttributes = FILE_ATTRIBUTE_NORMAL; pStack->Parameters.Create.ShareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE; pStack->Parameters.Create.EaLength = 0; //set the completion routine (that will free the irp) IoSetCompletionRoutine(pIrp, FsDefaultComplete, 0, TRUE, TRUE, TRUE); //Send it to the FSD ntRet = IoCallDriver(pVolume, pIrp); pIrp = NULL; if (ntRet == STATUS_PENDING) { //wait for the io to complete KeWaitForSingleObject(&event, Executive, KernelMode, TRUE, 0); ntRet = stat.Status; } CLEANUP: SAFE_FREE_POOL(pAuxData); if (!NT_SUCCESS(ntRet)) { if (pIrp != NULL) { IoFreeIrp(pIrp); } if (pFile) { SAFE_FREE_POOL(pFile->FileName.Buffer); pFile->DeviceObject = NULL; ObDereferenceObject(pFile); } } else { FsReferenceDeviceAndVpb(pFile); //set the out param *ppFile = pFile; } return ntRet; } VOID FsReferenceDeviceAndVpb(PFILE_OBJECT pFile) { NTSTATUS ntRet; ASSERT(pFile->Vpb != NULL ? pFile->DeviceObject->Vpb != NULL : pFile->DeviceObject->Vpb == NULL); //Increment RealDevice\'s reference count. InterlockedIncrement(&pFile->DeviceObject->ReferenceCount); //Increment Vpb\'s reference count, if one exists. if (pFile->Vpb) { ASSERT((pFile->Vpb->Flags & VPB_MOUNTED) && !(pFile->Vpb->Flags & VPB_LOCKED)); InterlockedIncrement(&pFile->Vpb->ReferenceCount); } } 我可不想要钱。我还是学生嘛,但是么,我觉得有技术就因该提供。 希望 版主 多提意见。但是如果有哪位未经我同意,把本程序投稿到相管杂志或是本站的资料文章区,来骗取个人利益,我一定饶不了他。 |
|
地板#
发布于:2004-11-18 17:28
lu0为什么不自己看看就回答啊,人家明明说的是如何避免zwCreatefile引起的重入??
|
|
地下室#
发布于:2004-11-19 09:44
我的提示就到这里. 如果需要进步的讲解就不是免费的了. 不要说的这么直接嘛,OSR上面的几个大牛Tony等等也没这么直接嘛。 |
|
|
5楼#
发布于:2004-11-19 09:49
lu0为什么不自己看看就回答啊,人家明明说的是如何避免zwCreatefile引起的重入?? 好像是文件驱动中如何避免自己的IRP_MJ_CREATE请求被重入,这个问题在filemon中好像(好长时间没搞了,不确定)有处理的方法。如果是搞文件驱动的话,建议看一下ifs中的osr docs。 |
|
|
6楼#
发布于:2004-11-19 15:55
Shadow Device or IoCreateFileSpecifyDeviceObjectHint
|
|
7楼#
发布于:2004-11-19 16:08
sorry,看错了,原来搂主就是在就是在讲解shadow device
其实shadow device很简单,就是创建一个普通的device(称之为shadow device),并且是一个有名字的device(比如\\device\\myshadowdevice), 然后我们在过滤设备的dispatchcreate例程中,如果需要避免冲入问题的话,可以把目标文件改名, 比如文件名"\\device\\harddiskvolume1\\1.txt",如果需要用ZwCreateFile打开该文件,我们可以用文件名"\\device\\myshadowdevice\\1.txt"打开,这样就不会再次进入我们的过滤驱动的dispatchcreate,而会进入我们的shadow device的dispatchcreate,然后在shadow device的dispatchcreate中将"\\device\\myshadowdevice\\1.txt"改回"\\device\\harddiskvolume1\\1.txt",再将irp传给过滤设备的下层驱动处理,从而避免重入。 |
|
8楼#
发布于:2004-11-19 16:29
BOOLEAN
HookDrive( IN PDRIVER_OBJECT DriverObject, IN WCHAR DriveLetter ) { IO_STATUS_BLOCK IoStatus; HANDLE FileHandle; OBJECT_ATTRIBUTES ObjectAttributes; PDEVICE_OBJECT FileSysDevice; PDEVICE_OBJECT DeviceObject; PDEVICE_OBJECT ShadowDeviceObject; UNICODE_STRING ShadowDeviceName; WCHAR ShadowDeviceNameBuffer[] = L"\\Device\\FDFilterShadowA"; UNICODE_STRING FileName; WCHAR FileNameBuffer[] = L"\\DosDevices\\A:\\"; NTSTATUS Status; PFILE_OBJECT FileObject; PDEVICE_EXTENSION Pdx; PDEVICE_EXTENSION ShadowPdx; // // Replace the drive letter // FileNameBuffer[12] = DriveLetter; ShadowDeviceNameBuffer[22] = DriveLetter; // // We have to figure out what device to hook - first open the volume's // root directory // RtlInitUnicodeString(&FileName, FileNameBuffer); InitializeObjectAttributes(&ObjectAttributes, &FileName, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = ZwCreateFile(&FileHandle, SYNCHRONIZE | FILE_ANY_ACCESS, &ObjectAttributes, &IoStatus, NULL, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT | FILE_DIRECTORY_FILE, NULL, 0); if (!NT_SUCCESS(Status)) { KdPrint(("fdfilter.sys: Could not open drive: %x\n", Status)); return FALSE; } KdPrint(("fdfilter.sys: opened the root directory!!! handle: %x\n", FileHandle)); // // Got the file handle, so now look-up the file-object it refers to // Status = ObReferenceObjectByHandle(FileHandle, FILE_READ_DATA, NULL, KernelMode, &FileObject, NULL); if (!NT_SUCCESS(Status)) { KdPrint(("fdfilter.sys: Could not get fileobject from handle\n")); ZwClose(FileHandle); return FALSE; } // // Next, find out what device is associated with the file object by getting its related // device object // FileSysDevice = IoGetRelatedDeviceObject(FileObject); if (!FileSysDevice) { KdPrint(("fdfilter.sys: Could not get related device object\n")); ObDereferenceObject(FileObject); ZwClose(FileHandle); return FALSE; } // // Create a shadow device object // RtlInitUnicodeString(&ShadowDeviceName, ShadowDeviceNameBuffer); Status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), &ShadowDeviceName, FileSysDevice->DeviceType, 0, FALSE, &ShadowDeviceObject); if (!NT_SUCCESS(Status)) { KdPrint(("fdfilter.sys: failed to create associated device\n")); ObDereferenceObject(FileObject); ZwClose(FileHandle); return FALSE; } ShadowDeviceObject->StackSize = FileSysDevice->StackSize; // // The file system's device hasn't been hooked already, so make a hooking device // object that will be attached to it. // Status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), NULL, FileSysDevice->DeviceType, 0, FALSE, &DeviceObject); if (!NT_SUCCESS(Status)) { KdPrint(("fdfilter.sys: failed to create associated device\n")); ObDereferenceObject(FileObject); ZwClose(FileHandle); IoDeleteDevice(ShadowDeviceObject); return FALSE; } // // Setup the device extensions. The drive letter and file system object are stored // in the extension. // ShadowPdx = ShadowDeviceObject->DeviceExtension; ShadowPdx->Type = SHADOW; ShadowPdx->FsDeviceObject = NULL; ShadowPdx->AttachToDeviceObject = NULL; ShadowPdx->BuddyDeviceObject = DeviceObject; Pdx = DeviceObject->DeviceExtension; Pdx->Type = STANDARD; Pdx->FsDeviceObject = FileSysDevice; Pdx->AttachToDeviceObject = FileSysDevice; Pdx->BuddyDeviceObject = ShadowDeviceObject; // // Finally, attach to the device. The second we're successfully attached, we may // start receiving IRPs targetted at the device we've hooked. // Pdx->AttachToDeviceObject = IoAttachDeviceToDeviceStack(DeviceObject, FileSysDevice); if (!Pdx->AttachToDeviceObject) { // // Couldn' attach for some reason // KdPrint(("fdfilter.sys: Connect with Filesystem failed\n")); // // Derefence the object and get out // ObDereferenceObject(FileObject); ZwClose(FileHandle); IoDeleteDevice(ShadowDeviceObject); IoDeleteDevice(DeviceObject); return FALSE; } // // Make a new drive group for the device,l if it does not have one // already // KdPrint(("fdfilter.sys: Successfully connected to Filesystem device\n")); DeviceObject->Flags |= FileSysDevice->Flags & (DO_DIRECT_IO | DO_BUFFERED_IO); DeviceObject->Flags |= DO_POWER_PAGABLE; DeviceObject->DeviceType = FileSysDevice->DeviceType; DeviceObject->Characteristics = FileSysDevice->Characteristics; // // Clear the device's init flag as per NT DDK KB article on creating device // objects from a dispatch routine // ShadowDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; // // Close the file and update the hooked drive list by entering a // pointer to the hook device object in it. // ObDereferenceObject(FileObject); ZwClose(FileHandle); return TRUE; } NTSTATUS DispatchCreate( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension; PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); if (pdx->Type == SHADOW) { // ShadowDevice // 调用过滤驱动的下一层驱动 PDEVICE_EXTENSION BuddyPdx = (PDEVICE_EXTENSION) pdx->BuddyDeviceObject->DeviceExtension; IoSkipCurrentIrpStatkLocation(Irp); return IoCallDriver(BuddyPdx->AttachToDeviceObject, Irp); } else if (pdx->Type == STANDARD) { WCHAR wszFile[MAX_PATH] = {0}; // FilterDevice // 我们需要再次打开该文件,为了避免重入... GetFileNameFromFileObject(IrpSp->FileObject, wszFile); // 假设 wszFile 里面原来内容为 L"1.txt" // 修改为 L"\\Device\\FDFilterShadowA\\1.txt" // 然后... OBJECT_ATTRIBUTES oa; InitializeObjectAttributes(&oa, wszFile, ...); ZwCreateFile(..., &oa, ...); // 不会再有重入的问题了,:))) // 因为会进入ShadowDevice's DispatchCreate // ... } } [编辑 - 11/19/04 by tooflat] |
|
9楼#
发布于:2005-01-04 11:15
讲得非常具体,谢谢了。
|
|
10楼#
发布于:2005-01-04 11:21
是不是应该给每个volume都创建一个单独的shadowdevice???
|
|
11楼#
发布于:2005-01-04 11:39
是不是应该给每个volume都创建一个单独的shadowdevice??? 是的 |
|
12楼#
发布于:2005-01-04 12:20
真不错!
|
|
13楼#
发布于:2005-01-06 00:36
good idel
|
|
14楼#
发布于:2005-03-11 16:53
osr上关于解决重入的方法讨论
大家详细看看。 |
|
|
15楼#
发布于:2009-04-25 17:32
请问ShadowDevice的 DispatchCreate 在什么地方??
|
|
16楼#
发布于:2010-01-21 17:16
在哪里调用HookDrive呢
|
|
17楼#
发布于:2010-01-23 14:19
HookDrive函数的的流程如下:
1.打开根目录,获取句柄; 2.通过句柄,获取文件对象; 3.获取文件对象相关的设备对象; 4.创建shadow设备对象; 5.创建过滤设备对象,并关联shadow设备对象和过滤设备对象; 6.把过滤设备对象绑定到目标设备对象上; 7.后续的清理工作; DispatchCreate函数似乎是IRP_MJ_CREATE的例程。 不知道这两个函数该如何调用... 我的理解如下: DispatchCreate函数作为IRP_MJ_CREATE的例程; 在绑定卷设备时调用HookDrive函数; 然而按照我的理解调试后发现,HookDrive函数进入第1步时(即打开根目录时),就会进入到IRP_MJ_CREATE的例程中, 这样根本没有避开重入问题。 不知道用shadow方式处理重入该如何操作。 望处理过重入问题的大侠多多赐教! |
|